拦截Rails中的getter和setter调用以进行数据操作

时间:2015-10-14 16:59:29

标签: ruby-on-rails ruby

我正在使用存储温度值的Rails / Angular应用程序。温度始终以摄氏度存储在数据库中。可以根据用户的偏好以摄氏度或华氏度向用户显示这些温度值。

如果温度达到某个值,应用程序也可以提醒用户。这很重要,因为用户可能会在华氏度中输入警报值,但在将其存储到数据库之前需要将其转换为摄氏度。这些alert值也与温度读数本身位于不同的表中,因此该解决方案非常适用于各种模型。

所以,基本上,我需要找到在读取并保存到数据库时操作值的最佳位置和策略。我们已经尝试了几个不同的东西,但我希望将它们重构为一个很好的可维护解决方案,尽可能少的代码路径。

对于温度读数的显示时间变化,我们在ReadingsController中使用了控制器问题。如果用户具有该偏好,则映射到ReadingPresenter将转换为华氏度。

class ReadingPresenter
  include ApplicationHelper

  def initialize(sensor_reading, sample_type)
    @model = sensor_reading
    @sample_type = sample_type
  end

  def value
    if @sample_type.temperature?
      TemperatureService.for_current_user @model.value
    else
      @model.value
    end
  end
end

当我们需要显示已经存储的华氏读数时,这种方法很好,但由于它是演示者,当我们需要将用户输入的华氏度alert值更改为摄氏度时,它显然不起作用存储在数据库中。

在这种情况下,我们创建了一个模型问题,其中包含before_saveafter_saveafter_find个回调来进行操作。

module TemperatureAttributes
  extend ActiveSupport::Concern

  module ClassMethods
    def temperatures(*temperature_attributes)
      options = temperature_attributes.extract_options!
      before_save TemperatureScaleConverter.new(temperature_attributes, options[:if])
      after_save TemperatureScaleConverter.new(temperature_attributes, options[:if])
      after_find TemperatureScaleConverter.new(temperature_attributes, options[:if])
    end
  end
end

这确实有效,但您可以看到它是一个完全不同的代码路径。我不得不想象在Rails中有更好的方法来处理这种情况。

我一直在尝试使用Ruby's prepend method来拦截这些调用,并且也考虑过以类似的方式使用alias_method_chain。我还想过尝试有条件地使用数据库视图来转换最低级别的值。

我不是在找你为我解决我的问题,但如果你对跨越模型的Rails中拦截getter和setter调用的最佳方法有任何建议我很乐意听到它

1 个答案:

答案 0 :(得分:0)

我认为你做的事情比他们必须做的更困难,同时也暗示要使用一些不错的模式。

第一个问题是你使用回调...我认为你想要的是使用after_initialize ......

 class ReadingPresenter
      ...
      after_initialize :presentation_value

      def presentation_value
           TemperatureService.for_current_user @model.value
      end

然后使用此presentation_value而不是value。 (我假设有一个很好的理由拥有ReadingPresenter类,而不仅仅是使用某些模型,如Temperature< ActiveRecord :: Base)。

我认为您可能正在决定使用服务来处理演示文稿值,尤其是我可以想象从应用程序的其他部分绘制状态。我还认为回调中的很多逻辑都是不重要的,并且通过使用像after_initialize这样的东西可以更好地处理。但是,你不仅要管理温度对象的呈现方式,而且处理你的演示文稿作为一个例子,你可以处理一些更原始的事情,这似乎也很奇怪。对自己。