Wednesday, January 20, 2010

Ruby and Simple Dynamic Programming IV

After my last post, Gregory Brown, the author of Ruby Best Practices wrote in to note that not only was eval evil, it was not ever really necessary. He suggested that the attr_r, attr_w, and attr_rw could be better implemented by using define_method, instance_variable_get, and instance_variable_set. So, as I like to do after someone smart shows me a better way of doing things, I reimplemented our last piece of code using these methods. It's really pretty straightforward so there's not much to really talk about, so here it is:

# Open the class Class and add three new access methods, access_r, access_w, access_rw.
# These are really just reimplementations for attr_reader, attr_writer, attr_accessor.
# This version will use define_method, instance_variable_get,
# and instance_variable_set to do this (based on a recommendation from Gregory Brown.
class Class

# Provide read access only. Take a list of symbols and create a
# method that will all the user to access each symbol.
def access_r(*symbols)
symbols.each do |symbol|
define_method(symbol) do
instance_variable_get("@#{symbol}")
end
end
end

# Provide write access only. Take a list of symbols and create a
# method that will all the user to write each symbol.
def access_w(*symbols)
symbols.each do |symbol|
define_method("#{symbol}=") do |val|
instance_variable_set("@#{symbol}", val)
end
end
end

# Provide read/write access. Take a list of symbols and create two
# methods that will all the user to read and write each symbol.
def access_rw(*symbols)
symbols.each do |symbol|
define_method(symbol) do
instance_variable_get("@#{symbol}")
end

define_method("#{symbol}=") do |val|
instance_variable_set("@#{symbol}", val)
end
end
end

end

if __FILE__ == $PROGRAM_NAME

# Using the new attribute accessor methods.
class Foo
access_r :bar
access_w :qux
access_rw :baz, :quux

# Since bar is read only from the outside, we'll just initialize
# it here.
def initialize(bar)
@bar = bar
end

# Since qux is write only, we'll create a method that lets us view
# it.
def show_qux
puts "qux = #{@qux}"
end

end


# Create a new foo and initialize bar (read only) with goodbye.
# Show that we can then access it.
foo = Foo.new ("goodbye")
puts "foo.bar = #{foo.bar}"


# Set and then access the two variables we set to
# read/write.
foo.baz = "hello"
puts "foo.baz = #{foo.baz}"

foo.quux = "world"
puts "foo.quux = #{foo.quux}"

# Set the write only field and then display it using the show_qux
# method.
foo.qux = "test"
foo.show_qux

# Try to access the write only field for reading. This should
# fail with an undefined method.
puts foo.qux

end



Like I said, it's all pretty straightforward. Also, here's a link to someone who's done something very similar using the same technique (there's some other good posts in there, so take some time and read a bit of it).

As always, let me know if you have questions or comments and thanks to Gregory Brown for starting me down this path.

Friday, January 1, 2010

Ruby and Simple Dynamic Programming III

In my last couple of posts, here and here, I began talking about dynamic programming using Ruby. In this post, I'm going to continue on the same lines and this time we're going to discuss using the eval series of methods, specifically class_eval. Before getting started however, here's a word of warning. Gregory Brown in Ruby Best Practices describes eval (and related methods) as "evil". The truth is that you need to be pretty careful about using these methods as they offer ample opportunity for a user to do some pretty bad things to your system. With that said ...

As you know, Ruby offers three convenience methods for read, write, and read/write access to variables and these are attr_reader, attr_writer, and attr_accessor respectively. Let's take a look at how we could implement these if they hadn't already been thoughtfully provided.

Here's the code:

# Open the class Class and add three new access methods, access_r, access_w, access_rw.
# These are really just reimplementations for attr_reader, attr_writer, attr_accessor.
class Class
# Provide read access only. Take a list of symbols and create a
# method that will all the user to access each symbol.
def access_r(*symbols)
symbols.each { | symbol |
class_eval "def #{symbol}() @#{symbol}; end"
}
end

# Provide write access only. Take a list of symbols and create a
# method that will all the user to write each symbol.
def access_w(*symbols)
symbols.each { | symbol |
class_eval "def #{symbol}=(val) @#{symbol} = val; end"
}
end

# Provide read/write access. Take a list of symbols and create two
# methods that will all the user to read and write each symbol.
def access_rw(*symbols)
symbols.each { | symbol |
class_eval "def #{symbol}() @#{symbol}; end"
class_eval "def #{symbol}=(val) @#{symbol} = val; end"
}
end

end

if __FILE__ == $PROGRAM_NAME

# Using the new attribute accessor methods.
class Foo
access_r :bar
access_w :qux
access_rw :baz, :quux

# Since bar is read only from the outside, we'll just initialize
# it here.
def initialize(bar)
@bar = bar
end

# Since qux is write only, we'll create a method that lets us view
# it.
def show_qux
puts "qux = #{@qux}"
end

end


# Create a new foo and initialize bar (read only) with goodbye.
# Show that we can then access it.
foo = Foo.new ("goodbye")
puts "foo.bar = #{foo.bar}"


# Set and then access the two variables we set to
# read/write.
foo.baz = "hello"
puts "foo.baz = #{foo.baz}"

foo.quux = "world"
puts "foo.quux = #{foo.quux}"

# Set the write only field and then display it using the show_qux
# method.
foo.qux = "test"
foo.show_qux

# Try to access the write only field for reading. This should
# fail with an undefined method.
puts foo.qux

end



We start out opening Class as this is where we're going to add our new methods, access_r, access_w, and access_rw. We're going to take a list of symbols, represented here with "*symbols" and covert each of the symbols into a new method. For access_r, we'll loop through the symbols and then generate a method with the name "symbol" that returns symbol. For example if we have a symbol :x, we'll get a method that looks like def x() @x; end. The access_rw will give us, for the same symbol :x, def x=(val) @x=val; end. Finally, the access_rw will give us both methods (there's probably a clean way to refactor this so we don't have duplicated code, but this is left as an exercise for the reader).

Finally, we create a class Foo, that uses all three access methods and then some code that exercises each one. The final call is a read access to a write only field and should fail with an undefined method.

As always, let me know if you have any questions or comments.