如何抽象 Rails 中多个模型使用的枚举属性?
How to abstract enum attribute used by multiple models in Rails?
我有一个名为 currency
的数据库属性,它被多个模型使用。我想创建一个关注点(或类似的)来定义 currency
属性的基本功能。如果它只用于单个模型,我可能会这样做:
class Transaction < ApplicationRecord
enum currency: [ :USD, :AUD ]
validates :currency, inclusion: { in: currencies.keys }
end
但是因为它被多个模型使用,所以我想做这样的事情:
class Transaction < ApplicationRecord
include Currency # maybe in a Concern?
acts_as_currency :currency
end
整个想法是能够执行 Transaction.first.currency.USD?
和枚举属性具有的类似功能,以及在一个地方定义属性验证。
你会如何用 Rails 设计它?有什么首选方法吗?
在 Ruby 中,共享行为需要模块,ActiveSupport 关注点提供了很酷的语法糖,e.g. included
# app/models/concerns/has_currency.rb
module HasCurrency
extend ActiveSupport::Concern
included do
enum currency: [ :USD, :AUD ]
validates :currency, inclusion: { in: currencies.keys }
end
end
class Transaction < ApplicationRecord
include HasCurrency
end
我们可以通过一个函数来给我们关注的用户更多的自由,正如你似乎建议的那样:
# app/models/concerns/has_currency.rb
module HasCurrency
def acts_as_currency(column = :currency)
enum column => [ :USD, :AUD ]
validates column, inclusion: { in: column.to_s.pluralize.keys }
end
end
class Transaction < ApplicationRecord
extend HasCurrency # Note the `extend`, not `include`
acts_as_currency
end
基于 Matthieu Libeer 的回答,我建立了这个问题以在我的所有模型中使用。
module SharedEnums
def acts_as_enum(*fields)
@fields = fields
dynamic_enums
end
private
attr_reader :fields
attr_accessor :enum_field
def dynamic_enums
fields.each do |enum_field|
@enum_field = enum_field
case enum_field
when :attr_1 then format_enum('MODEL.ATTR_1')
when :attr_2 then format_enum('MODEL.ATTR_2')
end
end
end
def format_enum(i18n)
enum_array = I18n.t("activerecord.enums.#{i18n}").stringify_keys.keys
enum({ "#{enum_field}": Hash[enum_array.zip(enum_array)] })
end
end
通过扩展 SharedEnums 并在需要的模型中添加 act_as_enum 方法来使用它非常简单。
class Model < ApplicationRecord
extend SharedEnums
acts_as_enum :attr_1, :attr_2
end
我有一个名为 currency
的数据库属性,它被多个模型使用。我想创建一个关注点(或类似的)来定义 currency
属性的基本功能。如果它只用于单个模型,我可能会这样做:
class Transaction < ApplicationRecord
enum currency: [ :USD, :AUD ]
validates :currency, inclusion: { in: currencies.keys }
end
但是因为它被多个模型使用,所以我想做这样的事情:
class Transaction < ApplicationRecord
include Currency # maybe in a Concern?
acts_as_currency :currency
end
整个想法是能够执行 Transaction.first.currency.USD?
和枚举属性具有的类似功能,以及在一个地方定义属性验证。
你会如何用 Rails 设计它?有什么首选方法吗?
在 Ruby 中,共享行为需要模块,ActiveSupport 关注点提供了很酷的语法糖,e.g. included
# app/models/concerns/has_currency.rb
module HasCurrency
extend ActiveSupport::Concern
included do
enum currency: [ :USD, :AUD ]
validates :currency, inclusion: { in: currencies.keys }
end
end
class Transaction < ApplicationRecord
include HasCurrency
end
我们可以通过一个函数来给我们关注的用户更多的自由,正如你似乎建议的那样:
# app/models/concerns/has_currency.rb
module HasCurrency
def acts_as_currency(column = :currency)
enum column => [ :USD, :AUD ]
validates column, inclusion: { in: column.to_s.pluralize.keys }
end
end
class Transaction < ApplicationRecord
extend HasCurrency # Note the `extend`, not `include`
acts_as_currency
end
基于 Matthieu Libeer 的回答,我建立了这个问题以在我的所有模型中使用。
module SharedEnums
def acts_as_enum(*fields)
@fields = fields
dynamic_enums
end
private
attr_reader :fields
attr_accessor :enum_field
def dynamic_enums
fields.each do |enum_field|
@enum_field = enum_field
case enum_field
when :attr_1 then format_enum('MODEL.ATTR_1')
when :attr_2 then format_enum('MODEL.ATTR_2')
end
end
end
def format_enum(i18n)
enum_array = I18n.t("activerecord.enums.#{i18n}").stringify_keys.keys
enum({ "#{enum_field}": Hash[enum_array.zip(enum_array)] })
end
end
通过扩展 SharedEnums 并在需要的模型中添加 act_as_enum 方法来使用它非常简单。
class Model < ApplicationRecord
extend SharedEnums
acts_as_enum :attr_1, :attr_2
end