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.

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete