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 Bini (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.

[1]: http://www.ruby-doc.org/stdlib/libdoc/forwardable/rdoc/index.html

Ola Bini 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 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 Bini (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.

Ola Bini 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:

<snip />

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

--
Posted via http://www.ruby-forum.com/.

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.

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

% 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 Aug 28, 2006, at 3:00 PM, Ola Bini 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 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 Bini (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.

Hi,

At Tue, 29 Aug 2006 04:00:39 +0900,
Ola Bini 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)

···

--
Nobu Nakada

Ola Bini 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 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.

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.

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

···

On 8/28/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

On Aug 28, 2006, at 3:00 PM, Ola Bini 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 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 Bini (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)

--
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

Logan Capaldo wrote:

% 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

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 Bini (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.

Robert Dober 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.

···

On 8/28/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

Cheers
Robert

--
Posted via http://www.ruby-forum.com/.

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]

···

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

Logan Capaldo wrote:
>% 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
>

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.

                                       ====================
                                       B(') is missing in
                                       the inheritance chain

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Robert Dober wrote:
<original code snipped>

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

···

On 8/29/06, Eero Saynatkari <eero.saynatkari@kolumbus.fi> wrote:

> On 8/28/06, Logan Capaldo <logancapaldo@gmail.com> wrote:

--
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

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.

···

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

Robert Dober 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:

     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.

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

> Logan Capaldo wrote:
> >% 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
> >
>
> 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:

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"

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

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

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

···

On 8/29/06, Mauricio Fernandez <mfp@acm.org> wrote:

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

--
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

>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 Tue, Aug 29, 2006 at 05:36:39PM +0900, Robert Dober wrote:

On 8/29/06, Mauricio Fernandez <mfp@acm.org> wrote:

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

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).

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 Aug 29, 2006, at 4:57 AM, Mauricio Fernandez wrote:

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

On 8/29/06, Mauricio Fernandez <mfp@acm.org> wrote:

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby