首页 > 解决方案 > 如何将活动记录类特定属性映射到rails中相应的通用关注方法

问题描述

我在我的 Rails 应用程序中担心目标是将标签转换为日期范围。基本上我有一个字符串 date_label ,它采用像“明天”这样的值来帮助计算两个日期字段,比如 date_end 和 date_end ,它们是实际日期。概念 date_label、date_start、date_end 是“常见关注点概念”,但它们需要映射到包含关注点的活动记录类中具有特定名称的相应活动记录字段。

这是到目前为止的关注代码:

module DateSchedulable
  extend ActiveSupport::Concern
  included do
    before_save :compute_date_range
  end

  LABEL_TO_DATE_RANGE =  {
    'asap': { start: Date.today, end: Date.today },
    'same-day': { start: Date.today, end: Date.today },
    'next-day': { start: Date.tomorrow, end: Date.tomorrow },
    'two-day': { start: Date.today, end: Date.today + 2.days },
    'seven-day': { start: Date.today, end: Date.today + 7.days }
  }

  def compute_date_range
    if self.date_label_changed?
      date_range = get_date_label_range(self.date_label)
      self.date_start = date_range[:start]
      self.date_end = date_range[:end]
    end
  end

  def get_date_label_range(date_label)
    if LABEL_TO_DATE_RANGE[date_label.to_sym]
      LABEL_TO_DATE_RANGE[date_label.to_sym]
    elsif Date.is_parseable?(date_label)
      date = Date.parse(date_label)
      { start: date, end: date }
    else
      { start: nil, end: nil }
    end
  end
end

我特别关心如何处理这个date_label_changed?问题compute_date_range

例如,我需要将此问题包含在HelpList具有以下活动记录属性的类中"default_requested_date_label", "default_requested_date_start","default_requested_date_end"。我需要确保我的类特定属性名称和我的一般关注概念之间的映射。理想情况下,我可能想写一些类似的东西:

class HelpList
  include DateSchedulable
  date_label :default_requested_date_label
  date_start :default_requested_date_start
  date_end   :default_requested_date_end
end

但我真的很愿意接受建议,并想知道是否有一种优雅的方式来处理这个问题。

标签: ruby-on-railsrubymoduleactivesupport-concern

解决方案


你真的应该重新开始并完全重新考虑你的方法,因为 Ruby 实际上有为此目的而构建的范围:

DATE_RANGES =  {
  asap: Date.today..Date.today,
  same_day: Date.today.begininng_of_day..Date.today.begininng_of_day,
  next_day: Date.tomorrow.begininng_of_day..Date.tomorrow.end_of_day,
  two_day: Date.today.advance(days: 2).beginning_of_day..Date.today.advance(days: 2).beginning_of_day,
  seven_day: Date.today..Date.today.advance(days: 7)
}

当您将范围传递给时,ActiveRecord 将创建 BETWEEN 查询.where

tricenarians = User.where(birth_day: 30.years.ago...40.years.ago)
# SELECT users.* FROM users WHERE users.birth_day BETWEEN ? AND ?

如果您使用的是 Postgres,它实际上具有本地时间戳和日期范围类型,因此您可以将范围存储在单个列中。Range#begin否则,您可以使用和获得界限Range#end

不要陷入使用用户标签作为哈希键的诱惑。使用 I18n 模块或String#dasherize. 将两者强耦合是一个错误,因为仅更新呈现给用户的标签会破坏代码中的任何引用。

我需要确保我的类特定属性名称和我的一般关注概念之间的映射。

您可以通过创建类方法并将列的名称保存在类实例变量中或通过动态创建方法来使您的模块可配置。

module DateSchedulable
  # ...
  class_methods do 
    def date_label(attribute_name)
      class_eval do
        @_date_label_attribute = attribute_name
      end
      # or
      class_eval do
        define_singleton_method :date_label_attribute do
          attribute_name
        end
      end
    end
    # ...
  end
end

这种元编程是 Rails 构建回调、验证和关联等内容的方式。请务必阅读 mixin 模式,因为您需要对 Ruby 风格的 OOP 有一个很好的理解。


推荐阅读