"Nathaniel Talbott" <nathaniel@talbott.ws> schrieb im Newsbeitrag news:2819AA48-1223-11D9-95A4-000A95CD7A8E@talbott.ws...
Me too... I haven't found a place to use it since I read about it two minutes ago, but it sends a tingle down my spine just thinking about it. It is definitely polymorphic Ruby goodness. Maybe we can convince Nowake to submit an RCR?
Possible uses are simple adaptors that adapt just one method like these:
1. Condition
ODD = MethodProc(:===) {|x| x % 2 != 0}
....
case n
when ODD
...
when ...
end
2. Fake Classes
Consider an object pool that needs a class instance as factory (i.e. to create new instances). We can use a MethodProc instead:
class Pool
def initialize(cl)
@cl = cl
@pool =
end
def get() @pool.shift || @cl.new end
def put(x) @pool << x end
end
pl = Pool.new( MethodProc(:new) { %w{a b c} } )
But the second example shows a more general pattern: what we need here is an Adaptor because we might want to check the type of the object returned via Class#=== to make sure only proper instances go into the Pool. In Ruby we often use singleton methods to do method adptions, but there might be usages where we don't want to change an instance. Here's a sample implementation:
class Adaptor
def initialize(obj, mappings)
@obj = obj
scl = class<<self; self end
# delegation of all public methods
obj.public_methods.each do |m|
m = m.to_sym
unless mappings[m]
scl.class_eval { define_method(m) { |*a| @obj.send(m,*a) } }
end
end
# remapping
mappings.each do |m,mapped|
case mapped
when Symbol
scl.class_eval { define_method(m) {|*a| @obj.send(mapped,*a) } }
when Proc
scl.class_eval { define_method(m,&mapped) }
else
raise ArgumentError, "Must be Proc or Symbol"
end
end
end
end
With this we can do
sample = %w{aa bb cc}
=> ["aa", "bb", "cc"]
fake_class = Adaptor.new(sample, :new => :dup, :=== => :==)
=> ["aa", "bb", "cc"]
x = fake_class.new
=> ["aa", "bb", "cc"]
"Is an instance of? #{fake_class === x}"
=> "Is an instance of? true"
x.id == sample.id
=> false
Now we can modify Pool#put to a more appropriate implementation:
class Pool
def put(x)
raise ArgumentError, "illegal type" unless @cl === x
@pool << x
end
end
pl = Pool.new fake_class
=> #<Pool:0x101b65d8 @cl=["aa", "bb", "cc"], @pool=>
pl.get
=> ["aa", "bb", "cc"]
pl.put( pl.get )
=> [["aa", "bb", "cc"]]
pl.put( "foo" )
ArgumentError: illegal type
from (irb):55:in `put'
from (irb):62
from (null):0
pl.put( pl.get << "x" )
ArgumentError: illegal type
from (irb):55:in `put'
from (irb):63
from (null):0
I'm not sure though whether there are many applications of this pattern in Ruby because we have per instance method definitions with singleton classes. What do others think?
Kind regards
robert