From: Erik Veenstra Date: 2007-10-11T18:27:31+09:00 Subject: Re: Why does this use a block About using blocks in constructors in general... (Although "initialize" is an initializer and not a constructor. It only takes care of one part of the construction.) "initialize" should take care of the initialization of the object: Setting instance variables and checking whether the instance variables are set correctly. It's the latter which is important. Setting instance variables is traditionally done in two ways: a list of arguments and a hash of options. (Or a combination of them.) You might want to call them "arguments" and "named arguments". The list: def initialize(first_name, last_name) @first_name = first_name.to_s @last_name = last_name.to_s raise ArgumentError, "first name isn't set" unless @first_name raise ArgumentError, "last name isn't set" unless @last_name end The hash: def initialize(options={}) @case_sensitive = options[:case_sensitive] || true @direction = options[:direction] || :forward raise ArgumentError, "" unless @first_name end In Java, you'll often see a third way (Java style, Ruby code): attr_writer :first_name attr_writer :last_name def initialize end something = Something.new something.first_name = "Erik" something.last_name = "Veenstra" Personally, I think this is conceptually unacceptable: There's no way for the constructor to check whether the instance variables are set, or whether they are set correctly. This can be overcome by calling the initializer with a block: attr_writer :first_name attr_writer :last_name def initialize(&block) block.call(self) raise ArgumentError, "first name isn't set" unless @first_name raise ArgumentError, "last name isn't set" unless @last_name end Which is called like this: Something.new do |something| something.first_name = "Erik" something.last_name = "Veenstra" end Personally, I always try to freeze the object once it is created: (This has advantages beyond the scope of this writing.) attr_writer :first_name attr_writer :last_name def initialize(&block) block.call(self) raise ArgumentError, "first name isn't set" unless @first_name raise ArgumentError, "last name isn't set" unless @last_name freeze end There's some more advantages. For example, the object can often be collected earlier. Consider this example, in the main of the application: direction = :forward EV::CommandLine.new do |cl| cl.option("-r", "--reverse") {direction = :backward} end.parse(ARGV) ...instead of... direction = :forward cl = EV::CommandLine.new cl.option("-r", "--reverse") {direction = :backward} cl.parse(ARGV) When will cl be collected, in the second piece of code? Never? It's just my opinion. Feel free to ignore me... gegroet, Erik V. - http://www.erikveen.dds.nl/