From: Trans Date: 2005-10-15T16:41:59+09:00 Subject: Re: Factory function like Array() for your own classes Sean O'Halpin wrote: > 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? Yep. Yep. Yep. I've sugegsted it before. But some people don't like it, insisting that the ".new" gives some sort of additional clarity. Don't know, but like your (our) way better --even wish it were built-in. T.