Understanding Ruby Symbol as Method Call

Symbol to method call

You could use Object#public_send method:

def conveyqnces_nb(symb)
User.public_send(symb).length
end

or, if you would like, Object#send might be suitable. The difference is that send allows to call private and protected methods, while public_send acts as typical method call - it raises an error if method is private or protected and the caller object isn't allowed to call it.

Ruby on Rails using Symbols in calling methods

For the first example, that's right. The before_action filter allows passing a method name as Symbol or String as its argument. But to create an instance of Event you need several different values, and for that, hashes are commonly used.

If you debug your code and check what event_params is, you're more likely to see it's an ActionController::Parameter object, which responds to a method to_h (and/or to_unsafe_h), and that's enough to create an instance of an ApplicationRecord inherited class.

So, for the first case you're invoking before_action with a Symbol, but in the second case Event.new is receiving the value event_params holds, which is a method accessible in your controller, but that evaluates to an ActionController::Parameter instance.


:event_params is a Symbol, because you constructed it that way by prefixing the identifier (name) with a :, in the other hand event_params should be a method and/or local variable, since it does not have any other way to identify as it's.

Depending on the value of event_params, they could be the same, for instance;

event_params = :event_params
# => :event_params
event_params == :event_params
# => true

In that sense, they do are the same because they point to the very same object;

event_params.object_id
# => 2155228
:event_params.object_id
# => 2155228

But what you need to understand is : makes a Hash (when valid) and event_params makes a local variable and/or method.

How method name is converted into a symbol in Ruby?

it's not clear for me, how Ruby converts name of method into :symbol?

That's the way Method#name works, it returns the name of the method as a symbol:

m = "foo".method(:size)     #=> #<Method: String#size>
m.name #=> :size
m.call #=> 3

All methods referencing other methods usually work this way. For example, Object#methods returns a array of method names:

"foo".methods
#=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, ...]

In method definition we give it name meth ... but if we want check, does any method exist, we give into method_defined symbol :meth

meth would be a reference to a variable or another method, whereas :meth is just a symbol:

meth = :foo
Mod.method_defined? meth #=> false, equivalent to Mod.method_defined? :foo
Mod.method_defined? :meth #=> true

Ruby - Call method from a symbol

you should explicitly convert String to Symbol

extract_locale_from_country(deal.country).to_sym

How are symbols used to identify arguments in ruby methods

Symbols and hashes are values like any other, and can be passed like any other value type.

Recall that ActiveRecord models accept a hash as an argument; it ends up being similar to this (it's not this simple, but it's the same idea in the end):

class User
attr_accessor :fname, :lname

def initialize(args)
@fname = args[:fname] if args[:fname]
@lname = args[:lname] if args[:lname]
end
end

u = User.new(:fname => 'Joe', :lname => 'Hacker')

This takes advantage of not having to put the hash in curly-brackets {} unless you need to disambiguate parameters (and there's a block parsing issue as well when you skip the parens).

Similarly:

class TestItOut
attr_accessor :field_name, :validations

def initialize(field_name, validations)
@field_name = field_name
@validations = validations
end

def show_validations
puts "Validating field '#{field_name}' with:"
validations.each do |type, args|
puts " validator '#{type}' with args '#{args}'"
end
end
end

t = TestItOut.new(:name, presence: true, length: { min: 2, max: 10 })
t.show_validations

This outputs:

Validating field 'name' with:
validator 'presence' with args 'true'
validator 'length' with args '{min: 2, max: 10}'

From there you can start to see how things like this work.

Calling private methods by symbol name in Ruby

After a fair amount of searching, I found an alternative answer than Object#send, that has an unanticipated feature benefit. The solution is to use the Object#method to return a Method object for the symbol name.

A Method object is a Proc-like callable object, so it implements the #call interface, which fits the bill nicely. Object has many such useful helpers defined in its interface.

In context of the original question, this is how it works:

#tests
[:test_value_1, :test_value_2, :test_value_3].each do |method|
data.each do |test|
value = test[0]
expected_result = test[1]
puts "#{method}: #{self.method(method).call(value) == expected_result ? 'Pass' : 'Fail'}: '#{value}'"
end
end

The important bits are:

self.method(method).call(value)

This will convert the symbol name to a Method object, and then invoke the method with value supplied as the parameter. This works roughly equivalently to the send method solution, in functional terms. However, there are some differences to note.

send is going to be somewhat more efficient, as there's no overhead in the conversion to a Method. Method#call and send use different internal calling mechanisms, and it appears that send has less call overhead, as well.

The unanticipated feature of using Object#method is that the Method object is easily converted to a Proc object (using Method#to_proc). As such, it can be stored and passed as a first-class object. This means that it can be supplied in place of a block or provided as a callback, making it useful for implementing flexible dispatch solutions.



Related Topics



Leave a reply



Submit