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.