【问题标题】:Intercept getter and setter calls in Rails for data manipulation在 Rails 中拦截 getter 和 setter 调用以进行数据操作
【发布时间】:2015-10-14 16:59:29
【问题描述】:

我正在使用存储温度值的 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 调用的最佳方法有任何建议,我很想听听.

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

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

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

     class ReadingPresenter
          ...
          after_initialize :presentation_value
    
          def presentation_value
               TemperatureService.for_current_user @model.value
          end
    

    然后用这个presentation_value代替value。 (我假设有一个很好的理由拥有 ReadingPresenter 类,而不是仅仅使用像 Temperature

    我认为您可能做出了正确的决定来使用服务来处理表示值,尤其是我可以想象它会从您的应用程序的其他部分获取状态。我还认为你在回调中的很多逻辑都是无关紧要的,最好使用 after_initialize 之类的东西来处理。但是,您不仅要管理 Temperature 对象的表示,而且比将表示作为本身的“事物”处理,您可以更原子地处理它,这似乎也很奇怪。

    【讨论】:

    • 感谢您抽出宝贵时间回答 - 我很感激。我喜欢你在 Presenter 对象中使用after_initialize。我没有想到。除非我有误解,否则当传入华氏值时,我无法使用after_initialize 块来帮助设置摄氏值,对吧?
    • 在没有看到整个代码库的情况下回答有点困难,因此我的回答很模糊。我认为你是对的,但我认为如果你使用 Temperature
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多