In the system there are employees with login information in the User model and other information about them in the Profile model.
We want to be able to display a list of employees who have an anniversary this month (the month of hire is the same as the current one) and it is their 1st, 2nd, or a multiple of 5 years on the job.
We want to use it like a scope, but since the logic is complex, we are making a Class method. Trying to split the logic into small chunks is becoming messy. I am sure that the code can be simplified.
The biggest issue is that instead of getting a list of only the employees with an anniversary as a scope would do, I am getting a list of all the employees as nil or their user info if it is their anniversary month.
An example:
irb_001 >> Profile.anniversary?
[
[0] nil,
[1] nil,
[2] #<User:0x007fd17c883740> {
:id => 3,
:first_name => "Sally",
:last_name => "Brown",
:email => "[email protected]",
:password_digest => "[redacted]",
:created_at => Tue, 21 Feb 2018 11:12:42 EST -05:00,
:updated_at => Sat, 25 Feb 2018 12:28:45 EST -05:00,
},
[3] nil,
[4] nil,
[5] #<User:0x007fd17a2eaf38> {
:id => 6,
:first_name => "Lucy",
:last_name => "Van Pelt",
:email => "[email protected]",
:password_digest => "[redacted]",
:created_at => Tue, 20 Nov 2018 21:01:04 EST -05:00,
:updated_at => Tue, 20 Nov 2018 21:02:36 EST -05:00,
},
[6] nil
]
irb_002 >>
What is the best way to achieve the desired result and clean up this code?
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile, allow_destroy: true
after_create :create_matching_profile
delegate :active, to: :profile, prefix: true
private
def create_matching_profile
profile = build_profile
profile.save
end
end
class Profile < ActiveRecord::Base
belongs_to :user
def self.years_employed(profile)
# calculate how many years employed
@profile = profile
if @profile.employed_since?
(( Date.today.to_time - @profile.employed_since.to_time )/1.year.second).to_i
else
0
end
end
def self.anniversary_month(profile)
# get the month of hire
@profile = profile
@profile.employed_since? ? @profile.employed_since.month : 0
end
def self.anniversary?
# first, second, or multiple of five year anniversary month
@profiles = Profile.where("employed_since is not null")
@profiles.map do |profile|
if ( Date.today.month == anniversary_month(profile) )
@years_working = years_employed(profile)
if ( @years_working> 0 &&
( @years_working == 1 || @years_working == 2 || ( @years_working % 5 == 0 )))
result = true
else
result = false
end
else
result = false
end
profile.user if result
end
end
end
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# first_name :string
# last_name :string
# email :string
# password_digest :string
# created_at :datetime not null
# updated_at :datetime not null
#
# Table name: profiles
#
# id :integer not null, primary key
# user_id :integer
# active :boolean
# employed_since :date
# ...other attributes...
# created_at :datetime not null
# updated_at :datetime not null
#
employed since data from Profiles
[
[0] Sun, 01 Dec 1991,
[1] Thu, 01 May 2018,
[2] Wed, 01 Nov 2017,
[3] Wed, 01 Feb 2017,
[4] Thu, 01 Aug 2018,
[5] Fri, 01 Nov 2013,
[6] Fri, 01 Nov 1991
]
This can be done in a much simpler and more efficient way by using the date functions in the database and doing the comparison there.
This example is written for Postgres and you might need to adapt it to your RDBMS.