ruby send method passing multiple parameters - ruby

Trying to create objects and call methods dynamically by
Object.const_get(class_name).new.send(method_name,parameters_array)
which is working fine when
Object.const_get(RandomClass).new.send(i_take_arguments,[10.0])
but throwing wrong number of arguments 1 for 2 for
Object.const_get(RandomClass).new.send(i_take_multiple_arguments,[25.0,26.0])
The Random Class defined is
class RandomClass
def i_am_method_one
puts "I am method 1"
end
def i_take_arguments(a)
puts "the argument passed is #{a}"
end
def i_take_multiple_arguments(b,c)
puts "the arguments passed are #{b} and #{c}"
end
end
Can someone help me on how to send mutiple parameters to a ruby method dynamically

send("i_take_multiple_arguments", *[25.0,26.0]) #Where star is the "splat" operator
or
send(:i_take_multiple_arguments, 25.0, 26.0)

You can alternately call send with it's synonym __send__:
r = RandomClass.new
r.__send__(:i_take_multiple_arguments, 'a_param', 'b_param')
By the way* you can pass hashes as params comma separated like so:
imaginary_object.__send__(:find, :city => "city100")
or new hash syntax:
imaginary_object.__send__(:find, city: "city100", loc: [-76, 39])
According to Black, __send__ is safer to namespace.
“Sending is a broad concept: email is sent, data gets sent to I/O sockets, and so forth. It’s not uncommon for programs to define a method called send that conflicts with Ruby’s built-in send method. Therefore, Ruby gives you an alternative way to call send: __send__. By convention, no one ever writes a method with that name, so the built-in Ruby version is always available and never comes into conflict with newly written methods. It looks strange, but it’s safer than the plain send version from the point of view of method-name clashes”
Black also suggests wrapping calls to __send__ in if respond_to?(method_name).
if r.respond_to?(method_name)
puts r.__send__(method_name)
else
puts "#{r.to_s} doesn't respond to #{method_name}"
end
Ref: Black, David A. The well-grounded Rubyist. Manning, 2009. P.171.
*I came here looking for hash syntax for __send__, so may be useful for other googlers. ;)

Related

Load function from variable name (Ruby)

Here's a short example of my problem.
prefix = "!"
commands = ["right"]
response = nil
message = "Some text !right here"
for i in 0..commands.length
if message.include?(prefix + commands[i]) # If message contains "!right"
response = commands[i](parameter) # Load matching function
end
end
if response
puts(response)
end
def right(parameter)
"This is an example response"
end
Why can't I load the function right by doing it like this?
First of all, numeric for loops are usually not used in ruby. You can just write
commands.each do |command|
...
end
As for calling "functions", you need to know that functions as such don't exist in ruby, only methods (which are, in theory, just a special case of function).
To call a method on an object, you use the send method, which "sends" (read: calls) a "message" (read: the method name) to the object.
The send method takes a symbol as its argument, which is in practice just an interned string, but you're supposed to use them differently from normal strings.
Last but not least, how can you write def outside of any class to define a function, but it's still somehow a method? That's because Ruby pretty much wraps your entire code in an implicit object.
In practice, you'll be better off using a lambda, which is really just an object with a call method that simulates first class functions as you may know them from javascript, lua, etc.
The syntactic sugar for defining one is whatever = lambda { |argument| puts "I'm a lambda" } or whatever = ->(argument){ puts "I'm a lambda too" }.
do |argumetn| ... some lines of code ... end syntax can also be used with both lambda and -> notations.
You can then call the lambda with whatever.call(<argument>)
There's also Procs, which are like lambdas, but with a few differences and I suggest you just google it if you want to know what exactly they are.
Assuming each command is a lambda (or a proc), to assign each one to a string, I recommend just using a hash (read: map or dict).
They are defined like this:
my_map_variable = {
20 => "I'm an integer",
"string" => "I'm a string",
:symbol => "I'm a symbol, an interned string",
symbol2: "I'm the same as above, but with fancy notation",
right: -> (parameter) { puts "This is an example response" }
}
You can then access the values like this
puts my_map_variable[20] # prints: I'm an integer
puts my_map_variable[:symbol2]
puts my_map_variable[:right].call(nil) # prints: "This is an example response"
Lastly, if you have a string "right", but your hash uses symbol indices, you can just call "right".to_sym to turn the string into a symbol. Or you can just use strings in the first place.

