如何在 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.all
或 Product.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 这样的库处理复杂性。
我正在尝试将会话值分配给模型对象,如下所示。
# 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.all
或 Product.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 这样的库处理复杂性。