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. First parameter is a key that should be used to access this element instead of using value, which comes second as integer (and is possibly stored in database). Note that value might be totally random, it just needs be unique across elements. 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 - note that it’s totally optional). :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.