From: Sean O'Halpin Date: 2005-10-15T11:00:44+09:00 Subject: Factory function like Array() for your own classes Hi, I was quite surprised today to find out that you can define both a class and a function with the same name and still be able to reference them both separately. This makes it possible to define factory functions that work like the built-in Array() and String() functions, e.g. so that Thing(1,2,3) is the same as Thing.new(1,2,3). (I know that Array(4) is different from Array.new(4) but I'm talking about what it looks like here.) Here's an example: class Thing factory # defaults to name of class # ... end t = Thing(:name => "glove", :description => "white") t = Thing :name => "glove", :description => "white" t = Thing() { |x| x.name = "Alice" } t = Thing :name => "White Rabbit" do |x| x.description = "flustered" end Here's the implementation (it really is very simple): def factory(name = self.to_s) Kernel::eval " def #{name}(*args, &block) #{name}.new(*args, &block) end " end Note that I use eval here rather than define_method because you can't pass blocks to blocks (yet). With this function, you can define factory functions for any class, e.g. factory :SomeOtherClass soc = SomeOtherClass(*args, &block) Here's a script to test interaction with blocks: if __FILE__ == $0 class Thing attr_accessor :attrs factory def initialize(attrs = {}, &block) @attrs = attrs block.call(self) if block_given? end ends t = Thing() p t t = Thing :name => "glove", :description => "white" p t p Thing() { |x| x.attrs[:name] = "Alice" } wr = Thing :name => "White Rabbit" do |obj| obj.attrs[:description] = "flustered" end p wr # and just to show that the class is still there t = Thing.new(:name => "Carpenter") p Module.const_get(:Thing) p method(:Thing) end I think this is a very natural idiom which doesn't seem to have any unwelcome side-effects. I'm curious what the general opinion is. It seems a cleaner solution than,say, aliasing [] to new (which I used to do a lot). So... what do you think? Regards, Sean