After some tinkering, I came up with this working, but ugly and definitely
improveable code to do it:
class Module
def use(mod, *methods)
c = Class.new
# Make a singleton that mixes everything in, so that we can cherrypick.
eval("class << c; include #{mod}; end")
methods.each do |m|
# There has to be a more elegant way to do this.
m_sym = m.to_sym
mc_sym = m.to_s.upcase.to_sym
um = c.method(m_sym)
self.const_set(mc_sym,um)
self.class_eval("def #{m.to_s}; #{mc_sym.to_s}.call; end")
end
end
end
module Foo
def a; 'abc'; end
def z; 'xyz'; end
end
class Bing
use(Foo, :a, 'z')
end
x = Bing.new
p x.a
p x.z
There has to be a more elegant way to do this. What is it?
Kirk Haines
I'm afraid that is not doing what you expect it too. Try:
def a; p self; end
and you will see that self is not a Bing.
There is no _simple_ solution to this. Ruby is picky about where its
methods come from and where they can thus go to. Try playing with
#method, Class#instance_method and #bind and you'll see what I mean.
BUT I have done what you ask, as I have/am considering using something
like this for Ruby Facets. Off the top of my head (so forgive any
little bugs) like:
class Module
METHINDEX = {}
def index_def( msym, &mdef )
METHINDEX[[self,:"#{msym}"]] = mdef
end
def use(mod, *methods)
methods.each { |msym| self.class_eval
&METHINDEX[[mod,:"#{msym}"]] }
end
end
module Foo
index_def( :a ) {
def a; p self ; end
}
index_def( :z ) {
def z; p "z" ; end
}
end
class Bing
use Foo, :a, 'z'
end
The block is needed to protect the method defintion from binding to an
unusable location.
Am interested in other people's solutions too.
T.
<snip>
>> There has to be a more elegant way to do this. What is it?
>>
>
<snip>
> Am interested in other people's solutions too.
harp:~ > cat a.rb
class Module
def import desc
desc.each do |a_module, a_list|
m = Module::new
m.module_eval do
include a_module
a = a_module.instance_methods
b = [ a_list ].flatten.map{|b| b.to_s}
(a - b).each{|um| undef_method um}
end
module_eval{ include m }
end
end
end
module Foo
def foo
p self
p 42
end
def foobar
p self
p @foobar
end
end
module Bar
def bar
p self
p 42.0
end
def barfoo
p self
p @barfoo
end
def not_imported
end
end
class C
import Foo => %w( foo foobar ),
Bar => %w( bar barfoo )
def initialize
@foobar = 'foobar'
@barfoo = 'barfoo'
end
end
c = C::new
c.foo
c.bar
c.foobar
c.barfoo
c.not_imported
harp:~ > ruby a.rb
#<C:0xb75d060c @barfoo="barfoo", @foobar="foobar">
42
#<C:0xb75d060c @barfoo="barfoo", @foobar="foobar">
42.0
#<C:0xb75d060c @barfoo="barfoo", @foobar="foobar">
"foobar"
#<C:0xb75d060c @barfoo="barfoo", @foobar="foobar">
"barfoo"
a.rb:56: undefined method `not_imported' for #<C:0xb75d060c @barfoo="barfoo", @foobar="foobar"> (NoMethodError)
seems to work. i'm probably missing something though...
cheers.
-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
You are clever. I give you that. Building an anonymous module to
include the target module and then undefining the methods _unwanted_...
yes, it should work. But it ain't elegant! :-)
T.
require 'nodewrap'
class Module
def use(mod, *methods)
methods.each do |method|
sym = method.to_sym
body = mod.instance_method(sym).body
add_method(sym, body, 0)
end
end
end
Be careful,
Paul
This doesn't work for me. There appears to be no UnboundMethod#body
method, even after requiring nodewrap.
What's missing?
Regards,
Dan
The latest version of nodewrap is missing. :)
I fixed this a year ago but haven't done a release yet. Get nodewrap
from cvs and you'll get UnboundMethod#body (along with lots of other
cool prizes).
Paul