foo = Foo.new.set_bar("baz).set_cat("dog")If you were to define the setter methods, they would all follow the same basic pattern
def bar=(value)The Initialization Chain module assumes you will follow this pattern and removes the need to define them.
@bar = value
end
def set_bar(value)
bar=value
end
Code and Tests:
module InitializationChain
alias :pre_init_chain_method_missing :method_missing
def method_missing(sym, *args, &block)
if matches_set_and_writer_exists?(sym)
self.send convert_set_to_setter(sym), *args, &block
return self
end
pre_init_chain_method_missing(sym, *args, &block)
end
def matches_set_and_writer_exists?(sym)
matches_set?(sym) && writer_exists?(sym)
end
def match_regex
/^set_/
end
def convert_set_to_setter(sym)
"#{sym.to_s.sub(match_regex,'')}=".to_sym
end
def writer_exists?(sym)
self.respond_to?(convert_set_to_setter(sym))
end
def matches_set?(sym)
sym.to_s =~ match_regex
end
end
class Foo
include InitializationChain
attr_accessor :bar
def cat
@cat
end
def cat=(value)
@cat=value
end
end
require 'test/unit'
class InitializationChainTest < Test::Unit::TestCase
def test_matches_set
assert(Foo.new.matches_set?(:set_foo))
end
def test_writer_exists
assert(Foo.new.writer_exists?(:bar))
end
def test_add_dynamic_set_for_attr
foo = Foo.new.set_bar("baz")
assert_equal "baz", foo.bar
end
def test_add_dynamic_set_for_defined_setter
foo = Foo.new.set_cat("dog")
assert_equal "dog", foo.cat
end
end
In Ruby, don't you think
ReplyDeletefoo = Foo.new :bar => 'baz', :cat => 'dog'
would be much more intuitive? You could perhaps implement Foo#update_attributes so you can update an already initialized object. It is easier to read, and Hash initialization is a pretty common pattern in Ruby.
I'm not sure if I either way is better than the other.
ReplyDeleteOn one hand I do like explicitly calling the methods I'm concerned with instead of depending on a hash.
On the other hand I like how options read very explicitly.
Perhaps a happy medium could be creating a module that uses the parameters from the hash to call the associated methods on the object.
I'll probably write that up this weekend...