Partial append_features?

Discussion in 'Ruby' started by Ola Bini, Aug 28, 2006.

  1. Ola Bini

    Ola Bini Guest

    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, Aug 28, 2006
    #1
    1. Advertising

  2. Ola Bini

    Guest

    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.
     
    , Aug 28, 2006
    #2
    1. Advertising

  3. 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/.
     
    Eero Saynatkari, Aug 28, 2006
    #3
  4. > 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
     
    Matthew Johnson, Aug 28, 2006
    #4
  5. 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)
     
    Logan Capaldo, Aug 28, 2006
    #5
  6. Robert Dober wrote:
    > On 8/28/06, Logan Capaldo <> 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 :(
    > 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




    --
    Posted via http://www.ruby-forum.com/.
     
    Eero Saynatkari, Aug 29, 2006
    #6
  7. Ola Bini

    Guest

    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
     
    , Aug 29, 2006
    #7
  8. Ola Bini

    Trans Guest

    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.
     
    Trans, Aug 29, 2006
    #8
  9. Ola Bini

    Ola Bini Guest

    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.
     
    Ola Bini, Aug 29, 2006
    #9
  10. 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 :(
    > > 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.


    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.

    --
    Mauricio Fernandez - http://eigenclass.org - singular Ruby
     
    Mauricio Fernandez, Aug 29, 2006
    #10
  11. 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.


    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

    --
    Mauricio Fernandez - http://eigenclass.org - singular Ruby
     
    Mauricio Fernandez, Aug 29, 2006
    #11
  12. On Tue, Aug 29, 2006 at 05:36:39PM +0900, Robert Dober wrote:
    > On 8/29/06, Mauricio Fernandez <> 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).

    --
    Mauricio Fernandez - http://eigenclass.org - singular Ruby
     
    Mauricio Fernandez, Aug 29, 2006
    #12
  13. Ola Bini

    Trans Guest

    Mauricio Fernandez wrote:
    >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:


    If you don't include the module but keep the inherited methods you have
    a sort of out-of-sync situation. That doesn't make much sense.

    And is there a reason you use clone vs. dup?

    Thanks,
    T.


    PS. Maybe I can improve Facets via this discussion. From Facets' I
    already have:

    class Module

    # Returns an anonymous module with only the specified methods
    # of the receiving module intact.
    def clone_using( *meths )
    meths = meths.collect { |m| m.to_s }
    methods_to_remove = (self.instance_methods - meths)
    mod = self.dup
    mod.class_eval { methods_to_remove.each { |m| undef_method m } }
    return mod
    end

    end

    T.
     
    Trans, Aug 29, 2006
    #13
  14. 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 <> 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).
    >


    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.

    > --
    > Mauricio Fernandez - http://eigenclass.org - singular Ruby
    >
     
    Logan Capaldo, Aug 29, 2006
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ferenc Engard

    problem with Module#append_features

    Ferenc Engard, Sep 8, 2003, in forum: Ruby
    Replies:
    22
    Views:
    306
    Ferenc Engard
    Sep 15, 2003
  2. T. Onoma
    Replies:
    9
    Views:
    395
    Dave Thomas
    Dec 15, 2003
  3. Trans

    #append_features deprecated?

    Trans, Dec 3, 2005, in forum: Ruby
    Replies:
    9
    Views:
    100
    Daniel Schierbeck
    Dec 4, 2005
  4. Brian Mitchell
    Replies:
    4
    Views:
    85
    Yukihiro Matsumoto
    Oct 6, 2006
  5. Rob Sanheim

    append_features vs include

    Rob Sanheim, Oct 6, 2006, in forum: Ruby
    Replies:
    3
    Views:
    655
    Yukihiro Matsumoto
    Oct 6, 2006
Loading...

Share This Page