如何在 ruby rails 中为每个活动记录对象设置不同的值?

How to set different values for setters per active record object in ruby rails?

我正在尝试将会话值分配给模型对象,如下所示。

 # models/product.rb 

 attr_accessor :selected_currency_id, :selected_currency_rate, :selected_currency_icon

 def initialize(obj = {})
    selected_currency_id = obj[:currency_id]
    selected_currency_rate = obj[:currency_rate] 
    selected_currency_icon = obj[:currency]
 end

但这仅在我初始化新产品对象时有效

selected_currency = (session[:currency].present? ? session : Currency.first.attributes)  
Product.new(selected_currency)

同时,我需要在每个产品对象上自动设置这些 setter 方法,即使是从数据库中获取的。(活动记录对象)即。 Product.allProduct.first

早些时候,我在控制器端从数据库中检索每个产品对象后,手动为每个产品对象分配值。

@products.each do |product|
   product.selected_currency_id = session[:currency_id] 
   product.selected_currency_rate = session[:currency_rate] 
   product.selected_currency_icon = session[:currency]
end

但是我需要在需要显示产品详细信息的每种方法上执行此操作。请建议一个更好的替代方法,以在 activerecord 对象上自动设置这些 setter 方法。

我认为您根本不想在模型层上执行此操作。您绝对不想做的一件事是覆盖模型上的初始化程序并更改其签名而不是调用 super。

你的模型应该只知道它自己的货币。以另一种货币显示价格应该是另一个对象的关注点,例如装饰器或辅助方法。

例如,一个非常简单的实现是:

class ProductDecorator < SimpleDelegator
  attr_accessor :selected_currency

  def initialize(product, **options)
    # Dynamically sets the ivars if a setter exists
    options.each do |k,v|
      self.send "#{k}=", v if self.respond_to? "#{k}="
    end
    super(product) # sets up delegation
  end

  def price_in_selected_currency
    "#{ price * selected_currency.rate } #{selected_currency.icon}"
  end
end 

class Product
  def self.decorate(**options)
    self.map { |product|  product.decorate(options) }
  end

  def decorate(**options)
    ProductDecorator.new(self, options)
  end
end

然后您将装饰控制器中的模型实例:

class ProductsController
  before_action :set_selected_currency 

  def index
    @products = Product.all
                       .decorate(selected_currency: @selected_currency)
  end

  def show
    @product = Product.find(params[:id])
                      .decorate(selected_currency: @selected_currency)
  end

  private
    def set_selected_currency 
      @selected_currency = Currency.find(params[:selected_currency_id])
    end
end

但是你不需要重新发明轮子,装饰器模式有很多实现,比如 Draper,处理货币本地化很复杂,你真的想看看使用像 money gem 这样的库处理复杂性。