Partial append_features?

Hi,

I’m looking for a way to copy methods from a Module, or specify more
directly which methods get included in a class. More or less, I would
like to be able to do something like this:

module Foo
def do_one_thing
end
def do_second
end
def do_third
end
end

class Bar
append_from Foo, :do_second, :do_third
end

or
module Baz
append_from Foo, :do_second, :do_third
end

and I would have a module Baz which could be included, without having
do_one_thing included.

Is this possible in Ruby right now? My first approach was to get the
UnboundMethod instance_method from the Module, but I couldn’t find a way
to attach these to an unrelated class since UnboundMethod must have a
is_a?-relationship with the binding object.

Regards

Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

Hmmmm… maybe you could do some of this with Forwardable 1, at least
for class instantiations. This isn’t a perfect fit, since you do need
to specify the delegate object, though.

Ola B. wrote:

Hi,

I’m looking for a way to copy methods from a Module, or specify more
directly which methods get included in a class. More or less, I would
like to be able to do something like this:

module Baz
append_from Foo, :do_second, :do_third
end

and I would have a module Baz which could be included, without having
do_one_thing included.

Is this possible in Ruby right now? My first approach was to get the
UnboundMethod instance_method from the Module, but I couldn’t find a way
to attach these to an unrelated class since UnboundMethod must have a
is_a?-relationship with the binding object.

I think the ‘proper’ solution is to chop your module into
smaller pieces. Alternatively, Method#to_proc, maybe?

Regards

On Aug 28, 2006, at 3:00 PM, Ola B. wrote:

end
append_from Foo, :do_second, :do_third
Regards

Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

% cat Projects/Ruby Experiments/append_from.rb
class Module
def append_from( mod, *methods_to_keep )
methods_to_keep.map! { |m| m.to_s }
methods_to_remove = mod.instance_methods(false) - methods_to_keep
new_mod = Module.new
new_mod.module_eval do
include mod
methods_to_remove.each { |meth| undef_method meth }
end
include new_mod
end
end

module M
def a
puts “a”
end

def b
puts “b”
end
end

class A
append_from M, :b
end

a = A.new
a.b
a.a

% ruby Projects/Ruby Experiments/append_from.rb
b
-:40: undefined method `a’ for #<A:0x1e89b4> (NoMethodError)

On 8/28/06, Logan C. [email protected] wrote:

module Foo
end
the UnboundMethod instance_method from the Module, but I couldn’t
“Yields falsehood when quined” yields falsehood when quined.
include mod

a.b
a.a

% ruby Projects/Ruby Experiments/append_from.rb
b
-:40: undefined method `a’ for #<A:0x1e89b4> (NoMethodError)

unfortunately that approach undefines eralier defined methods in the
appendee, especially inherited ones :frowning:
I guess that could be fixed but the code will become rather clumsy.

Cheers
Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

Robert D. wrote:

On 8/28/06, Logan C. [email protected] wrote:

module Foo
end
the UnboundMethod instance_method from the Module, but I couldn’t
“Yields falsehood when quined” yields falsehood when quined.
include mod

a.b
a.a

