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