I want to store several different methods in an array in Ruby. Suppose I want to store the type method twice:
[type, type]
doesn't store two entries of type in an array; it executes type twice, and stores the results in the array. how do I refer explicitly to the method object itself?
(this is just a simplified version of what I really want.)
EDIT: on second thoughts, it bothers me that the solution proposed below avoids the problem by passing the name of the method. how do you pass the method object itself? for example, what if you pass [:type, :type] to a method that has an alternative resolution for type? how can you pass the type method object itself?
If you want to store a method rather than the result of calling a method or just the message you'd send to invoke it, you need to use the method method on the owning object. So for example
"hello".method(:+)
will return the + method of the object "hello", so that if you call it with the argument " world", you'll get "hello world".
helloplus = "hello".method(:+)
helloplus.call " world" # => "hello world"
If you're thinking about doing method references in Ruby, you're doing it wrong.
There is a built-in method in Ruby called method. It will return a proc version of the method. It is not a reference to the original method, though; every call to method will create a new proc. Changing the method won't affect the proc, for example.
def my_method
puts "foo"
end
copy = method(:my_method)
# Redefining
def my_method
puts "bar"
end
copy.call
# => foo
When you want to store pieces of code, and don't want to use regular methods, use procs.
stack = [proc { do_this }, proc { do_that }]
stack.each {|s| s.call }
[:type, :type].collect { |method_name| self.send(method_name) }
Alternatively, if the method is part of an object:
method = obj.method(:type)
values = [method.call, method.call]
Related
This article touches on the issues but doesn't give a solution.
This started when I wanted to write a method and optionally pass it an argument which could be null or a ???? (proc, lambda, method, block, ???). Lets call it, for now, a block because a block works. The block takes one required argument. An example of the method and a call to it would be:
#!/usr/bin/env ruby
def foo(&proc)
puts "before"
if proc
yield "passed to proc"
end
puts "after"
end
def add_message(s)
puts "from add_message #{s}"
end
foo { |s| add_message(s) }
foo
And the output is:
before
from add_message passed to proc
after
before
after
Great. But, what I'd like to do is be able to call foo like this: foo(&:add_message). But I can't. Changing line 15 above I get:
before
./temp.rb:11:in `add_message': wrong number of arguments (given 0, expected 1) (ArgumentError)
from ./temp.rb:6:in `foo'
from ./temp.rb:15:in `<main>'
And, as the article above mentions, the arity is now -2. So, how do I write a simple method like add_message that I can use with &:add_message. OR!!! as is the case 99.99% of the time, please set me on the proper track on how to do this.
The problem is that Symbol#to_proc does not create a proc that calls add_message method correctly.
# `yield` will pass its arguments to proc
>> :add_message.to_proc.call('passed to proc')
# => ArgumentError
This calls 'passed to proc'.add_message, because our method is defined in Object it works when called on String, however it is missing the required argument.
The solution is to make a proc that can accept the same arguments as add_message method and pass them along to that method. We can use Object#method that returns Method object that implements its own to_proc and has the same arity as the method.
>> method(:add_message).to_proc.arity
=> 1
>> method(:add_message).to_proc.call('passed to proc')
from add_message passed to proc
>> foo(&method(:add_message))
before
from add_message passed to proc
after
From the Ruby docs
Conversion of other objects to procs
Any object that implements the to_proc method can be converted into a proc by the & operator, and therefore can be consumed by iterators.
class Greeter
def initialize(greeting)
#greeting = greeting
end
def to_proc
proc {|name| "#{#greeting}, #{name}!" }
end
end
hi = Greeter.new("Hi")
hey = Greeter.new("Hey")
["Bob", "Jane"].map(&hi) #=> ["Hi, Bob!", "Hi, Jane!"]
["Bob", "Jane"].map(&hey) #=> ["Hey, Bob!", "Hey, Jane!"]
Of the Ruby core classes, this method is implemented by Symbol, Method, and Hash.
So when you pass an argument with a unary ampersand before it, to_proc gets called on it. The &: "syntax" is actually & being called on a symbol literal, i.e. &(:foobar), and Symbol.to_proc has the behavior of converting a symbol into a method call on its first argument, i.e. these two are roughly equivalent (modulo named argument forwarding)
:foobar.to_proc
proc { |x, *args| x.foobar(*args) }
Ruby's Method type also implements to_proc, so if you have a standalone method called foobar (on a module, say, Example), then you can call Example.method(:foobar) and get an &-compatible object. If you have a "top-level" method, then it's probably being defined on the main object and calling method with no explicit receiver will work.
The other type mentioned in that quote is hashes, which can be turned into a function mapping their keys to their values (and returning nil if no matching key exists). And, of course, you can always implement a method called to_proc on your own classes and it'll work just as well as any built-in type.
class Integer
def set
return self + 1
end
end
p [1,2,3,4,5,6].map(&:set)
I think when you can use &: syntax that a method have been defined for a class like above
How is a good way of passing a method that accept a block?
I.e. to treat the method as a variable so it could be used in this way:
(#glob&.each || crawl_dir(#base_dir)) do |file|
puts "#{file}"
end
A simple example that can be tried out:
> require 'csv'
=> true
> CSV {|v|v}
=> <#CSV io_type:$stdout encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
> a=CSV
=> CSV
> a==CSV
=> true
> a {|v|v}
Traceback (most recent call last):
1: from (irb):14
NoMethodError (undefined method `a' for main:Object)
Is this possible?
In your example, you are using different things which are all called CSV.
CSV {|v|v}
Here, you are calling the method named CSV with a block. You get the return value of that method back. Methods such as this are usually used as converters. E.g. there is the Integer() method which takes an argument (such as a String) which is converted to an Integer object. Usually, those methods are called the same as the class of object they return.
a=CSV
Here, you are assigning the value of the CSV constant (which is the CSV class) to the a variable. In your code, you can then either use the CSV constant or the a variable to refer to the class object.
In these cases where you have the same name refer to different things (a class and a method respectively), Ruby can distinguish which one to call / return based on how you use it. Only of you explicitly and unambiguously call the "thing" (i.e. by passing a block or any other arguments), Ruby will call the method.
In all other cases, if you refer to a thing with a name starting with a capital letter, Ruby expects it to a constant and returns its referred to object (which in this case is the CSV class object).
a {|v|v}
Here, you get an error since Ruby tries to call the method named a (which doesn't exist). Even if this would work, the a variable is at this point a reference to the CSV class (which can't be called directly).
(Note: to call a method whose name you have stored in a variable, you can use the send method, e.g. my_receiver.send(a).)
Now, to solve your initial issue, you could create a method object and use it to call the desired method, e.g.
method_proc = #glob&.method(:each) || method(:crawl_dir).curry(#base_dir)
method_proc.call do |file|
puts file
end
However, this is not very idiomatic Ruby. Here, method references are seldom carried around (rather than in Javascript or Python where you regularly do this with passed function objects). A more idiomatic implementation in Ruby could be:
def handle_file(file)
puts file
end
if #glob.respond_to(:each)
#glob.each { |file| handle_file(file) }
else
crawl_dir(#base_dir) { |file| handle_file(file) }
end
Even more idiomatic would be if your crawl_dir method would (optionally) return an Enumerator object, in which case you could simplify the calling code.
Here, I assume that #glob is either nil or an Enumerable object (such as an Array or an Enumerator which thus response directly to each). This allows us to simplify the code further.
def crawl_dir(directory)
# Return an Enumerator object for the current method invocation if no
# block was passed, similar to how the standard `Enumerable#each` method
# works.
return enum_for(__method__) unless block_given?
# the rest of the method here...
end
enumerable = #glob || crawl_dir(#base_dir)
enumerable.each do |file|
puts file
end
What happens here is that you either take #glob (which we assume to be an Enumerable object if it exists, and thus response do each) or crawl_dir(#base_dir) without a block. From your crawl_dir method, you will get then an Enumerator object back, which is again an Enumerable. You can then loop over this object with each. Both of your objects thus have the same return type can can thus be used similarly.
If you want to get a reference to a method, you can use the Object#method method. You can then call this method using the Method#call method, or the .() syntactic sugar:
require 'csv'
a = method(:CSV)
a.() {|v| v }
#=> #<CSV io_type:$stdout encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">
You can always use tap on any object:
a.tap do |v|
p v
end
Is there a way to bind an existing method to an existing instance of an object if both the method and the instance are passed as symbols into a method that does that if the instance is not a symbol?
For example:
def some_method
#do something
end
some_instance = Klass.new(something)
def method_that_binds(:some_method, to: :some_instance)
#how do I do that?
end
Your requirements are a little unusual, but it is possible to do this mostly as you say:
class Person; end
harry = Person.new
barry = Person.new
def test
puts 'It works!'
end
define_method :method_that_binds do |a_method, to|
eval(to[:to].to_s).singleton_class.send(:define_method, a_method, &Object.new.method(a_method))
end
method_that_binds :test, to: :harry
harry.test
# It works! will be sent to STDOUT
barry.test
# undefined method 'test'
This doesn't actually use a named parameter, but accepts a hash with a to key, but you can see you can call it in the way you want. It also assumes that the methods you are defining are defined globally on Object.
The API you want doesn't easily work, because you have to know from which scope you want to access the local variable. It's not quite clear to me why you want to pass the name of the local variable instead of passing the content of the local variable … after all, the local variable is present at the call site.
Anyway, if you pass in the scope in addition to the name, this can be accomplished rather easily:
def some_method(*args)
puts args
puts "I can access some_instance's ivar: ##private_instance_var"
end
class Foo; def initialize; #private_instance_var = :foo end end
some_instance = Foo.new
def method_that_binds(meth, to:, within:, with: [])
self.class.instance_method(meth).bind(within.local_variable_get(to)).(*with)
end
method_that_binds(:some_method, to: :some_instance, within: binding, with: ['arg1', 'arg2'])
# arg1
# arg2
# I can access some_instance's ivar: foo
As you can see, I also added a way to pass arguments to the method. Without that extension, it becomes even simpler:
def method_that_binds(meth, to:, within:)
self.class.instance_method(meth).bind(within.local_variable_get(to)).()
end
But you have to pass the scope (Binding) into the method.
If you'd like to add a method just to some_instance i.e. it's not available on other instances of Klass then this can be done using define_singleton_method (documentation here.)
some_instance.define_singleton_method(:some_method, method(:some_method))
Here the first use of the symbol :some_method is the name you'd like the method to have on some_instance and the second use as a parameter to method is creating a Method object from your existing method.
If you'd like to use the same name as the existing method you could wrap this in your own method like:
def add_method(obj, name)
obj.define_singleton_method(name, method(name))
end
Let's say we have a class A with a method a and a local variable c.
class A
def a; 10 end
end
c = '5'
And we want to add the method A#a to c.
This is how it can be done
c.singleton_class.send :define_method, :b, &A.new.method(:a)
p c.b # => 10
Explanations.
One way to add a method to an object instance and not to its class is to define it in its singleton class (which every ruby object has).
We can get the c's singleton class by calling the corresponding method c.signleton_class.
Next we need to dynamically define a method in its class and this can usually be accomplished by using the define_method which takes a method name as its first argument (in our case :b) and a block. Now, converting the method into a block might look a bit tricky but the idea is relatively simple: we first transform the method into a Method instance by calling the Object#method and then by putting the & before A.new.method(:a) we tell the interpreter to call the to_proc method on our object (as our returned object is an instance of the Method, the Method#to_proc will be called) and after that the returned proc will be translated into a block that the define_method expects as its second argument.
I understand that method_missing is something of a last resort when Ruby is processing messages. My understanding is that it goes up the Object hierarchy looking for a declared method matching the symbol, then back down looking for the lowest declared method_missing. This is much slower than a standard method call.
Is it possible to intercept sent messages before this point? I tried overriding send, and this works when the call to send is explicit, but not when it is implicit.
Not that I know of.
The most performant bet is usually to use method_missing to dynamically add the method being to a called to the class so that the overhead is only ever incurred once. From then on it calls the method like any other method.
Such as:
class Foo
def method_missing(name, str)
# log something out when we call method_missing so we know it only happens once
puts "Defining method named: #{name}"
# Define the new instance method
self.class.class_eval <<-CODE
def #{name}(arg1)
puts 'you passed in: ' + arg1.to_s
end
CODE
# Run the instance method we just created to return the value on this first run
send name, str
end
end
# See if it works
f = Foo.new
f.echo_string 'wtf'
f.echo_string 'hello'
f.echo_string 'yay!'
Which spits out this when run:
Defining method named: echo_string
you passed in: wtf
you passed in: hello
you passed in: yay!
for instance in python it is possible to assign a method to a variable:
class MyClass
def myMethod(self):
return "Hi"
x = MyClass()
method = x.myMethod
print method() # prints Hi
I know this should be possible in Ruby, but I don't know what's the syntax.
You need to grab the method by using method with the method’s name as an argument. This will return you an instance of type Method, which can be called with call().
class MyClass
def myMethod
"Hi"
end
end
x = MyClass.new
m = x.method(:myMethod)
# => #<Method: MyClass#myMethod>
puts m.call
# You can also do m[] instead of m.call()
Note that any arguments would need to be added to the call method.
In many practical cases, however, there is no need to have the method itself saved to a variable in Ruby; if you just want to dynamically call a method (i.e. send a message to an object) and there is no need to save the method, you could also use the send (or __send__ method in case of name clashes).
x = MyClass.new
puts x.send :myMethod # also possible with a string: m.send "myMethod"
# "Hi"
Any arguments should follow the method name:
puts x.send(:myMethod, arg1, arg2)
To use it like this is probably more Ruby-like, as the concept of Method classes is not as prominent as it is in Python. In Python, you can always think of a two step mechanism when doing something like a_string.split(); first you grab the method with a_string.split and then you call it (either implicitly with () or explicitly with __call__()). So, cutting that two-step mechanism is rather natural to do.
Ruby is more based on message passing and to actually get a method class in Ruby, you’ll have to do some more work, because in some way, the method object will have to be constructed for you at that point. So, unless you really need some Methods object in Ruby, you should rather stick to the message passing abstraction and simply use send.
I think you are looking for Proc or lambda block
x = Proc.new { return "Hello World" }
puts x.call
x = lambda { return "Hello World" }
puts x.call
I would read this short post - there is a slight but significant difference in the way the methods behave
http://samdanielson.com/2007/3/19/proc-new-vs-lambda-in-ruby