<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: injection code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sat, 11 Oct 2008 20:28:44 GMT</pubDate>
    <description>DZone Snippets: injection code</description>
    <item>
      <title>Specifying default values with Ruby's attr_reader and attr_accessor</title>
      <link>http://snippets.dzone.com/posts/show/4382</link>
      <description>Dependency injection in Ruby is as easy as falling off a log. As &lt;a href="http://weblog.jamisbuck.org/2007/7/29/net-ssh-revisited#comment-2734"&gt;Jamis Buck has pointed out&lt;/a&gt;, DI is a good thing, but DI frameworks for Ruby are overkill. The language makes them unnecessary.&lt;br /&gt;&lt;br /&gt;Here's how to enhance attr_reader and attr_accessor so that they can receive an options hash for specifying the default value of an attribute.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;module AttrWithDefaultExtension&lt;br /&gt;  &lt;br /&gt;  module ClassMethods&lt;br /&gt;    &lt;br /&gt;    def attr_accessor(*args)&lt;br /&gt;      attrs, attrs_with_defaults = split_for_last_hash(args)&lt;br /&gt;      attrs_with_defaults.each do |name, default|&lt;br /&gt;        attr_reader_with_default name, default&lt;br /&gt;        attr_writer              name&lt;br /&gt;      end&lt;br /&gt;      &lt;br /&gt;      super(*attrs)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def attr_reader(*args)&lt;br /&gt;      attrs, attrs_with_defaults = split_for_last_hash(args)&lt;br /&gt;      attrs_with_defaults.each do |name, default|&lt;br /&gt;        attr_reader_with_default name, default&lt;br /&gt;      end&lt;br /&gt;      &lt;br /&gt;      super(*attrs)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  private&lt;br /&gt;    &lt;br /&gt;    def attr_reader_with_default(name, default)&lt;br /&gt;      define_method(name) do&lt;br /&gt;        unless instance_variable_defined?("@#{name}")&lt;br /&gt;          default = default.call(self) if default.kind_of?(Proc)&lt;br /&gt;          instance_variable_set "@#{name}", default&lt;br /&gt;        end&lt;br /&gt;        instance_variable_get "@#{name}"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def split_for_last_hash(args)&lt;br /&gt;      if args.last.kind_of?(Hash)&lt;br /&gt;        [args[0...-1], args.last]&lt;br /&gt;      else&lt;br /&gt;        [args, {}]&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def self.included(other_module)&lt;br /&gt;    other_module.extend ClassMethods&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;class Object; include AttrWithDefaultExtension; end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Here are the unit tests. They demonstrate not only the enhanced behavior of attr_reader and attr_accessor, but also that the standard behavior remains unbroken.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;require 'test/unit'&lt;br /&gt;&lt;br /&gt;module AttrWithDefaultExtensionTest&lt;br /&gt;  &lt;br /&gt;  class TwoStandardAttrReaders &lt; Test::Unit::TestCase&lt;br /&gt;    &lt;br /&gt;    attr_reader :foo, :baz&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_first_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@foo)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_first_instance_variable_when_sent_first_attr_reader&lt;br /&gt;      @foo = 'bar'&lt;br /&gt;      assert_equal 'bar', foo&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_second_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@baz)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_second_instance_variable_when_sent_second_attr_reader&lt;br /&gt;      @baz = 'bat'&lt;br /&gt;      assert_equal 'bat', baz&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  class TwoStandardAttrAccessors &lt; Test::Unit::TestCase&lt;br /&gt;    &lt;br /&gt;    attr_accessor :foo, :baz&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_first_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@foo)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_first_instance_variable_when_sent_first_attr_reader&lt;br /&gt;      @foo = 'bar'&lt;br /&gt;      assert_equal 'bar', foo&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_first_instance_variable_when_sent_first_attr_writer&lt;br /&gt;      self.foo = 'bar'&lt;br /&gt;      assert_equal 'bar', @foo&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_second_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@baz)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_second_instance_variable_when_sent_second_attr_reader&lt;br /&gt;      @baz = 'bat'&lt;br /&gt;      assert_equal 'bat', baz&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_second_instance_variable_when_sent_second_attr_writer&lt;br /&gt;      self.baz = 'bat'&lt;br /&gt;      assert_equal 'bat', @baz&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  class TwoStandardAttrReadersAndADefault &lt; Test::Unit::TestCase&lt;br /&gt;    &lt;br /&gt;    attr_reader :foo, :baz, :blit =&gt; 'blat'&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_first_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@foo)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_first_instance_variable_when_sent_first_attr_reader&lt;br /&gt;      @foo = 'bar'&lt;br /&gt;      assert_equal 'bar', foo&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_second_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@baz)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_second_instance_variable_when_sent_second_attr_reader&lt;br /&gt;      @baz = 'bat'&lt;br /&gt;      assert_equal 'bat', baz&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_third_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@blit)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_third_instance_variable_when_sent_third_attr_reader&lt;br /&gt;      blit&lt;br /&gt;      assert_equal 'blat', @blit&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_set_third_instance_variable_if_already_set_when_sent_third_attr_reader&lt;br /&gt;      @blit = 'splat'&lt;br /&gt;      assert_equal 'splat', blit&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  class TwoStandardAttrAccessorsAndADefault &lt; Test::Unit::TestCase&lt;br /&gt;    &lt;br /&gt;    attr_accessor :foo, :baz, :blit =&gt; 'blat'&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_first_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@foo)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_first_instance_variable_when_sent_first_attr_reader&lt;br /&gt;      @foo = 'bar'&lt;br /&gt;      assert_equal 'bar', foo&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_first_instance_variable_when_sent_first_attr_writer&lt;br /&gt;      self.foo = 'bar'&lt;br /&gt;      assert_equal 'bar', @foo&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_second_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@baz)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_return_second_instance_variable_when_sent_second_attr_reader&lt;br /&gt;      @baz = 'bat'&lt;br /&gt;      assert_equal 'bat', baz&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_second_instance_variable_when_sent_second_attr_writer&lt;br /&gt;      self.baz = 'bat'&lt;br /&gt;      assert_equal 'bat', @baz&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_third_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@blit)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_third_instance_variable_when_sent_third_attr_reader&lt;br /&gt;      blit&lt;br /&gt;      assert_equal 'blat', @blit&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_set_third_instance_variable_if_already_set_before_sent_third_attr_reader&lt;br /&gt;      @blit = 'splat'&lt;br /&gt;      assert_equal 'splat', blit&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_third_instance_variable_when_sent_third_attr_writer&lt;br /&gt;      self.blit = 'splat'&lt;br /&gt;      assert_equal 'splat', @blit&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  class AProcDefault &lt; Test::Unit::TestCase&lt;br /&gt;    &lt;br /&gt;    attr_accessor :my_object_id =&gt; Proc.new { |obj| obj.object_id }&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@my_object_id)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_instance_variable_to_result_of_proc_when_sent_attr_reader&lt;br /&gt;      my_object_id&lt;br /&gt;      assert_equal object_id, @my_object_id&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_set_instance_variable_if_already_set_before_sent_attr_reader&lt;br /&gt;      @my_object_id = 'foo'&lt;br /&gt;      assert_equal 'foo', my_object_id&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_instance_variable_when_sent_attr_writer&lt;br /&gt;      self.my_object_id = 'foo'&lt;br /&gt;      assert_equal 'foo', @my_object_id&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  class AProcWithinAProcDefault &lt; Test::Unit::TestCase&lt;br /&gt;    &lt;br /&gt;    attr_accessor :my_proc =&gt; Proc.new { Proc.new { } }&lt;br /&gt;    &lt;br /&gt;    def test_should_not_define_instance_variable&lt;br /&gt;      assert_equal false, instance_variable_defined?(:@my_proc)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_instance_variable_to_result_of_outer_proc_when_sent_attr_reader&lt;br /&gt;      my_proc&lt;br /&gt;      assert_kind_of Proc, @my_proc&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_not_set_instance_variable_if_already_set_before_sent_attr_reader&lt;br /&gt;      @my_proc = 'foo'&lt;br /&gt;      assert_equal 'foo', my_proc&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def test_should_set_instance_variable_when_sent_attr_writer&lt;br /&gt;      self.my_proc = 'foo'&lt;br /&gt;      assert_equal 'foo', @my_proc&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Thu, 02 Aug 2007 14:03:03 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4382</guid>
      <author>njonsson (Nils Jonsson)</author>
    </item>
  </channel>
</rss>
