From: Thorsten Hater Date: 2009-07-14T23:04:02+09:00 Subject: Re: Prototype-Based Inheritance (#214) Hi, a simple Solution, which works for static Prototypes: module Prototype def method_missing name, *args # if we have a prototype forward to it's methods # else propagate method missing upwards if defined?(@__proto__) && @__proto__ @__proto__.send name,*args else super end end def set_prototype pt # clone the prototype, so we can freely modify # it's attributes w/o interference with others @__proto__ = pt.clone end end As said above this is a simple solution, but it should work for all classes and is easily written. But it comes at the cost of duplicating arbitrarily complex objects (perhaps many times) and just allows static prototypes. I couldn't come up with a solution which filters writing access to the proto- type (apart from simple regexing) but allows modification from the parentside. Possibly one could use observers and method_defined hooks for this, but the complexity would be rather high on this way. Best regards, Thorsten David Masover wrote: > On Sunday 12 July 2009 12:49:55 pm lith wrote: > > >> def prototype=(prototype) >> merge!(prototype) {|k,o,n| o} >> > > I based my implementation on Javascript, and one of the nice properties there > is that the prototype can change later. So, for example, in my implementation: > > irb(main):005:0> pele.favorite_food = 'hamburger' > => "hamburger" > irb(main):006:0> pele_jr.favorite_food > => "hamburger" > irb(main):007:0> pele > => {:name=>"Pele", :sport=>"football", :position=>"Forward", > :favorite_food=>"hamburger"} > irb(main):008:0> pele_jr.favorite_food = 'cheeseburger' > => "cheeseburger" > irb(main):009:0> pele_jr.favorite_food > => "cheeseburger" > irb(main):010:0> pele.favorite_food > => "hamburger" > > Of course, this is slower, but more powerful, I think. > > >> when Proc >> return val.call(self, *args) >> end >> > [snip] > >> This would allow you to do something like: >> >> pele_jr.age = 20 >> pele_jr.foo = lambda {|this, a| this.age + a} >> pele_jr.foo(10) #=> 30 >> > > Nice touch... > > Here's mine: > > module Prototype > attr_accessor :prototype > def method_missing *args > prototype.public_send *args > end > end > > That's more generic than just hashes. Mix it into Object and it works on > anything. Of course, it's not exactly convenient to have to define new values > like this: > > pele = Object.new > class << pele > attr_accessor :name > end > pele.name = 'Pele' > > It also severely limits what you can do with some of the builtin classes like > Fixnums that can't have a metaclass. > > And of course, to make it saner for the example (and because OpenStruct won't > cooperate), I can do this: > > class Hash > include Prototype > def method_missing name, *args > if has_key? name > self[name] > elsif name =~ /.=$/ > self[name.to_s.chomp('=').to_sym] = args.first > else > super > end > end > end > > -- Thorsten Hater Institut fuer Theoretische Physik I Ruhr-Universitaet Bochum E-Mail: tp1@tp1.rub.de Tel.: 0234/32-23-441