Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-2 of 2 total  RSS 

Specifying default values with Ruby's attr_reader and attr_accessor

Dependency injection in Ruby is as easy as falling off a log. As Jamis Buck has pointed out, DI is a good thing, but DI frameworks for Ruby are overkill. The language makes them unnecessary.

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.

module AttrWithDefaultExtension
  
  module ClassMethods
    
    def attr_accessor(*args)
      attrs, attrs_with_defaults = split_for_last_hash(args)
      attrs_with_defaults.each do |name, default|
        attr_reader_with_default name, default
        attr_writer              name
      end
      
      super(*attrs)
    end
    
    def attr_reader(*args)
      attrs, attrs_with_defaults = split_for_last_hash(args)
      attrs_with_defaults.each do |name, default|
        attr_reader_with_default name, default
      end
      
      super(*attrs)
    end
    
  private
    
    def attr_reader_with_default(name, default)
      define_method(name) do
        unless instance_variable_defined?("@#{name}")
          default = default.call(self) if default.kind_of?(Proc)
          instance_variable_set "@#{name}", default
        end
        instance_variable_get "@#{name}"
      end
    end
    
    def split_for_last_hash(args)
      if args.last.kind_of?(Hash)
        [args[0...-1], args.last]
      else
        [args, {}]
      end
    end
    
  end
  
  def self.included(other_module)
    other_module.extend ClassMethods
  end
  
end

class Object; include AttrWithDefaultExtension; end


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.

require 'test/unit'

module AttrWithDefaultExtensionTest
  
  class TwoStandardAttrReaders < Test::Unit::TestCase
    
    attr_reader :foo, :baz
    
    def test_should_not_define_first_instance_variable
      assert_equal false, instance_variable_defined?(:@foo)
    end
    
    def test_should_return_first_instance_variable_when_sent_first_attr_reader
      @foo = 'bar'
      assert_equal 'bar', foo
    end
    
    def test_should_not_define_second_instance_variable
      assert_equal false, instance_variable_defined?(:@baz)
    end
    
    def test_should_return_second_instance_variable_when_sent_second_attr_reader
      @baz = 'bat'
      assert_equal 'bat', baz
    end
    
  end
  
  class TwoStandardAttrAccessors < Test::Unit::TestCase
    
    attr_accessor :foo, :baz
    
    def test_should_not_define_first_instance_variable
      assert_equal false, instance_variable_defined?(:@foo)
    end
    
    def test_should_return_first_instance_variable_when_sent_first_attr_reader
      @foo = 'bar'
      assert_equal 'bar', foo
    end
    
    def test_should_set_first_instance_variable_when_sent_first_attr_writer
      self.foo = 'bar'
      assert_equal 'bar', @foo
    end
    
    def test_should_not_define_second_instance_variable
      assert_equal false, instance_variable_defined?(:@baz)
    end
    
    def test_should_return_second_instance_variable_when_sent_second_attr_reader
      @baz = 'bat'
      assert_equal 'bat', baz
    end
    
    def test_should_set_second_instance_variable_when_sent_second_attr_writer
      self.baz = 'bat'
      assert_equal 'bat', @baz
    end
    
  end
  
  class TwoStandardAttrReadersAndADefault < Test::Unit::TestCase
    
    attr_reader :foo, :baz, :blit => 'blat'
    
    def test_should_not_define_first_instance_variable
      assert_equal false, instance_variable_defined?(:@foo)
    end
    
    def test_should_return_first_instance_variable_when_sent_first_attr_reader
      @foo = 'bar'
      assert_equal 'bar', foo
    end
    
    def test_should_not_define_second_instance_variable
      assert_equal false, instance_variable_defined?(:@baz)
    end
    
    def test_should_return_second_instance_variable_when_sent_second_attr_reader
      @baz = 'bat'
      assert_equal 'bat', baz
    end
    
    def test_should_not_define_third_instance_variable
      assert_equal false, instance_variable_defined?(:@blit)
    end
    
    def test_should_set_third_instance_variable_when_sent_third_attr_reader
      blit
      assert_equal 'blat', @blit
    end
    
    def test_should_not_set_third_instance_variable_if_already_set_when_sent_third_attr_reader
      @blit = 'splat'
      assert_equal 'splat', blit
    end
    
  end
  
  class TwoStandardAttrAccessorsAndADefault < Test::Unit::TestCase
    
    attr_accessor :foo, :baz, :blit => 'blat'
    
    def test_should_not_define_first_instance_variable
      assert_equal false, instance_variable_defined?(:@foo)
    end
    
    def test_should_return_first_instance_variable_when_sent_first_attr_reader
      @foo = 'bar'
      assert_equal 'bar', foo
    end
    
    def test_should_set_first_instance_variable_when_sent_first_attr_writer
      self.foo = 'bar'
      assert_equal 'bar', @foo
    end
    
    def test_should_not_define_second_instance_variable
      assert_equal false, instance_variable_defined?(:@baz)
    end
    
    def test_should_return_second_instance_variable_when_sent_second_attr_reader
      @baz = 'bat'
      assert_equal 'bat', baz
    end
    
    def test_should_set_second_instance_variable_when_sent_second_attr_writer
      self.baz = 'bat'
      assert_equal 'bat', @baz
    end
    
    def test_should_not_define_third_instance_variable
      assert_equal false, instance_variable_defined?(:@blit)
    end
    
    def test_should_set_third_instance_variable_when_sent_third_attr_reader
      blit
      assert_equal 'blat', @blit
    end
    
    def test_should_not_set_third_instance_variable_if_already_set_before_sent_third_attr_reader
      @blit = 'splat'
      assert_equal 'splat', blit
    end
    
    def test_should_set_third_instance_variable_when_sent_third_attr_writer
      self.blit = 'splat'
      assert_equal 'splat', @blit
    end
    
  end
  
  class AProcDefault < Test::Unit::TestCase
    
    attr_accessor :my_object_id => Proc.new { |obj| obj.object_id }
    
    def test_should_not_define_instance_variable
      assert_equal false, instance_variable_defined?(:@my_object_id)
    end
    
    def test_should_set_instance_variable_to_result_of_proc_when_sent_attr_reader
      my_object_id
      assert_equal object_id, @my_object_id
    end
    
    def test_should_not_set_instance_variable_if_already_set_before_sent_attr_reader
      @my_object_id = 'foo'
      assert_equal 'foo', my_object_id
    end
    
    def test_should_set_instance_variable_when_sent_attr_writer
      self.my_object_id = 'foo'
      assert_equal 'foo', @my_object_id
    end
    
  end
  
  class AProcWithinAProcDefault < Test::Unit::TestCase
    
    attr_accessor :my_proc => Proc.new { Proc.new { } }
    
    def test_should_not_define_instance_variable
      assert_equal false, instance_variable_defined?(:@my_proc)
    end
    
    def test_should_set_instance_variable_to_result_of_outer_proc_when_sent_attr_reader
      my_proc
      assert_kind_of Proc, @my_proc
    end
    
    def test_should_not_set_instance_variable_if_already_set_before_sent_attr_reader
      @my_proc = 'foo'
      assert_equal 'foo', my_proc
    end
    
    def test_should_set_instance_variable_when_sent_attr_writer
      self.my_proc = 'foo'
      assert_equal 'foo', @my_proc
    end
    
  end
  