% ruby Projects/Ruby Experiments/append_from.rb
b
-:40: undefined method `a’ for #<A:0x1e89b4> (NoMethodError)

unfortunately that approach undefines eralier defined methods in the
appendee, especially inherited ones :frowning:
I guess that could be fixed but the code will become rather clumsy.

Heh, take another look:

 new_mod = Module.new     # Only this anonymous is affected
 new_mod.module_eval do
   include mod
   methods_to_remove.each { |meth| undef_method meth }
 end

A rather nice solution.

Cheers
Robert

end

and I would have a module Baz which could be included, without
having do_one_thing included.

Is this possible in Ruby right now? My first approach was to get
the UnboundMethod instance_method from the Module, but I couldn’t
find a way to attach these to an unrelated class since
UnboundMethod must have a is_a?-relationship with the binding object.

The closest I have seen to this is generating a module on the fly
(with Module.new) that includes the Module and then undefs all the
methods you don’t want. That gives you a custom module to include
that will have only the methods you are after.

Matthew

Hi,

At Tue, 29 Aug 2006 04:00:39 +0900,
Ola B. wrote in [ruby-talk:211191]:

and I would have a module Baz which could be included, without having
do_one_thing included.

http://www.rubyist.net/~nobu/ruby/aliasing.rb may help you.

require ‘aliasing’

module Foo
def do_one_thing
“one_thing”
end
def do_second
“second”
end
def do_third
“third”
end
end

class Bar
include Foo.only_aliasing(:do_second, :do_third)
end

p Bar.instance_methods.grep(/^do/)
bar = Bar.new
p bar.do_second
p bar.do_third
p (begin bar.do_one_thing; rescue NoMethodError => e; e; end)

Ola B. wrote:

end
append_from Foo, :do_second, :do_third
end

and I would have a module Baz which could be included, without having
do_one_thing included.

Is this possible in Ruby right now? My first approach was to get the
UnboundMethod instance_method from the Module, but I couldn’t find a way
to attach these to an unrelated class since UnboundMethod must have a
is_a?-relationship with the binding object.

Try Facets’ module/integrate.rb Here’s the doc:

Using integrate is just like using include except the

module included is a reconstruction of the one given

altered via commands in the block.

Convenient commands available are: #rename, #redef,

#remove, #nodef and #wrap. But any module method

can be used.

module W

def q ; “q” ; end

def y ; “y” ; end

end

class X

integrate W do

nodef :y

end

end

x = X.new

x.q #=> “q”

x.y #=> missing method error

This is like #revisal, but #revisal only

returns the reconstructred module. It does not

include it.

http://facets.rubyforge.org

T.

On Tue, Aug 29, 2006 at 08:15:47AM +0900, Eero S. wrote:

   methods_to_remove.each { |meth| undef_method meth }
 end

A rather nice solution.

I think this is what he meant:

RUBY_VERSION # => “1.8.5”
class X; def foo; “X#foo” end end
class Y < X; end
module M; def foo; “M#foo” end end
N = Module.new
N.module_eval do
include M
undef_method :foo
end

y = Y.new
y.foo # => “X#foo”
class Y; include N end
y.foo # =>

~> -:14: undefined method `foo’ for #<Y:0xa7e04000> (NoMethodError)

IMO it’s better to have the last call to #foo return X#foo.
You can do that by cloning the module then using remove_method on the
copy.

Logan C. wrote:

end
include new_mod

end
end

Hi,

Thank you for writing it up for me. This was more or less what I had in
mind of writing up myself. This solution is definitely the best for me,
since the methods I want to keep is a small subset compared to how many
to remove, and the ones to remove will grow with time.

Thanks.


Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

On Tue, Aug 29, 2006 at 03:26:58PM +0900, Ola B. wrote:

end
include new_mod
end
end

Thank you for writing it up for me. This was more or less what I had in
mind of writing up myself. This solution is definitely the best for me,
since the methods I want to keep is a small subset compared to how many
to remove, and the ones to remove will grow with time.

The above code effectively removes inherited methods too (as well as
those
from other modules that were included previously):

class Module
def append_from( mod, *methods_to_keep )
methods_to_keep.map! { |m| m.to_s }
methods_to_remove = mod.instance_methods(false) - methods_to_keep
new_mod = Module.new
new_mod.module_eval do
include mod
methods_to_remove.each { |meth| undef_method meth }
end
include new_mod
end
end

module A; def foo; “A#foo” end end
module B
def foo; “B#foo” end
def bar; “B#bar” end
end
class X; include A end
x = X.new
x.foo # => “A#foo”
class X; append_from B, :bar end
x.bar # => “B#bar”
x.foo # =>