How to pass a block

For the sake of simplicity, I've tried to abstract the problem down to its core elements. I've included a small piece of functionality wherein I use Socket to show that I want to pass the block further down into a method which is a black box for all intents and purposes. I'm also passing a constant True for the sake of showing I want to pass arguments as well as a yield block.
With all that being said, if I small have a hierarchy of calls as such:
def foo(use_local_source)
if use_local_source
Socket.unix("/var/run/my.sock") &yield
else
Socket.tcp("my.remote.com",1234) &yield
end
end
foo(True) { |socket|
name = socket.read
puts "Hi #{name}, I'm from foo."
}
How can I pass the implicitly declared block right down through foo and into Socket as if I were calling Socket.tcp(...) { ... } directly.
I know I could set it as an argument, but it doesn't feel idiomatic to Ruby. Is this also untrue and I should pass it as an argument? I've tried combinations of & and *, and I get a range of exception.
def foo(use_local_source)
if use_local_source
yield Socket.unix("/var/run/my.sock")
else
yield Socket.tcp("my.remote.com",1234)
end
end
From the docs for yield:
Yields control back to the context that resumed the fiber, passing along any arguments that were passed to it.

Omitting an argument for a method in a block

I wonder, is it possible to do something similar in Ruby to what I can do in Scala or other languages:
someCollection.foreach(x => println(x)) // a full version
someCollection.foreach(println) // a short version
In Ruby I can do:
some_array.each { |x| puts x }
So how can I do this?
some_array.each { puts }
UPDATE:
I'm not talking about puts in particular, it just picked it for example. There might be some_other_method which takes one parameter.
some_array.map { some_other_method }
some_array.map(some_other_method) # ???
def some_other_method a
# ... doing something with a
end
If you look up the rules for implicit η-expansion in the SLS (§6.26.5), it should be immediately obvious that it relies crucially on static type information and thus cannot possibly work in Ruby.
You can, however, explicitly obtain a Method object via reflection. Method objects respond to to_proc and like any object that responds to to_proc can thus be passed as if they were blocks using the unary prefix & operator:
some_array.each(&method(:puts))
Not quite like that, unfortunately. You can send a method name to be called on each object, e.g.:
some_array.each &:print_myself
Which is equivalent to:
some_array.each {|x| x.print_myself}
But I don't know of a clean (read: built-in) way to do what you're asking for. (Edit: #Jörg's answer does this, though it doesn't really save you any typing. There is no automatic partial function application in Ruby)

Understanding Ruby symbol as method call [duplicate]

This question already has answers here:
How to understand symbols in Ruby
(11 answers)
Closed 10 years ago.
class A
def test
"Test from instance"
end
class << self
def test
"Test from class"
end
end
end
p A.send(:test) # "Test from class"
p A.new.method(:test).call # "Test from instance"
Here symbol works as expected, but here:
s="test"
s1=:s
p s1 # :s
why :s is printed here?? I dont understand the reason behind it.
Can anyone please explain for me ?
Symbols are sort of lightweight strings (though they are not strings). The send() and method() methods can take strings or symbols; one is converted to the other in the inner workings (not sure which) and then ruby executes the method with the matching name. Hence A.send(:text) is equivalent to A.text(). If you had a variable named methodName = :text, you could do A.send(methodName) but not A.methodName().
Symbols are not variables, so you can't assign a value to a symbol. In your example, the symbol :s is unrelated to the variable s (despite the fact that they have the same "name", preceding it with a colon makes it a symbol instead of a variable). You're assigning a string value to the variable s but telling it to print the symbol :s, which it does.
Symbols are just a special kind of stringlike value that's more efficient for the runtime to deal with than a regular string. That's it. They aren't methods or variables or anything like that.
When you do A.send(:test), all you are doing is saying "hey, A, call the method named 'test'". You aren't sending the method itself, just the name; it's the logic inside send that is responsible for looking up the actual method to call.
The same thing goes when you ask for method with A.new.method(:test). All you are passing to method is the name "test", not the method defined with that name. It's up to method to use the name and find the actual method so it can return it, and it's that return value - a Method object - that you are doing call on. You can't do call on a Symbol like :test, because it's just a name.
From https://stackoverflow.com/a/1255362/509710:
p foo does puts foo.inspect, i.e. it prints the value of inspect instead of to_s, which is more suitable for debugging (because you can e.g. tell the difference between 1, "1" and "2\b1", which you can't when printing without inspect).
s="test"
s1=:s
p :s.object_id #137448
p s.object_id #77489950
p s1.object_id #137448
I have understand it now. I was assigning a symbol but expecting a string.
You set the value of s1 to be :s, so why would you expect it to return anything different?
If you look at the ruby API for the Object class, you will see both Object#send and Object#method take a symbol as a parameter, so the top example is also totally expected.

Strange ruby syntax

what the syntax is in Action Mailer Basics rails guide ?
class UserMailer < ActionMailer::Base
def welcome_email(user)
recipients user.email
from "My Awesome Site Notifications <notifications#example.com>"
subject "Welcome to My Awesome Site"
sent_on Time.now
body {:user => user, :url => "http://example.com/login"}
end
end
How should i understand the construction, like
from "Some text for this field"
Is it an assignment the value to a variable, called "from" ?
No, it's a method call. The name of the method is from, and the argument is a string. In Ruby, parentheses around method calls are optional, so
from "Some text for this field"
is the same as
from("Some text for this field")
Rails (and many Ruby libraries) like to express code in a natural language style, though, so the parentheses-less version reads better, hence why it is used in examples.
It is a call to a method from with the argument "Some text for this field"
The method comes from the ActionMailer::Base class that your UserMailer extends from.
In Ruby the parentheses around a method call are optional unless something would be ambiguous so the statement is equivalent to from("Some text for this field")
Rails has a coding style that prefers to be close to natural language where possible, hence not using parentheses unless necessary.
Calling this method sets an instance variable #from to the value you provide so that it can be used later when sending the message.
Normally when you have accessor methods for getting and setting a variable you would have from= to set the value and from to return the value, however ActionMailer uses something called adv_attr_accessor to define the from method so that if you call it with a parameter then it acts as a setter but if you call it with no parameters then it acts as a getter.
This can be seen in actionmailer-2.x.x/lib/action_mailer/base.rb and actionmailer-2.x.x/lib/action_mailer/adv_attr_accessor.rb
It's not an assignment. In Ruby, assignments are done using the assignment operator = like this:
var = val
You are probably thinking of some Lisp dialects where assignment looks like this:
(def var val)
It's just a simple receiverless message send.
In Ruby, the general syntax for a message send is
receiver.selector(argument1, argument2)
However, if the receiver is self, you can leave off the receiver, so
selector(argument1, argument2)
is the same as
self.selector(argument1, argument2)
[Note: this is not quite true. In Ruby, private methods can only be invoked via a receiverless message send, so if in this example self responds to the selector message by invoking a private method, only the first variant will work, the second will raise a NoMethodError exception.]
Also, in cases where there are no ambiguities, you can leave off the parentheses around the arguments like this:
receiver.selector argument1, argument2
If you put the two things together, you can now see that
selector argument1, argument2
is equivalent to
self.selector(argument1, argument2)
and thus
from "Some text for this field"
is equivalent to
self.from("Some text for this field")
There is a third shortcut in Ruby's message sending syntax: if the very last argument to a message send is a Hash literal, then you can leave out the curly braces. So, the last line in the above example could also be written as
body :user => user, :url => "http://example.com/login"
Also, in Ruby 1.9, a Hash literal where all keys are Symbols can be written using an alternative Hash literal syntax:
{ key1: val1, key2: val2 }
is the same as the old syntax
{ :key1 => val1, :key2 => val2 }
which means that, at least in Ruby 1.9, that last line could also be written as
body user: user, url: "http://example.com/login"
You could also call from an attribute. It's a property of the email, but how it's implemented is hidden from you (encapsulation). This is a Good Thing. It means that if Rails core decided it's better to change #from into several variables, you wouldn't need to change any of your code.

Resources