Метод класса Rails, используемый как область со сложной логикой - PullRequest
0 голосов
/ 25 ноября 2018

В системе есть сотрудники с данными для входа в модель пользователя и другой информацией о них в модели профиля.

Мы хотим иметь возможность отображать список сотрудников, у которых в этом месяце юбилей (месяц найма такой же, как и текущий), и это их 1-й, 2-й или кратный 5 годам на работе.

Мы хотим использовать его как прицел, но так как логикасложный, мы делаем метод класса.Попытка разбить логику на маленькие кусочки становится грязной.Я уверен, что код может быть упрощен.

Самая большая проблема заключается в том, что вместо того, чтобы получать список только сотрудников с юбилеем в качестве области действия, я получаю список всех сотрудников как ноль или информацию о пользователях, если это их юбилейный месяц.

Пример:

irb_001 >> Profile.anniversary?
[
    [0] nil,
    [1] nil,
    [2] #<User:0x007fd17c883740> {
                            :id => 3,
                    :first_name => "Sally",
                     :last_name => "Brown",
                         :email => "sally@peanuts.com",
               :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 => "lucy@peanuts.com",
               :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 >>

Каков наилучший способ достичь желаемого результата и очистить этот код?

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
#

используется с момента получения данных из профилей

[
[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
]

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

При использовании SQLITE предложение where выглядит следующим образом:

where "strftime('%m',employed_since) = strftime('%m', date('now'))
       AND employed_since < date('now','-1 year','+1 day')
       AND ( (strftime('%Y','now') - strftime('%Y', employed_since)) BETWEEN 1 AND 2
          OR (strftime('%Y','now') - strftime('%Y', employed_since)) % 5 = 0 )" 

На самом деле это работает как область видимости, нет необходимости в методе класса, как я изначально думал.

0 голосов
/ 25 ноября 2018

Это можно сделать намного проще и эффективнее, используя функции даты в базе данных и проводя там сравнение.

class User < ApplicationRecord
  has_one :profile

  def self.anniversary
    self.joins(:profile)
        .where("EXTRACT(MONTH FROM profiles.employed_since) = EXTRACT(MONTH FROM now())")
        .where("profiles.employed_since < ?", 1.year.ago)
        .where(%q{
          EXTRACT(year FROM now()) - EXTRACT(year FROM profiles.employed_since BETWEEN 1 AND 2
          OR
          CAST(EXTRACT(year FROM now()) - EXTRACT(year FROM profiles.employed_since) AS INTEGER) % 5 = 0
        })
  end
end

Этот пример написан для Postgres, и вам может потребоваться адаптацияэто к вашей РСУБД.

...