コピペコードで快適生活

明日使えるソースを自分のために

class_evalを使って、classに自由な名前でメソッドを追加

concernに書くとこんな感じか。

#
# include ::Logica::DateRangeSearchable
# date_range_name_is :validity, :valid
#
# klass.valid
#   -> validity_start_at, validity_end_atが期間内のみ取り出す
# instance.valid?
#   -> validity_start_at, validity_end_atが期間内か
#
module Logica
  module DateRangeSearchable
    extend ActiveSupport::Concern

    class_methods do
      def date_range_name_is(name, method_name = nil)
        method_name = name if method_name.blank?

        validation_name = "#{name}_start_lt_end_validation".to_sym
        validate validation_name

        class_eval <<-EOS
          def #{method_name}?(now = Time.zone.now)
            (self.#{name}_start_at.nil? || self.#{name}_start_at <= now) &&
            (self.#{name}_end_at.nil? || self.#{name}_end_at >= now )
          end

          def #{validation_name}
            if self.#{name}_start_at.nil? || self.#{name}_end_at.nil?
              return true
            end
            if self.#{name}_end_at <= self.#{name}_start_at
              message = I18n.t('errors.messages.less_than', count: self.#{name}_end_at)
              self.errors.add("#{name}_start_at".to_sym, message)
            end
          end

          def self.#{method_name}_conds(now = Time.zone.now)
            a = arel_table[:#{name}_start_at].lteq(now).or(arel_table[:#{name}_start_at].eq(nil))
            b = arel_table[:#{name}_end_at].gteq(now).or(arel_table[:#{name}_end_at].eq(nil))
            a.and(b)
          end
        EOS

        class_eval do
          scope method_name.to_sym, -> (now = Time.zone.now) {
            where(self.try("#{method_name}_conds", now))
          }
        end

      end
    end
  end
end