From: Ken Bloom Date: 2008-05-02T12:10:03+09:00 Subject: Re: DSL - class-level methods that affect instances On Thu, 01 May 2008 19:55:45 -0500, Daniel Waite wrote: > Ken Bloom wrote: >> On Thu, 01 May 2008 18:07:11 -0500, Daniel Waite wrote: >> >>> class C >>> >>> I have no idea how to break the barrier between class and instance. :( >> >> Use class variables (the @@ variety). >> >> class Translator::Base >> def self.translate word, params >> @@translations ||= {} >> @@translations[word]=params[:to] >> end >> def translations >> @@translations ||= {} >> end >> end >> >> class C < Translator::Base >> translate 'a', :to => 'b' >> translate 'c', :to => 'd' >> end >> >> C.new.translations #=> {"a"=>"b", "c"=>"d"} > > That works! Except it shares translations across all subclasses. > > module Translator > class Base > > def self.translate word, params > @@translations ||= {} > @@translations[word]=params[:to] > end > > def translations > @@translations ||= {} > end > > end > end > > class C < Translator::Base > > translate 'a', :to => 'b' > translate 'c', :to => 'd' > > end > > class D < Translator::Base > > translate 'this', :to => 'that' > > end > > irb(main):028:0> c = C.new > => # > irb(main):029:0> c.translations > => {"a"=>"b", "c"=>"d", "this"=>"that"} > > While I understand the behavior, its undesirable. I'm going to dust off > Ruby for Rails... I couldn't remember why I didn't use @@ variables for another very similar metaprogramming problem I had this week. Now you've reminded why. This version uses instance variables on the singleton class (which is different from using @@ class variables) class Translator::Base def self.translate word, params @translations ||= {} @translations[word]=params[:to] end def translations self.class.instance_variable_get(:@translations) || {} end end class C < Translator::Base translate 'a', :to => 'b' translate 'c', :to => 'd' end C.new.translations #=> {"a"=>"b", "c"=>"d"} class D < Translator::Base translate 'e', :to => 'f' end D.new.translations #=> {"e"=>"f"} C.new.translations #=> {"a"=>"b", "c"=>"d"} #But beware: class E < C translate 'g', :to => 'h' end E.new.translations #=> {"g"=>"h"} There are ways to solve this by explicitly asking for your parent class's translations, if you'd like to have that functionality. --Ken -- Ken (Chanoch) Bloom. PhD candidate. Linguistic Cognition Laboratory. Department of Computer Science. Illinois Institute of Technology. http://www.iit.edu/~kbloom1/