Hash Initializer

Yes, this is a very simple snippet of code. I just thought I'd throw it on here, since I haven't posted anything new in a while, it's Sunday night, and my belly is full of sushi.

After using ActiveRecord for such a long time, I've come to appreciate its constructor behavior. Pass in a hash, and attributes corresponding with the keys are set automatically. I've become so used to it that I often find myself duplicating the effect in nearly every project I use. So... I finally added it to my personal library as a module:

                  module Panda
                    module HashInitializer
                      def self.included(base)        
                        base.class_eval do
                          alias_method :initialize, :hash_initialize
                        end
                      end
                  
                      def hash_initialize(opts = {})
                        opts.each {|k,v| self.send("#{k}=".intern, v)}
                      end
                    end
                  end
                  

This allows you to do this:

                  require 'hash_initializer'
                  class NameTag
                    attr_accessor :label
                    include Panda::HashInitializer
                  end
                  
                  a = NameTag.new(:label => "Bob")
                  puts a.label
                  # => Bob
                  

Aliasing the default constructor instead of directly overriding allows you to keep the functionality around if you need to do customizations:

                  require 'hash_initializer'
                  class NameTag
                    attr_accessor :label
                    include Panda::HashInitializer
                    def initialize(opts = {})
                      opts[:label] ||= "Untitled person"
                      hash_initialize(opts)
                    end
                  end
                  
                  a = NameTag.new(:label => "Bob")
                  puts a.label
                  # => Bob
                  b = NameTag.new
                  puts b.label
                  # => Untitled person
                  

This is also handy when you want a user to be able to set an initial value, but not be able to change it later (maybe you've done an expensive calculation on the value, maybe you spanked as a child, whatever):

                  require 'hash_initializer'
                  class NumbersOnly
                    attr_reader :a_readonly_number
                    include Panda::HashInitializer
                  
                    def a_readonly_number=(v)
                      @a_readonly_number = v.to_i
                    end
                    private :a_readonly_number=
                  
                  end
                  
                  a = NumbersOnly.new(:a_readonly_number => "5")
                  puts a.a_readonly_number.class
                  # => Fixnum
                  a.a_readonly_number = "10"
                  # Raises NoMethodError: private method `a_readonly_number=' called for #<NumbersOnly:0x896e8 @a_readonly_number=5>
                  

Word.