I have a database attribute called currency
which is used by multiple models. I'd like to create a concern (or similar) which defines the basic functionality of the currency
attribute. If it was only used in a single model, I'd probably do something like this:
class Transaction < ApplicationRecord
enum currency: [ :USD, :AUD ]
validates :currency, inclusion: { in: currencies.keys }
end
But since it's used by multiple models, I'd like to do something like this instead:
class Transaction < ApplicationRecord
include Currency # maybe in a Concern?
acts_as_currency :currency
end
The whole idea is to be able to do things like Transaction.first.currency.USD?
and similar functionality that an enum attribute has, as well as defining the attribute validation in a single place.
How would you design that with Rails? Is there any preferred way to do so?
In Ruby, shared behaviours call for modules, ActiveSupport concerns provide cool syntactic sugar, 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
We can give the user of our concern more freedom by going through a function instead, as you seem to suggest:
# 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