<<EOF
class C:
def foo(self):
print "foo"
def bar1(self):
print "bar version 1"
def bar2(self, arg):
print "bar version 2, arg =", arg
def facet(self, whichFacet=1):
if whichFacet == 1:
return C_facet1(self)
elif whichFacet == 2:
return C_facet2(self)
return
class C_facet1:
def __init__(self, c):
self.foo = c.foo
self.bar = c.bar1
class C_facet2:
def __init__(self, c):
self.foo = c.foo
self.bar = c.bar2
c1 = C().facet(1) # client1 picks interface1
c1.foo() # prints "foo"
c1.bar() # prints "bar version 1"
c2 = C().facet(2) # client2 picks interface2
c2.foo() # prints "foo"
c2.bar(123) # prints "bar version 2, arg = 123"
EOF
if a client wants interface 1, then she will get the methods foo and bar
(which is actually C#bar1). if he picks interface 2, then she will get
the methods foo and bar (which is actually C#bar2). for all she cares,
she just wants to know/deal with a single [versatile] class, C. and from
that single class, she can pick several sets of features/interfaces she
needs/wants.
i know that, in ruby, modules are usually used for things like this, but
in my case many of the method names are the same so i don't think
modules are approriate for this. for example, interface1 consists of
foo, bar, baz, abc, and abd. interface2 consists of foo, bar, baz, bab,
bac. in most of the cases all of the interfaces provide the same set of
features, just in a different style or different revision.
any pointers of how i might do something like this in ruby?
--
dave
Elegant is often in the eye of the beholder. I have this ...
class C
class << self
def facet(n)
facets = [C1, C2]
facets[n-1].new
end
end
def foo
"foo"
end
end
class C1 < C
def bar
"bar version 1"
end
end
class C2 < C
def bar(n)
"bar version 2, arg = #{n}"
end
end
class TestFacet < Test::Unit::TestCase
def test_foo
c = C.facet(1)
assert_equal "foo", c.foo
assert_equal "bar version 1", c.bar
end
def test_facet2_foo
c = C.facet(2)
assert_equal "foo", c.foo
assert_equal "bar version 2, arg = 123", c.bar(123)
end
end
But if the client knows enough to say "c = C.facet(2)", it seems to me
that it wouldn't be any harder to have the client say "c = C2.new".
Then you can drop the the whole facet method in the Class, and the base
class no longer needs knowledge of its child classes (which is a good
thing).
--
-- Jim Weirich jwei...@one.net http://w3.one.net/~jweirich
---------------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
> if a client wants interface 1, then she will get the methods foo and bar
> (which is actually C#bar1). if he picks interface 2, then she will get
> the methods foo and bar (which is actually C#bar2). for all she cares,
> she just wants to know/deal with a single [versatile] class, C. and from
> that single class, she can pick several sets of features/interfaces she
> needs/wants.
>
> i know that, in ruby, modules are usually used for things like this, but
> in my case many of the method names are the same so i don't think
> modules are approriate for this. for example, interface1 consists of
> foo, bar, baz, abc, and abd. interface2 consists of foo, bar, baz, bab,
> bac. in most of the cases all of the interfaces provide the same set of
> features, just in a different style or different revision.
>
> any pointers of how i might do something like this in ruby?
I know that you're avoiding inheritance on purpose, but I'd do it like
this:
class C
def self.new(facet = 1, *args)
if self == C
case facet
when 1 then C1.new(*args)
when 2 then C2.new(*args)
end
else
super(*args)
end
end
def foo
puts "foo"
end
def bar1
puts "bar version 1"
end
def bar2
puts "bar version 2"
end
end
class C1 < C
alias_method :bar, :bar1
end
class C2 < C
alias_method :bar, :bar2
end
c1 = C.new(1) # cilent1 picks interface as defined in C1
c1.foo # prints "foo"
c1.bar # prints "bar version 1"
c2 = C.new(2) # client2 picks interface as defined in C2
c2.foo # prints "foo"
c2.bar # prints "bar version 2"
You can define a class method in C named .facet if you don't want to
touch .new for this purpose, just rename .new.
def self.facet(facet = 1, *args)
if self == C
case facet
when 1 then C1.new(*args)
when 2 then C2.new(*args)
end
else
super(*args)
end
end
And can easily take away the ugly case statement, too:
def self.facet(facet = 1, *args)
if self == C
Object.const_get("#{self}#{facet}").new(*args)
else
super(*args)
end
end
Finally, you might want to make it generally useful by stuffing it
into a mixin:
module Facetable
def facet(facet = 1, *args)
if self == C
Object.const_get("#{self}#{facet}").new(*args)
else
super(*args)
end
end
end
class C
extend Facetable
...
end
class C1 < C
...
end
class C_something < C
...
end
c1 = C.facet(1)
csth = C.facet("_something")
...and so on.
Massimiliano
On Mon, 20 Jan 2003, David Garamond wrote:
> i want to have a class that can support multiple sets of methods, based
> on what the client requests. so far i have written the following python
> code. it's ugly/not elegant and i haven't managed to come up with a less
> ugly ruby version :-)
[...]
> i know that, in ruby, modules are usually used for things like this, but
> in my case many of the method names are the same so i don't think
> modules are approriate for this. for example, interface1 consists of
> foo, bar, baz, abc, and abd. interface2 consists of foo, bar, baz, bab,
> bac. in most of the cases all of the interfaces provide the same set of
> features, just in a different style or different revision.
>
> any pointers of how i might do something like this in ruby?
I wouldn't dismiss modules for this purpose. For example:
class C
module Facet1
def bar
puts "bar version 1"
end
end
module Facet2
def bar(arg)
puts "bar version 2, arg=#{arg}"
end
end
def foo
print "foo"
end
def facet(f=1)
extend self.class.const_get("Facet" + f.to_s)
end
end
C.new.facet(1).bar # bar version 1
C.new.facet(2).bar("hi") # bar version 1, arg=hi
Mind you, as Jim pointed out, once you have to know about facet(1) and
all that, you might as well bring it up front, which in my version
would mean doing:
c = C.new.extend(C::Facet1)
c.bar # bar version 1
or equivalent. (extend is very cool :-)
David
--
David Alan Black
home: dbl...@candle.superlink.net
work: blac...@shu.edu
Web: http://pirate.shu.edu/~blackdav
I have a module that I will use as a mixin:
module Original
def foo
pust "Original#foo()"
end
end
include Original
foo # => Original#foo()
Everything is OK so far. Then I would like to mix another module into
Original that redefines method foo:
module Another
def foo
puts "Another#foo()"
end
end
module Original
include Another
end
foo # => Original#foo()
Here I have a problem -- Original's foo is still being called (and I expect
Another's). The only way to achieve what I want is to redefine module
Another above to:
module Another
def self.append_features(mod)
super mod
mod.module_eval %Q{
def foo
puts "Another#foo()"
end
}
end
end
Am I missing something? Is there a way to go without eval?
Thanks in advance,
Gennady.
thanks. so as i see it, 'extend' is like 'include' but operates at the
object level (while 'include' usually works at the class level), right?
extend _is_ cool :-)
btw, does 'extend' have a significant runtime cost?
--
dave
D> thanks. so as i see it, 'extend' is like 'include' but operates at the
D> object level (while 'include' usually works at the class level), right?
Well, it's perhaps best if you see that #extend work at singleton level
For example
class A
extend M
end
is *like* if you write
class A
class << self
include M
end
end
a = A.new.extend(M)
is *like*
class << a
include M
end
D> btw, does 'extend' have a significant runtime cost?
no, why ?
Guy Decoux
>>>>>> "D" == David Garamond <davega...@icqmail.com> writes:
D>> thanks. so as i see it, 'extend' is like 'include' but operates at the
D>> object level (while 'include' usually works at the class level), right?
> Well, it's perhaps best if you see that #extend work at singleton level
> For example
> class A
> extend M
> end
Is
A.extend M
a shortcut for this? Practically it seems to be, and theoretically it
should be, since "class A" sets "self" to "A".
So is there a shortcut for
class A
include M
end
?
The logic above appears to apply, but
A.include M
fails because "private method `include' called for A:Class".
Just wondering if I've got everything right, and if there's anything
else to learn about it.
Gavin
You do probably already know it, but for the record, we have
Object#extend (public)
Module#include (private)
The question is why are Module#include and Module#append_features
private?
>
> Just wondering if I've got everything right, and if there's anything
> else to learn about it.
>
> Gavin
>
--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com
People disagree with me. I just ignore them.
-- Linus Torvalds, regarding the use of C++ for the Linux kernel
>> The logic above appears to apply, but
>>
>> A.include M
>>
>> fails because "private method `include' called for A:Class".
> You do probably already know it, but for the record, we have
> Object#extend (public)
> Module#include (private)
> The question is why are Module#include and Module#append_features
> private?
Well, here's some cool code then:
>> module M; def hi; "hi"; end; end
=> nil
>> class A; end
=> nil
>> A.include M
NameError: private method `include' called for A:Class
from (irb):3
>> class Module
>> def inc(*args)
>> include *args
>> end
>> end
=> nil
>> A.inc M
=> A
>> A.new.hi
=> "hi"
Gavin (who's trying to get his Ruby-English ratio up...)
> On Thursday, January 23, 2003, 3:57:28 AM, Mauricio wrote:
>>> The logic above appears to apply, but
>>>
>>> A.include M
>>>
>>> fails because "private method `include' called for A:Class".
>> You do probably already know it, but for the record, we have
>> Object#extend (public)
>> Module#include (private)
>> The question is why are Module#include and Module#append_features
>> private?
> Well, here's some cool code then:
.. and some more:
module M
def hi
"hi"
end
end
class B
end
B.include M # => fails: Class#include is private
class Class
def pub(*a) # Because public is private, too.
public *a
end
end
Class.pub :include
B.include M # => succeeds
B.new.hi # => "hi"
Gavin
PS. I think allowing "A.include M" would be nice. It jars that
"extend" is allowed but not "include". I'm not going to make a big
deal of it though.
Me too :)
batsman@tux-chan:/tmp$ irb --prompt-mode simple
>> Module.instance_eval <<EOF
public :include # why not default?
EOF
=> Module
>> module M; def hi; "hi"; end; end
=> nil
>> class A; end
=> nil
>> A.new.hi
NameError: undefined method `hi' for #<A:0x40272a44>
from (irb):6
>> A.include M
=> A
>> A.new.hi
=> "hi"
--
_ _
| |__ __ _| |_ ___ _ __ ___ __ _ _ __
| '_ \ / _` | __/ __| '_ ` _ \ / _` | '_ \
| |_) | (_| | |_\__ \ | | | | | (_| | | | |
|_.__/ \__,_|\__|___/_| |_| |_|\__,_|_| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com
Never make any mistaeks.
-- Anonymous, in a mail discussion about to a kernel bug report
>> Well, here's some cool code then:
> ... and some more:
.. and finally (I just noticed this in fileutils.rb)
Ever find it annoying when you create a module which is basically
intended to be a namespace/container for a set of functions, and they
don't work because you have to use "public_module_method" or whatever?
Here's another way.
module M
def hi
"hi"
end
end
M.extend M # These
module M # three are
extend M
end
module M # equivalent
extend self
end
M.hi # => "hi"
The code I saw opted for "extend self" after all the methods had been
defined. This had me scratching my head wondering what it meant. A
comment up the top switched the light on. But it could have been
written "extend FileUtils", which would have been a bit clearer, and
it could have been done up the top, with a comment saying all methods
are hearby public module methods.
That's right, it could be up the top. Check this:
>> module M
>> def hi
>> "hi"
>> end
>> extend M
>> def lo
>> "lo"
>> end
>> end
=> nil
>> M.hi
=> "hi"
>> M.lo
=> "lo"
Apparently, using "extend" merely hooks something up behind the
scenes, rather than dumping a lot of information into the target
module. This is not particularly surprising, but it's still nice to
have a language that lets you play around like this and experiment.
Gavin
G> Apparently, using "extend" merely hooks something up behind the
G> scenes, rather than dumping a lot of information into the target
G> module.
Well, if you have
module M end
module N end
class A
include M
extend N
end
internally (if you don't modify what do ruby by default) you'll have
A ===> [M] ===> Object
| |
| |
v v
(A) ==> [N] ===> (Object)
Where (A), (Object) are the singleton classes associated with A, Object
[M], [N] are the proxy classes for M, N
This is the same for a module (rather than class A)
Guy Decoux
I'm just learning Ruby, but what about the really simple:
class C
class Ca
def foo
...
end
end
class Cb
def foo
...
end
end
def foo
@facet.foo
end
def initialize(type)
if(type)
@facet = Va.new
else
@facet = Vb.new
end
end
Completely untested, but that's what I'd do in C++. Granted C::Ca#foo
isn't really a method of C, so it can't access instance variables of C,
but you could simulate this by having C#foo pass self as an arg to
@facet.foo.
Is this somehow fundamentally bad?
Sam
likewise untested:
class C
def C.factory(type)
case type
when :a
return Ca
else
return Cb
end
end
class Ca < self
def foo
puts 'override foo'
end
end
class Cb < self
def foo
puts 'override foo'
end
end
def foo
puts 'default foo'
end
end
c = C.factory(:a).new
--
tom sawyer, aka transami
tran...@transami.net