Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > delegation question, where I want prototype style delegation

Reply
Thread Tools

delegation question, where I want prototype style delegation

 
 
Sam Roberts
Guest
Posts: n/a
 
      05-06-2008
I want to make an object that behaves like another object would, if that
object had had it's #each method redefined.

I can do this with extend(), but that permanently damages the object.

I can do it with delegate/method_missing, except I need to reimplement
every method of the delegatee, which sucks.

I could do this in prototype based languages, where you can effectively
create a new object that behaves like another object with a few differences,
but don't see a way in an OO language like ruby.

Below is an example of what I want to do, implemented with extend.

But it has a problem, it modifies the target object, but I may want
to create mutiple delegates to the
target, each with a different set of "views", that behave like the
target object would if it
had its #each method redefined.

Btw, what I'm actually doing is I have a calendar object, and I want
to create various views
of the calendar, ones including events but not todos, ones that appear
to only have
components that occur in a particular period, etc.

class Base
include Enumerable

def initialize(ary)
@ary = ary.to_a
end

def each
@ary.each{|a| yield a}
end

def show
inject("show: ") {|accum, o| accum + o.to_s + ","}
end
end

module Negate
def each(&block)
super do |a|
yield -a
end
end
end

module Add10
def each(&block)
super do |a|
yield a+10
end
end
end

o = Base.new(1..3)
o.extend Negate.dup
o.extend Add10
o.extend Negate.dup

o.each {|_| puts _ }

puts o.show

 
Reply With Quote
 
 
 
 
James Gray
Guest
Posts: n/a
 
      05-06-2008
On May 6, 2008, at 1:00 AM, Sam Roberts wrote:

> I want to make an object that behaves like another object would, if
> that
> object had had it's #each method redefined.


What you really want in this case is normal inheritance, so the
overridden each() method replaces the original.

Then the only thick becomes getting the existing object into an
equivalent subclass form. We can use a little Ruby magic to track
subclasses and do the conversion for us:

class Base
def self.subclasses
@subclasses ||= [ ]
end

def self.inherited(subclass)
subclasses << subclass
end

def self.subclass(snake_case_name)
camel_case_name = snake_case_name.gsub(/(?:\A|_)(.)/)
{ $1.capitalize }
subclasses.find { |sc| sc.to_s == camel_case_name }
end

include Enumerable

def initialize(ary)
@ary = Array(ary)
end

def each(&block)
@ary.each(&block)
end

def show
"show: #{to_a.join(", ")}"
end

def method_missing(method, *args, &block)
if method.to_s =~ /\Aas_(\w+)\z/ and (sc = self.class.subclass($1))
sc.new(@ary)
else
super
end
end
end

class Negated < Base
def each
super { |e| yield -e }
end
end

class Plus10 < Base
def each
super { |e| yield e + 10 }
end
end

puts "Base:"
b = Base.new(1..3)
puts b.show

puts "Negated:"
puts b.as_negated.show

puts "Plus 10:"
puts b.as_plus_10.show

__END__

Hope that helps.

James Edward Gray II

 
Reply With Quote
 
 
 
 
ara.t.howard
Guest
Posts: n/a
 
      05-06-2008

On May 6, 2008, at 12:00 AM, Sam Roberts wrote:
> But it has a problem, it modifies the target object, but I may want
> to create mutiple delegates to the
> target, each with a different set of "views", that behave like the
> target object would if it
> had its #each method redefined.


you can play with this:


cfp:~ > cat a.rb
class Base
instance_methods.each do |m|
unless m[%r/^__/]
old = m
new = "__#{ old }__"
new << '?' if new.sub!('?', '')
new << '!' if new.sub!('!', '')
alias_method new, old
undef_method old
end
end

def initialize object
@object = object
end

def method_missing m, *a, &b
@object.__send__ m, *a, &b
end

Delegates = {}

def / m
Delegates[m] ||=
__dup__.__instance_eval__ do
extend m
self
end
end
end

module Reverse
def each *a, &b
reverse.each *a, &b
end
end

a = Base.new [2,4]

p a.each{}

p (a/Reverse).each{}



cfp:~ > ruby a.rb
[2, 4]
[4, 2]




a @ http://codeforpeople.com/
--
we can deny everything, except that we have the possibility of being
better. simply reflect on that.
h.h. the 14th dalai lama




 
Reply With Quote
 
Sam Roberts
Guest
Posts: n/a
 
      05-07-2008
On Tue, May 6, 2008 at 5:58 AM, James Gray <(E-Mail Removed)> wrote:
> On May 6, 2008, at 1:00 AM, Sam Roberts wrote:
>
>
> > I want to make an object that behaves like another object would, if that
> > object had had it's #each method redefined.
> >

>
> What you really want in this case is normal inheritance, so the overridden
> each() method replaces the original.


Educational code, but doesn't quite do the trick, because the views
aren't stackable.

In my example I negated, added 10, and negated again. With yours:

puts "Stacked operations:"
puts b.as_negated.as_plus_10.as_negated.show

=> in `method_missing': undefined method `as_plus_10' for
#<Negated:0x261ec @ary=[1, 2, 3]> (NoMethodError)

The end result of this would have to be an instance of

class Negated < Plus10 < Negated < Base
end

or something...

Also, my real class has much more complex internal state and
relationships, it isn't just
a wrapper for an Array, and creating a new instance isn't desireable.
I'm trying to create
calendar views, where the view of the calendar looks like the real
calendar (including
reflecting changes in the base calendar), but iterates over only dates
in a particular range,
or only journal entries.

Thanks,
Sam

 
Reply With Quote
 
Sam Roberts
Guest
Posts: n/a
 
      05-07-2008
On Tue, May 6, 2008 at 7:25 AM, ara.t.howard <(E-Mail Removed)> wrote:

Ara, thanks for the suggestion. It suffers from the same
non-stackability problem:

p ((a/Reverse)/Reverse).each{}
=> [4,2]

However, the combination of this and James' suggestion made me think
maybe I should be cloning my src object, and then modifying it.
clone() will get the singleton class, but won't do a deep copy, so it
will still share most of the internal state of the original class. I
think this will work for me:

Classes defined as in my original, but do the extending like:

o = Base.new(1..3)
n = o.clone.extend Negate.dup
an = n.clone.extend Add.dup
nan = an.clone.extend Negate.dup

puts o.show
puts n.show
puts an.show
puts nan.show


% ruby m.rb
show: 1,2,3,
show: -1,-2,-3,
show: 9,8,7,
show: -9,-8,-7,

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Class prototype vs C function prototype June Lee C++ 2 04-13-2008 08:17 PM
prototype __proto__ super and delegation cbare Javascript 1 11-01-2007 06:28 PM
prototype __proto__ super and delegation cbare Javascript 8 10-31-2007 05:01 PM
Prototype Object.extend(new Base() | Hash | Hash.prototype) usage: jacobstr@gmail.com Javascript 3 03-27-2007 07:56 AM
relation between prototype and Prototype.js shypen42@yahoo.fr Javascript 9 05-26-2006 01:13 AM



Advertisments