Forum: Ruby How to call a module method dynamically

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Aad27343b68b90195befc0885351dc63?d=identicon&s=25 Myxz Ptlk (huckyducky)
on 2009-02-21 17:59
I know about object instantiation and the send method, but the methods I
want to call are not in a class.  They live in a module.

module Foo

  module Bar

    def mymethod
      "xyz"
    end

  end

end


I can call it statically, like:

myvar = Foo::Bar::xyz


But how can I call it dynamically.  Basically I have some batch jobs
kicked off by a rake task.  I want to use a single rake task to kick off
any job, so I don't have to have a task for every single job, I can just
pass the job name (i.e. the name of the module method) to my one task
and have it kicked off.

Any help is appreciated!  I'm missing something simple, but I can't find
it on google or in pickaxe.
9b905791cbdbb1af35b65e02c3217e23?d=identicon&s=25 Tom Link (Guest)
on 2009-02-21 19:22
(Received via mailing list)
> I can just pass the job name (i.e. the name of the module method)

Couldn't you also pass a block?

  do_something {Foo::Bar.mymethod}

You can get the method object via instance_method but then you have an
unbound method. Or you could turn that method into a module_function
which you could invoke via MyModule.send(:mymethod). But IMHO that's
weird.
Aad27343b68b90195befc0885351dc63?d=identicon&s=25 Myxz Ptlk (huckyducky)
on 2009-02-21 19:28
Tom Link wrote:
>> I can just pass the job name (i.e. the name of the module method)
>
> Couldn't you also pass a block?
>
>   do_something {Foo::Bar.mymethod}
>
> You can get the method object via instance_method but then you have an
> unbound method. Or you could turn that method into a module_function
> which you could invoke via MyModule.send(:mymethod). But IMHO that's
> weird.

Even weirder is monkey-patching Object with my module so I can just use
"send", which is what I did.
E16e84e861c1815ce11ba7bd851c857d?d=identicon&s=25 lasitha (Guest)
on 2009-02-21 19:37
(Received via mailing list)
On Sat, Feb 21, 2009 at 10:28 PM, Adrian Klingel
<adrian.klingel@illumaware.com> wrote:
>
>  end
>
> end
>
>
> I can call it statically, like:
>
> myvar = Foo::Bar::xyz

I'm going to assume you meant Foo::Bar::mymethod, but even that wouldn't
work.

The way you've defined mymethod above makes it an instance method.
The only way to access a module instance method is to mix the module
into a class/object.  If you need an instance method to double as a
class method, Module#module_function can help:

$: irb
01> module Foo
02>   def mymethod; "xyz"; end
03> end
--> nil
04> Foo::mymethod
NoMethodError: undefined method `mymethod' for Foo:Module
        from (irb):4
        from /usr/local/ruby1.9/bin/irb:12:in `<main>'
05> module Foo
06>   module_function :mymethod
07> end
--> Foo
08> Foo::mymethod
--> "xyz"

Having said all that, you haven't indicated any reason for these be
instance methods.  If they aren't intended to be mixed into anything
and the Module is essentially just a namespace in which to define your
methods, they might as well be class methods.

If so, then Object#send will work just fine:

09> Foo.send(:mymethod)
--> "xyz"


> I want to use a single rake task to kick off
> any job, so I don't have to have a task for every single job

Reconsider.  One of the nice things about rake tasks is they double as
documentation.  Being able to run 'rake -T' and see a listing of
available commands is really useful, and more durable than having to
remember (or document elsewhere) the names of the available methods.
And since rake tasks are created dynamically, some simple reflection
and loop will ensure the tasks always match the methods available:

namespace :command
  Foo.methods(false).each do |method_name|
    desc "run #{method_name}"
    task method_name do
      Foo.send(:method_name)
    end
  end
end

I'm still not sure i understand your problem (!), but i hope some part
of all the above helps :)

lasitha.
E1d641bfe4071a5413bac781f06d3fd1?d=identicon&s=25 Sean O'halpin (sean)
on 2009-02-21 19:43
(Received via mailing list)
On Sat, Feb 21, 2009 at 4:58 PM, Adrian Klingel
<adrian.klingel@illumaware.com> wrote:
>
>  end
>
> end
>
>
> I can call it statically, like:
>
> myvar = Foo::Bar::xyz
>

Can you? I get:

