From: Gunnar Andersson Date: 2001-10-30T07:45:52+09:00 Subject: [ruby-talk:23768] RCR: Fun with attribute shortcuts solves RCR #3 and more Hi everyone, remember this? >I'm constantly creating small classes, and many of them start something >like: > class Msg > def initialize(name, type, txt) > @name, @type, @txt = name, type, txt > end > # ... > end Yeah, it's a classic, RCR #3. We all hate repetition and typing stuff that is obvious, right? Last time it was Joe McDonald who said: > and if you have a sec, vote on RCR #3. It's a pet peeve of mine. I think it's a pet peeve for a lot of us but I can see why RCR #3 was rejected as it stands now. What would it mean for example, if you created another function in the same way, like def func(@instance_var) - is @instance_var assigned also in that case? If not, why should there be a special case for initialize? So instead I propose to extend on the much loved shortcut methods that exist for accessors. I.e. attr_accessor :a, :b, :c creates getters and setters for @a, @b and @c so why not: class Mine attr_initializer :a, :b, :c ... end attr_initializer creates an initializer for them: def initialize(a, b, c) @a, @b, @c = a, b, c end So there's the idea - I thought it was quite nice but now comes the fun part. I love this - when you get a good idea, often it's easy to implement in Ruby without touching any interpreter or such! For example, if you didn't get this yet, notice that "attr_accessor" and the like are described in docs as being just plain methods, in the class Module! So in some spare time at RubyConf and OOPSLA I fooled around with this and got really positive responses, for example at the BOF session at OOPSLA. Trying it out for real makes a much better impact. Using the eval-family of functions and the open classes, for example "Class" and "Module", you can do a lot of cool stuff that looks a lot like extentions of the language syntax, so here is my implementation of attr_initializer: # file: AttrInitializer class Module def attr_initializer(*attrs) alist = plist = "" attrs.each do |v| alist += "@#{v}," # >> @var1,@var2,@var3, ... plist += "#{v}," # >> var1,var2,var3, ... end alist.chop!; plist.chop! # Remove last comma class_eval "def initialize(#{plist}); #{alist} = #{plist}; end" end end Since the Class class inherits from Module, now you can just require this file and you are able to use the attr_initializer shortcut in all classes. OK, now let's keep it going. What else is common to define in many classes? Well, the default definition of equality is that the compared references must be refs to the same object. However, it is more common to want to compare (part of) the inner state of objects and if they are equal, then the two different objects should be considered equal. So you end up often defining == in your classes, like this, right? class Mine attr_accessor :foo, :bar, :moo # @foo, @bar, and @moo are my # important inner state def ==(obj) @foo = obj.foo && @bar = obj.bar && @moo = obj.moo end end Nowadays I just do this: class Mine attr_equality :foo, :moo, :bar ... end Another option is to leave out the names of objects to make it even simpler. This requires the equality function to introspect self and the compared object to find all their instance vars. class Mine attr_equality ... end I won't post the implementation of attr_equality because it's longish, to support also the second option but you get the idea from the attr_initializer example. I'll make an RCR for this and we'll see if people like it. If you want a full implementation of both shortcuts with some tests to go with them Drop me an email directly. Don't reply to the list by mistake... (Yes, XP-enthusiasts, I wrote the tests first! Great isn't it? Although I updated them to run with Lapidary later) Have fun /Gunnar --- Gunnar Andersson gand -at- acm.org __________________________________________________ Do You Yahoo!? Make a great connection at Yahoo! Personals. http://personals.yahoo.com