Introduction
SuperMap is a two-way, multi-purpose ordered hash with some additional capabilities (additional attributes, translations). It is meant to be used with Rails, but it doesn’t depend on them, so someone might find it useful in other environments.
Ever had an enumerable field in a model that would map to a select box in a form? Wanted some nice way to operate it as symbol in the code, integer in database and label in views? That’s where SuperMap comes into play.
Example
Let’s say there is a Payment model with two enumerable fields:
-
payment_type: Cash In, Cash Out, Bonus, Commission
-
payment_method: Paypal, Bank Transfer, Manual
We define 2 SuperMaps:
class Payment
PAYMENT_TYPES = SuperMap.new( [:cash_in, 1, { :label => “Cash In”, :tax => 0.01 }], [:cash_out, 2, { :label => “Cash Out”, :tax => 0.02 }], [:bonus, 123, { :label => “Bonus Payment”, :tax => 0.03 }], [:commission, 384728, { :tax => 0.01 }] )
PAYMENT_METHODS = SuperMap.new( [:paypal, 1, { :label => “Paid via Paypal”, :favorite => true }], [:bank_transfer, 2], [:manual, 3], :translation_scope => “activerecord.models.payment.payment_methods” )
end
PAYMENT_TYPES defines options for payment_type field. Some labels are set explicitly, for other options key.titleize will be used implicitly. We also defined a custom attribute :tax, which can be accessed using SuperMap#attribute method.
PAYMENT_METHODS define translation_scope, which means labels will be present in translations file (I18n). :paypal will have custom label, which overrides translations.
We can than use those SuperMaps to handle attributes for Payment instances: payment = Payment.new( :payment_method => 1 ) payment.payment_type = PAYMENT_TYPES # Set to 2 label = PAYMENT_TYPES.label( payment.payment_type ) # “Cash Out” label = PAYMENT_TYPES.label( :bonus ) # “Bonus Payment” value = PAYMENT_METHODS # 1 tax = PAYMENT_TYPES.attribute( payment.payment_type, :tax ) # 0.02 tax = PAYMENT_TYPES.attribute( :bonus, :tax ) # 0.03
We can also use
PAYMENT_TYPES.labeled_values
to get the form directly suitable to pass options to f.select helper method.
To make things even simpler, we can use super_mapped_attr:
class Payment … # SuperMaps declarations
super_mapped_attr :payment_type, PAYMENT_TYPES super_mapped_attr :payment_method, PAYMENT_METHODS
end
Now a whole lot of new methods comes into play:
p = Payment.new( :payment_method => 1 ) p.payment_method_label # “Paid via Paypal” p.payment_method_key # :paypal p.payment_method_attr( :favorite ) # true p.payment_method_key = :manual p.payment_method # 3
Thanks
Many thanks go to Stefan Nothegger and Sharewise project (www.sharewise.com), where the original idea and large parts of the code originate from.