144

Can someone please tell me what the following snippet

obj.send("#{method_name}")

is and does?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Christian Bankester
  • 2,044
  • 2
  • 15
  • 18

8 Answers8

159

send sends a message to an object instance and its ancestors in class hierarchy until some method reacts (because its name matches the first argument).

Practically speaking, those lines are equivalent:

1.send '+', 2
1.+(2)
1 + 2

Note that send bypasses visibility checks, so that you can call private methods, too (useful for unit testing).


If there is really no variable before send, that means that the global Object is used:

send :to_s    # "main"
send :class   # Object
giraff
  • 4,601
  • 2
  • 23
  • 35
  • 1
    Oh I see, so one might use send if one wanted to store something like 1.month on the database instead of statically saying the number of days. – Christian Bankester Jul 26 '10 at 20:00
  • 3
    True, you could use it to call method with names that are computed, not static. (You shouldn't allow unrestricted user input, though, to avoid calling private methods... You could, however, give them a unique prefix: send 'user_method_'+methodname, *args) – giraff Jul 26 '10 at 20:06
  • 2
    Good use case might be it you want to test a protected class method, you could call it outside a class—in test file.. – GN. Jun 28 '17 at 02:40
134

send is a Ruby method allowing to invoke another method by name passing it any arguments specified.

 class Klass
   def hello(*args)
     "Hello " + args.join(' ')
   end
 end
 k = Klass.new
 k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"

Source

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
80

One of the most useful feature I think with .send method is that it can dynamically call on method. This can save you a lot of typing. One of the most popular use of .send method is to assign attributes dynamically. For example:

class Car
  attr_accessor :make, :model, :year
end  

To assign attributes regularly one would need to

c = Car.new
c.make="Honda"
c.model="CRV"
c.year="2014"

Or using .send method:

c.send("make=", "Honda")
c.send("model=", "CRV")
c.send("year=","2014")

But it can all be replaced with the following:

Assuming your Rails app needs to assign attributes to your car class from user input, you can do

c = Car.new()
params.each do |key, value|
  c.send("#{key}=", value)
end
the12
  • 2,395
  • 5
  • 19
  • 37
Antonio Jha
  • 1,301
  • 13
  • 13
  • 9
    Using .send in this manner adds unnecessary complexity and makes it easier to inadvertently introduce a bug into the code. For example, in your code above, if you add a new entry to your parameters hash (such as 'cylinders'), the code will fail with an undefined method error. – Kevin Schwerdtfeger Aug 05 '15 at 12:06
  • 3
    respond_to? could be used to prevent such errors, if desired. – Richard_G Oct 04 '15 at 19:14
  • 1
    @Kevin you are right, but sometimes it may be necessary. More flexibility correlates to more risk, which can be mitigated. – Will Sheppard Mar 26 '18 at 08:59
  • I always `rescue NoMethodError` when using `send` and either send the error to a log and/or implement some default behavior that should be done in the event of an undefined method. – Matthew Jan 03 '20 at 19:26
18

Another example, similar to Antonio Jha's https://stackoverflow.com/a/26193804/1897857

is if you need to read attributes on an object.

For example, if you have an array of strings, if you try to iterate through them and call them on your object, it won't work.

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.a
end
# => NoMethodError: undefined method `a'

However, you can send the strings to the object:

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.send(a)
end
# => Vandalay Project
# => A very important project
Community
  • 1
  • 1
Mike Vallano
  • 326
  • 4
  • 4
  • Thanks! That's exactly the answer I am after. Wondering is this commonly used? I came across something similar in the legacy code, not sure I should stick with it.@Mike Vallano – B Liu Oct 23 '17 at 03:51
  • 1
    @b-liu I've seen it used by experienced developers in new code. It can also be helpful when using `define_method`: https://apidock.com/ruby/Module/define_method. – Mike Vallano Nov 02 '17 at 11:18
14

What does send do?

send is another way of "calling a method". Example:

o = Object.new
o.to_s # => "#<Object:0x00005614d7a24fa3>"
# is equivalent to:
o.send(:to_s) # => "#<Object:0x00005614d7a24fa3>"

Send lives in the Object class.

What is the benefit of this?

The benefit of this approach is that you can pass in the method you want to call as a parameter. Here is a simple example:

def dynamically_call_a_method(method_name)
    o = Object.new
    o.send method_name
end
dynamically_call_a_method(:to_s) # => "#<Object:0x00005614d7a24fa3>"

You can pass in the method you want to be called. In this case we passed in :to_s. This can be very handy when doing ruby metaprogramming, because this allows us to call different methods according to our different requirements.

BenKoshy
  • 33,477
  • 14
  • 111
  • 80
5

Send can also be used as a way of showing how everything in Ruby is an object

1.send(:+, 1)  ## -> 2
3.send(:*, 2)  ## -> 6
DuDa
  • 3,718
  • 4
  • 16
  • 36
Chi Joel
  • 68
  • 1
  • 4
2

Another use case for views:

    <%= link_to 
    send("first_part_of_path_#{some_dynamic_parameters}_end_path", 
    attr1, attr2), ....
    %>

Allow . you to write scalable view who work with all kind of objects with:

    render 'your_view_path', object: "my_object"
mahatmanich
  • 10,791
  • 5
  • 63
  • 82
1

I am pretty late to the topic. As a noob I just used it and wanted to be helpful to people like me who wanted straightforward answer. filter_hash.each{|k,v| order_additional_hash[send(method_name, k)] = v}

as seen above send(method_name) is the method, which we want to call. It matches the name and calls the method if the method name is passed as string in the params and k is the argument we want to pass in the method.