I'd like to know how to call a method with a varying name. I have:
queries = ['new_teachers']
I'd like to call the method new_teachers on a module DailyQueries using a reference to the array element like DailyQueries[queries[i]], which should be equivalent to DailyQueries.new_teachers. Any help greatly appreciated.
You can use the public_send method to call an arbitrary method on an object. For example, foo.public_send(:bar) is equivalent to foo.bar, and works exactly the same way.
Knowing this you can define an array of symbols, where each symbol is a method you want to call. So:
[:bar, :baz, :qorg].each {|method|
DailyQueries.public_send(method)
end
will work the same as:
DailyQueries.bar
DailyQueries.baz
DailyQueries.qorg
Here's some reading material if you'd like to learn more about the public_send method, and its less privacy-respecting cousin, the send method:
What is the difference between ruby send and ruby public_send method?
What does send() do in Ruby?
Object#public_send on RubyDoc
If you need just a way to call a method if a variable has method name string, then, refer to #sawa's answer. If, for some specific reason, you need to invoke a module method using [] syntax, then, read on.
If you say method is part of module, then, it must be its singleton method as shown below, only then, you can invoke it using DailyQueries.new_teachers syntax
module DailyQueries
def self.new_teachers
["A", "B"]
end
end
You cannot invoke module methods using [] syntax - hence, you may have to add a method to module that does this for you.
module DailyQueries
def self.[] name, *params
method(name)[*params]
end
#... other methods definitions
end
Now, you can do this:
DailyQueries[queries[0]]
If the method had any parameters, you can pass arguments as:
DailyQueries[queries[0], "param1", "param2", :another_param]
DailyQueries.send(queries[i])
....................
Related
Is there a way to create a object that has properties and can be called using only the notation of a function call? Something equivalent to Python's __call__.
For example:
obj = ExampleClass.new()
obj() # call notation
Alternate approaches are welcome, I need a way for the callable "object" to store its own properties.
What I am trying to do is store a callable "object" in a variable. This object has properties that are associated with it, while at the same time, it can be used exactly like a method.
If you're willing to change the desired syntax a little bit...
class Foo
def call
puts 'called'
end
end
f = Foo.new
f.()
# >> called
The exact syntax, as in your question, is not possible in ruby, because parentheses are optional and, therefore, f() is the same as f. Which, in the case of a callable object is ambiguous. Do you want to perform the call or only reference the callable object? Explicit call (with the dot syntax) removes the ambiguity.
You can't make an object callable, as far as I know that's reserved for method calls, but you can do it with a bit of a hack using an alternate notation:
class ExampleClass
def []
:callable
end
end
Where now you can do:
example = ExampleClass.new
example[]
This is similar to how you can call a Proc:
proc = Proc.new { :return_value }
proc[]
# => :return_value
It's not perfect, but it'll work.
Note that normally you can side-step a lot of this mess by architecting your API around these limitations.
One thing that u can do is simple create an class and a function with the same name:
class Thing
end
def Thing()
end
Thing.new
Thing()
Given a class that has method which accepts a splat, for example a method you get from ActiveRecord::FinderMethods:
class Settle
def self.find(*args)
end
end
How should I call that method from another method, say another class, so that it has the exact same signature?
class Settler
def self.find(*args)
Settle.find(*args)
end
end
Or
class Settler
def self.find(*args)
Settle.find(args)
end
end
Or something else?
Note that the exact same signature is the important part: Settler.find should work exactly similar to Settle.find.
I am not, however, interested in code that allows the Settler.find signature to magically update whenever Settle.find changes into something completely different, like e.g. .find(scope, *args). In that case, updating the version in Settler is no problem.
It should be
Settle.find(*args)
This way, all the arguments passed into Settler.find, are passed also to Settle.find and stored in args array inside of it. So the args arrays inside of both methods hold the same value.
Passing arguments as 'splat' is quite simple - it's just passing an array as separate arguments. So if you have
ar = [arg1, arg2, arg3, argn]
calling
some_method(*ar)
is equivalent to
some_method(arg1, arg2, arg3, argn)
Beginner in ruby world, I would like to do something like:
[1,2.0,"a",2].select(&:is_a?(Integer))
but like this it definitely don't work...
Any ideas?
You can't do what you are asking for because when you use the & syntax you have to use a method that doesn't take parameters.
However, if you for some reason you really want to do something like that, you need to make a method that doesn't take parameters like so:
class Object
def is_an_integer?
is_a? Integer
end
end
You can then do:
[1,2.0,"a",2].select(&:is_an_integer)
&:method_name is syntactic sugar for &:method.to_proc. Enumerators like select and whatnot accept a block and yield each element of the enumerator to the passed block. That is:
[1,2,3].select &:even?
is equivalent to:
p = :even.to_proc
[1,2,3].select {|val| p.yield(val) }
Since only the parameters yielded by the enumerator are yielded to the proc, you would have to include them in the source list. That is, we might expect:
[[1, Integer]].select &:is_a?
to result in:
select {|*args|, p.yield(*args) }
However, remember that p isn't a method bound to any particular class! It's going to try to invoke the given method on the passed argument. So, it's going to try to invoke Array#is_a? with no arguments, rather than splatting the arguments out and invoking Integer#is_a?(Integer).
So, to accomplish this, we'll have to somehow create a proc that binds the passed arguments, and then calls the given method on the yielded receiver with the passed args. We can do this by adding a method to the Symbol class:
class Symbol
def with_args(*args)
proc {|receiver| receiver.send(self, *args) }
end
end
[1, "a"].select &:is_a?.with_args(Integer)
While it's perhaps not amazingly clean, it does work.
As the title suggests, I would like to assign all the instance methods defined on one class to another. I know at I can get a list of the methods that I want to copy from ClassA to ClassB like this:
ClassA.instance_methods(false)
And I think I can define them on ClassB like this:
ClassA.instance_methods(false).each do |method_name|
ClassB.method_define(method_name, [body here??])
end
Is there a way to get the corresponding method body, and if so, will this method work? If not, is there even a way to do this?
Others already told you to subclass. But to answer your literal question, we would be getting involved with UnboundMethod objects:
class Object
def kokot; 'kokot' end
end
o = Object.new
o.kokot
#=> kokot
3.kokot
#=> kokot
So far so good. Now let's redefine kokot method on Numeric:
class Numeric
def kokot; 'pica' end
end
o.kokot
#=> kokot
3.kokot
#=> pica
But what if we decide, that new kokot method is great for numerics, but just complex numbers should keep using the old kokot method. We can do it like this:
um = Object.instance_method :kokot
#=> #<UnboundMethod: Object#kokot>
Complex( 2, 3 ).kokot # gives the redefined kokot method
#=> pica
Complex.module_exec { define_method :kokot, um }
# Now we've just bound the old kokot to Complex
Complex( 2, 3 ).kokot
#=> kokot
In short, there is a way to "copy and paste" methods among related classes. It is required that the target be a subclass of the unbound method source. Method #source_location shows the file and the line where #kokot has been defined:
um.source_location
#=> ["(irb)", 2]
For built-in methods, #source_location returns nil. In Ruby 2.0, RubyVM class has method #disassemble:
RubyVM::InstructionSequence.disassemble( um )
#=> ( program listing goes here )
In any case, Ruby bytecode is not that beautiful to look at. Going back to your original needs, not even #define_method or UnboundMethod#bind can bind methods to incompatible objects. This cannot be cheated by tricks like redefining #kind_of?, one would have to cheat CLASS_OF() function in the native code...
From the available gems, Sourcify, RubyParser and Sorcerer are of interest. (Thanks, #Casper.) Using these, one could theoretically transplant code between incompatible objects via #eval-ling extracted method source. Long way as it goes, this technique would still fall short of realiable method transfer, as it fails whenever the source is not available at runtime (eg. self-modifying source).
It seems like what you might want is mix-ins:
Taken from http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
module Debug
def whoAmI?
"#{self.type.name} (\##{self.id}): #{self.to_s}"
end
end
class Phonograph
include Debug
# ...
end
class EightTrack
include Debug
# ...
end
ph = Phonograph.new("West End Blues")
et = EightTrack.new("Surrealistic Pillow")
ph.whoAmI? » "Phonograph (#537766170): West End Blues"
et.whoAmI? » "EightTrack (#537765860): Surrealistic Pillow"
In ruby 2.0 you can use modules. Matz explicitly forbade this behavior from classes.
But you can use instance_methods from modules.
ModuleA.instance_methods(false).each do |name|
meth = ModuleA.instance_method(name)
ClassB.send(:define_method, name, meth)
end
define_method is a private method, so that's why you use send here.
But why do this? Just include the module.
If you want to just apply behavior to an object you can unbind a method from any module and bind it to any object.
ModuleA.instance_method(:something).bind(some_object).call(args)
If this is what you want, take a look at casting, a gem that adds a convenience to doing delegation like this as well as adding methods to an object only for the life of a block.
In that case, classB should inherit classA.
Using dynamic method calls (#send or #method) the methods' visibility is ignored.
Is there a simple way to dynamically call a method that will fail calling a private method?
As I know - you need method public_send:
----------------------------------------------------- Object#public_send
obj.public_send(symbol [, args...]) => obj
From Ruby 1.9.1
------------------------------------------------------------------------
Invokes the method identified by _symbol_, passing it any arguments
specified. Unlike send, public_send calls public methods only.
1.public_send(:puts, "hello") # causes NoMethodError
Use public_send instead of send:
my_object.public_send :method, *args
It's new to Ruby 1.9, so for older Ruby, you can require 'backports/1.9.1/kernel/public_send'.
If you are using ruby-1.9, you can use Object#public_send which does what you want.
If you use ruby-1.8.7 or earlier you have to write your own Object#public_send
class Object
def public_send(name, *args)
unless public_methods.include?(name.to_s)
raise NoMethodError.new("undefined method `#{name}' for \"#{self.inspect}\":#{self.class}")
end
send(name, *args)
end
end
Or you could write your own Object#public_method which behaves like Object#method but only for public methods
class Object
def public_method(name)
unless public_methods.include?(name.to_s)
raise NameError.new("undefined method `#{name}' for class `#{self.class}'")
end
method(name)
end
end
Thought I don't understand why you want to do so, you can use eval.
class Klass
private
def private_method(arg)
end
end
k = Klass.new
m = "private_method"
eval "k.#{m}('an arg')"
NoMethodError: private method `private_method' called for #<Klass:0x128a7c0>
from (irb):15
from (irb):15
It's true though, eval really is I think the only way to actually do it pre-1.9. If you want to read more about visibility Jamis Buck wrote an awesome article about what method visibility actually means in Ruby.
Much like other things in Ruby visibility is ever so slightly different than other languages.
If you want to avoid eval, send or public_send, or you want better performance, use the public_method method:
obj.public_method('my_method_name').call
You can add arguments like this:
obj.public_method('my_method_name').call('some_argument_1', 'some_argument_2')