Recently my colleague showed my a little trick that I found to be very useful in some situations. It’s nothing fancy or mind-blowing or unusual in terms of using Ruby. It’s just applied in a way that I haven’t seen before. It kind of even seems obvious after seeing it :)
class Order < ActiveRecord::Base has_one :meta_data, dependent: :destroy, autosave: true def meta_data super || build_meta_data end delegate :ip_address, :ip_address= :user_agent, :user_agent= to: :meta_data, prefix: false end
Now you can just do:
order.ip_address = request.remote_ip order.save!
without wondering if
if this associated record was never saved then
build_meta_data will create a new one for you.
Same goes with reading such attributes. You can get
but you won’t get
NoMethodError from calling
on an empty association (
Not so nice
It has some downsides, however. Reading (event an empty)
can trigger a side-effect in saving the
ip = order.ip_address order.save!
MetaData can not have non-null columns unless you set all of them
at the same time. Otherwise, when
ip_address can be null but
user_agent cannot, setting only
one of them will cause troubles.
order.ip_address = request.remote_ip order.save! # Exception
The same problem can occur with validations on
But if you don’t have such situations in your code and just have multiple attributes that are either optional or all set at the same time, then why not.
Check out more patterns that can help you in maintaining Rails apps in our Fearless Refactoring: Rails controllers ebook