~> -:24: undefined method `foo’ for #<X:0xa7d72a88> (NoMethodError)

In this example, #undef_method has blocked A#foo too.

Here’s another way to do it without clobbering inherited methods; the
key difference is that the original module will not be added to the
inheritance chain:

RUBY_VERSION # => “1.8.5”
RUBY_RELEASE_DATE # => “2006-08-25”
class Module
def append_from(mod, *methods)
methods.map!{|x| x.to_s}
m = mod.clone
m.module_eval do
(instance_methods(false) - methods).each{|x| remove_method x }
end
include m
end
end

module A; def foo; “A#foo” end end
module B
def foo; “B#foo” end
def bar; “B#bar” end
end
class X; include A end
x = X.new
x.foo # => “A#foo”
class X; append_from B, :bar end
x.bar # => “B#bar”
x.foo # => “A#foo”
X.ancestors # => [X, #Module:0xa7d94214, A, Object,
Kernel]
====================
B(’) is missing in
the inheritance chain

On 8/29/06, Mauricio F. [email protected] wrote:

 include mod

to remove, and the ones to remove will grow with time.
include mod
end

Here’s another way to do it without clobbering inherited methods; the
key difference is that the original module will not be added to the
inheritance chain:

Brilliant, the is_a? relation should not hold anyway after a partial
include, that was kind of where I blocked.

RUBY_VERSION # => “1.8.5”

end
x.bar # => “B#bar”

That is great work, thank you, well thank you for showing us, I think
the
feature itself is not desireable, but it is desireable to know how to do
it
:wink:

Cheers
Robert

Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

On Tue, Aug 29, 2006 at 05:36:39PM +0900, Robert D. wrote:

On 8/29/06, Mauricio F. [email protected] wrote:

The above code effectively removes inherited methods too (as well as those
from other modules that were included previously):
[…]
Here’s another way to do it without clobbering inherited methods; the
key difference is that the original module will not be added to the
inheritance chain:
[…]

Brilliant, the is_a? relation should not hold anyway after a partial
include, that was kind of where I blocked.

Right, since is_a? doesn’t hold anymore, you cannot add methods to the
module
either, e.g.

module B; def foo; “B#foo” end end
class X; append_from B, :foo end
module B; def bar; “B#bar” end end

X.new.bar # ====> would raise a NoMethodError

… and you cannot capture new methods in B.method_added since Ruby
won’t let
you rebind them. But that’s OK, since we wanted only the methods
explicitly
passed to #append_from to be imported. In fact, you’d have to add some
code
(similar to BlankSlate’s) to the other solution (the one with a “child
module”
and #undef_method) to handle this correctly (and it’d still suffer from
the
method shadowing/clobbering problem).

On Aug 29, 2006, at 4:57 AM, Mauricio F. wrote:

[…]
module B; def bar; “B#bar” end end
“child module”
and #undef_method) to handle this correctly (and it’d still suffer
from the
method shadowing/clobbering problem).

Yeah I knew it would clobber the old methods, but I couldn’t think of
a way to do it w/o clobbering them. Silly me not thinking of clone.
(My original attempt want to use UboundMethod#to_proc + define_method
(IMO being closest to the desired behavior), unfortunately there is
no such thing as UnboundMethod#to_proc.

On 8/29/06, Eero S. [email protected] wrote:

Robert D. wrote:

On 8/28/06, Logan C. [email protected] wrote:

unfortunately that approach undefines eralier defined methods in the

appendee, especially inherited ones :frowning:
I guess that could be fixed but the code will become rather clumsy.

Heh, take another look:

That is needless, I do not understand the code :wink:

Ruby does however:
508/31 > cat append.rb && ./append.rb
#!/usr/local/bin/ruby -w

class Module
def append_from( mod, *methods_to_keep )
methods_to_keep.map! { |m| m.to_s }
methods_to_remove = mod.instance_methods(false) - methods_to_keep
new_mod = Module.new
new_mod.module_eval do
include mod
methods_to_remove.each { |meth| undef_method meth }
end
include new_mod
end
end

class Base
def c
puts “inherited c”
end
end

module M
def a
puts “a”
end
def c
puts “c”
end
end

class A < Base
append_from M, :a
end

a = A.new
a.a
a.c
a
./append.rb:38: undefined method `c’ for #<A:0xb7d4a174> (NoMethodError)

 new_mod = Module.new     # Only this anonymous is affected
 new_mod.module_eval do
   include mod  # The appender (not appendee, sorry I wrote this at

1:00)

is the receiver

   methods_to_remove.each { |meth| undef_method meth }

and thus undef_method also undefines the method in the receiver
that should be clear from the design of the solution.
I cannot fix it but I guess Logan could

 end

A rather nice solution.

Well I like Logan’s posts a lot and this too, it is interesting code,
but
sorry if I fail to agree
it is not a solution.

Cheers
Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein