This code is based on the idea of a Form Object
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
(see #3 if unfamiliar with the concept).
My actual code in question may be found here: https://gist.github.com/frankjmattia/82a9945f30bde29eba88
The code takes a hash of objects/attributes and creates a reverse lookup hash to keep track of their delegations to do this.
delegate :first_name, :email, to: :user, prefix: true
But I am manually creating the delegations from a hash like this:
DELEGATIONS = {
user: [ :first_name, :email ]
}
At runtime when I want to look up the translated attribute names for the objects, all I have to go on are the delegated/prefixed (have to use a prefix to avoid naming collisions) attribute names like :user_first_name which aren’t in sync with the rails i18n way of doing it:
en:
activerecord:
attributes:
user:
email: 'Email Address'
The code I have take the above delegations hash and turns it into a lookup table so when I override human_attribute_name I can get back the original attribute name and its class. Then I send #human_attribute_name to the original class with the original attribute name as its argument.
The code I’ve come up with works but it is ugly to say the least. I’ve never really used #inject so this was a crash course for me and am quite unsure if this code effective way of solving my problem. Could someone recommend a simpler solution that does not require a reverse lookup table or does that seem like the right way to go?
Thanks,
– FJM
I think I would keep the actual class constants intact so you’re not jumping through ActiveSupport’s library so much and also immediately build the delegations table instead of also having the REVERSE_DELEGATIONS constant. Finally, refactor the humanize_attribute_name into two lines for clarity’s sake.
class RegistrationForm
DELEGATIONS = {
User => [
:first_name, :first_name=,
:last_name, :last_name=,
:email, :email=,
:password, :password=,
:password_confirmation, :password_confirmation=
],
Organization => [
:name, :name=
]
}.map{|klass, attrs| attrs.map{|attr| {:"#{klass.to_s.downcase}_#{attr}" => [klass, attr]}}}.flatten.reduce({}, :merge)
def self.human_attribute_name(method)
klass, attribute = DELEGATIONS[method]
klass ? klass.send(:human_attribute_name, attribute) : method
end
end
You could also further refine the reverse_mapping to create both :user_first_name, and :user_first_name= from just :first_name and do away with the always paired declarations.
1