end

Accessing private methods and fields of a Java class

This class uses reflection to enable you to invoke private methods on a class, or access its private fields. This can be useful for unit testing.

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import junit.framework.Assert;

/**
 * Provides access to private members in classes.
 */
public class PrivateAccessor {
	
  public static Object getPrivateField (Object o, String fieldName) {   
	 // Check we have valid arguments... 
    Assert.assertNotNull(o);
    Assert.assertNotNull(fieldName);
    
    // Go and find the private field... 
    final Field fields[] = o.getClass().getDeclaredFields();
    for (int i = 0; i < fields.length; ++i) {
      if (fieldName.equals(fields[i].getName())) {
        try {
          fields[i].setAccessible(true);
          return fields[i].get(o);
        } 
        catch (IllegalAccessException ex) {
          Assert.fail ("IllegalAccessException accessing " + fieldName);
        }
      }
    }
    Assert.fail ("Field '" + fieldName +"' not found");
    return null;
  }
  
  public static Object invokePrivateMethod (Object o, String methodName, Object[] params) {   
		 // Check we have valid arguments... 
	    Assert.assertNotNull(o);
	    Assert.assertNotNull(methodName);
	    Assert.assertNotNull(params);
	    
	    // Go and find the private method... 
	    final Method methods[] = o.getClass().getDeclaredMethods();
	    for (int i = 0; i < methods.length; ++i) {
	      if (methodName.equals(methods[i].getName())) {
	        try {
	          methods[i].setAccessible(true);
	          return methods[i].invoke(o, params);
	        } 
	        catch (IllegalAccessException ex) {
	          Assert.fail ("IllegalAccessException accessing " + methodName);
	        }
	        catch (InvocationTargetException ite) {
	        	Assert.fail ("InvocationTargetException accessing " + methodName);	        	
	        }
	      }
	    }
	    Assert.fail ("Method '" + methodName +"' not found");
	    return null;
	  }  
}
« Newer Snippets
Older Snippets »
Showing 1-2 of 2 total  RSS