My Ruby Learning Path: Constructing the Constructor
By divas on Nov 27, 2007
When I started learning Ruby, all of the code samples that I looked at used positional arguments in the constructor, such as with the following code:
def initialize(id, type, price) @id, @type, @price = id, type, price end
By positional arguments, I mean that the first argument must pass the id, the second must pass type, and the third must pass price. Having worked with C and Java, this feels very familiar, and so that is how I did it in my own code. However, when I posted my code samples to the email@example.com alias, a few developers suggested that I use a hash, like in the following code.
def initialize(attributes) @id = attributes['id'] @type = attributes['type'] @price = attributes['price'].to_f end
I can see some advantages of using a hash instead of requiring arguments in a certain order. For one, with positional arguments, there is the possibility of passing the values in the wrong order. Another advantage of a hash is that if you add more attributes to a class, the constructor's signature does not change.
There was a bit of a debate about which version to use. Some developers like the constructor with positional arguments because it clearly documents the intent of the class. Others feel that keyword arguments, along with RDoc, make the code more clear than depending on argument ordering to convey meaning. Comparing the two types of method invocations, I have to agree that the hash seems easier to read.
Item.new 'id' => 'ABC', type' => 'book', 'price' => 17 Item.new 'ABC', 'book', 23
How about you? What are your preferences? Do you carry it even further? Would you ever consider doing something like this?
def initialize(&block) instance_eval &block end item = Item.new do self.id = 'ABC' ... end
Do you know of scenarios where it might be appropriate to use code like this?
def initialize(attributes) @attributes = attributes.with_indifferent_access end def method_missing(symbol, \*args) @attributes[symbol] ? @attributes[symbol] : super end
Charles Nutter has blogged about how to extend Class with a field-initializing 'new' method. Jay Fields posted in his blog code forcreating constructors that can take either positional arguments or a hash. For someone starting out with Ruby, I find these latter methods a bit uncomfortable, but it does save a bit of finger work. What do you think?