irb --> myvar = Foo::Bar::xyz
NoMethodError: undefined method `xyz' for Foo::Bar:Module

Perhaps you meant:

module Foo
  module Bar
    def self.mymethod
      "xyz"
    end
  end
end

myvar = Foo::Bar::mymethod      # => "xyz"

> But how can I call it dynamically.

myvar = Foo::Bar.send("mymethod") # => "xyz"

Regards,
Sean
Aad27343b68b90195befc0885351dc63?d=identicon&s=25 Myxz Ptlk (huckyducky)
on 2009-02-21 19:49
Ahhh, I'm missing the "self", thanks Sean.  But here's the other issue.
At runtime I don't know what module is going to be used.  It could be
one of several. I don't know that it's Foo::Bar.  It might be Foo:Bat or
Bat::Fat.

Is the answer then to do a dynamic include on Foo::Bar and just call
plain old "send"?
E16e84e861c1815ce11ba7bd851c857d?d=identicon&s=25 lasitha (Guest)
on 2009-02-21 20:02
(Received via mailing list)
On Sun, Feb 22, 2009 at 12:19 AM, Adrian Klingel
<adrian.klingel@illumaware.com> wrote:
> Ahhh, I'm missing the "self", thanks Sean.  But here's the other issue.
> At runtime I don't know what module is going to be used.  It could be
> one of several. I don't know that it's Foo::Bar.  It might be Foo:Bat or
> Bat::Fat.
>

You can use Module#const_get to look up a module by name.

It helps if you can arrange for all the possible modules to be
contained in a single namespace - it can be Foo::Bar or Foo:Baz, but
not X::Y.  That way you can look up the module with
Foo.const_get(name).

If it can be any arbitrary module (bad idea), you'll have to call
const_get on Kernel.

Cheers,
lasitha.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-21 20:49
Adrian Klingel wrote:
> Ahhh, I'm missing the "self"


Ahhh, I guess you don't need this then:

module Foo

  module Bar
    def mymethod
      "xyz"
    end
  end

end


str = "Foo::Bar::mymethod"

pieces = str.split("::")
method_name = pieces.pop
module_name = pieces.join("::")

mod = eval(module_name)

mod.module_eval do
  module_function method_name.to_sym
end

puts mod.module_eval(method_name)
Aad27343b68b90195befc0885351dc63?d=identicon&s=25 Myxz Ptlk (huckyducky)
on 2009-02-21 21:05
Yeah, that's definitely new to me.  Thanks stud.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-02-21 21:28
Adrian Klingel wrote:
> Yeah, that's definitely new to me.  Thanks stud.

Me too. Can anyone explain why I can do this:

num = 1
puts Object.module_eval("num")   #=>1

def f
  "xyz"
end

puts Object.module_eval("f")   #=>xyz

but I can't do this:

module Foo
  def mymethod
    "xyz"
  end
end

puts Foo.module_eval("mymethod")

--output:--
`module_eval': undefined local variable or method `mymethod' for
Foo:Module (NameError)

That result and the result from my previous example seem to imply that
free standing def's are added to Object's singleton class, i.e. they are
class methods.  But in pickaxe2, p.346 it says:

-------
Outside a class or module definition, a definition with an unadorned
method name is added as a private method to class Object, and hence may
be called in any context without an explicit receiver.
-------
Aad27343b68b90195befc0885351dc63?d=identicon&s=25 Myxz Ptlk (huckyducky)
on 2009-02-21 21:33
What about:

puts Object.module_eval("Foo:method")
E16e84e861c1815ce11ba7bd851c857d?d=identicon&s=25 lasitha (Guest)
on 2009-02-22 07:15
(Received via mailing list)
On Sun, Feb 22, 2009 at 1:58 AM, 7stud -- <bbxx789_05ss@yahoo.com>
wrote:
>
> --output:--
> `module_eval': undefined local variable or method `mymethod' for
> Foo:Module (NameError)
>
> That result and the result from my previous example seem to imply that
> free standing def's are added to Object's singleton class, i.e. they are
> class methods.

No, they are private instance methods of Object:

$: irb
01> def free_standing; end
--> nil
02> Object.private_instance_methods.grep /free_standing/
--> [:free_standing]

I suppose the confusion comes about because of the line:
> puts Object.module_eval("f")       #=>xyz

The reason this works is not that f was defined as a class method.
Its because the context in which f is resolved inherits from Object.

03> Object.module_eval do
04>   puts "self: #{self}"
05>   puts "ancestors:", self.class.ancestors
06> end
self: Object
ancestors:
Class
Module
Object
PP::ObjectMixin
Kernel
BasicObject

The current object inherits from Object (just like all objects) and
the method f was defined as an instance method of Object, so it is
available in this context.

It is of course confusing that the current object is Object and also
inherits from Object.  How ruby keeps all that straight is beyond my
knowledge :), but it does make sense intuitively.

Cheers,
lasitha
This topic is locked and can not be replied to.