a generic solution to defaults in AR model initialization

tron
2008-09-08T10:07:10Z

This afterthought to this ancient but still relevant post
http://groups.google.com/group/rubyonrails-core/browse_thread/thread/b509a2fe2b62ac5

After realizing that after_initialize callback is a serious misnomer,
I had to figure out a way
to implement a generic solution for defaults.

Below is what I came up with.
You can simply give a defaults class method in your models. Features:

* It is also able to assign to associations
* warns against undefined assigners (typos)
* defaults can be overriden in the hash passed to the ''new''
constructor
* block given to new are correctly executed

Comments welcome

V

E.g.
class Person < ActiveRecord::Base
  has_one :profile
  def self.defaults
   { :profile => Profile.new, :name => ''tron''  }
 end
end

config/initializers/active_record_defaults.rb

class ActiveRecord::Base
  # this does not work with associations...

  def initialize_with_defaults(attributes=nil,&block)
    defaults = self.class.respond_to?(:defaults) ?
self.class.defaults : false

    initialize_without_defaults(attributes) do

      if defaults then
        defaults.each_pair do |a,v|
          assigner_f = "#{a}="
          # we force check of default attributes even if
          # they are assigned already but do not override
          if respond_to?(assigner_f) then
            # assign value if it does not already have one
            # nil is important cause value can be false!!
            send(assigner_f,v) if send(a).nil?
          else
            raise(ArgumentError.new("unable to assign to
''%s''" % a))
          end
        end
      end
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end