Ruby's send method with block variable interpolation - ruby

I'm a newb working through some Ruby tutorials and am stumped on the use of the send method below. I can see the send method is reading the value of the attribute iterator over, but the Ruby documentation states the send method takes a method prepended with a colon. So, my confusion lies in how the send method below is interpolating the attribute variable being iterated over.
module FormatAttributes
def formats(*attributes)
#format_attribute = attributes
end
def format_attributes
#format_attributes
end
end
module Formatter
def display
self.class.format_attributes.each do |attribute|
puts "[#{attribute.to_s.upcase}] #{send(attribute)}"
end
end
end
class Resume
extend FormatAttributes
include Formatter
attr_accessor :name, :phone_number, :email, :experience
formats :name, :phone_number, :email, :experience
end

It's not "invoking the value of the iterator", but instead calling a method with that name. In this case because of the attr_accessor declaration, these methods map to properties.
Calling object.send('method_name') or object.send(:method_name) are equivalent to object.method_name in general terms. Likewise, send(:foo) and foo will call the method foo on the context.
Since the module declare a method that is later mixed in with an include, calling send in the module has the effect of calling a method on an instance of the Resume class.

send Documentation
here is simplified version of your code,to feed you what's going on:
def show
p "hi"
end
x = "show"
y = :show
"#{send(y)}" #=> "hi"
"#{send(x)}" #=> "hi"

Related

Why can't a class method have the same name as a non-class method?

I'm learning ruby, and noticed that I cannot create a class method called puts:
class Printer
def initialize(text="")
#text = text
end
def puts
puts #text
end
end
The error is:
`puts': wrong number of arguments (given 1, expected 0)
My expectation was that I could use the code like this:
p = Printer.new("hello")
p.puts
It's not just because puts is a built-in method, though. For instance, this code also gives a syntax error:
def my_puts(text)
puts text
end
class Printer
def initialize(text="")
#text = text
end
def my_puts
my_puts #name
end
end
tldr; within the scope of the instance, the puts resolves to self.puts (which then resolves to the locally defined method, and not Kernel#puts). This method overriding is a form of shadowing.
Ruby has an 'implicit self' which is the basis for this behavior and is also how the bare puts is resolved - it comes from Kernel, which is mixed into every object.
The Kernel module is included by class Object, so its methods [like Kernel#puts] are available in every Ruby object. These methods are called without a receiver and thus can be called in functional form [such as puts, except when they are overridden].
To call the original same-named method here, the super keyword can be used. However, this doesn't work in the case where X#another_method calls X#puts with arguments when it expects to be calling Kernel#puts. To address that case, see Calling method in parent class from subclass methods in Ruby (either use an alias or instance_method on the appropriate type).
class X
def puts
super "hello!"
end
end
X.new.puts
P.S. The second example should trivially fail, as my_puts clearly does not take any parameters, without any confusion of there being another "puts". Also, it's not a syntax error as it occurs at run-time after any language parsing.
To add to the previous answer (https://stackoverflow.com/a/62268877/13708583), one way to solve this is to create an alias of the original puts which you use in your new puts method.
class Printer
alias_method :original_puts, :puts
attr_reader :text
def initialize(text="")
#text = text
end
def puts
original_puts text
end
end
Printer.new("Hello World").puts
You might be confused from other (static) programming languages in which you can overwrite a method by creating different signatures.
For instance, this will only create one puts method in Ruby (in Java you would have two puts methods (disclaimer: not a Java expert).
def puts(value)
end
def puts
end
If you want to have another method with the same name but accepting different parameters, you need to use optional method parameters like this:
def value(value = "default value")
end

Ruby - reference of self in the class method

Went over this code on RubyMonk:
class Item
def initialize(item)
#item = item
end
def show
puts "The item name is: #{self}"
end
def to_s
"#{#item}"
end
end
Item.new("potion").show
The code passes but the use of the self variable is a bit ambiguous to me. You could've easily supplanted to_s with self in the show method and gotten the same results. Can somebody explain the difference between both interpolations and why/how self is used here?
Additionally, without the the method to_s, the code returns a proxy. What is the significance of defining the to_s here?
String interpolation implicitly calls the to_s method on an object. So, when you define the to_s method on Item, you are explicitly telling that object how to represent itself with respect to a string. self is used in this case because there is an implicit call to to_s within the interpolation of the Item object. Defining to_s explicitly tells Item how to render itself within a string.
For some additional details, check out this excellent post on explicit vs. implicit conversion methods.
While it's true that, in the example you provided, you could have just written "The item name is: #{#item}", that's not always the case.
As CDub points out, string interpolation implicitly calls to_s. If an object doesn't define a to_s method, Ruby returns an object reference in its place. In the example you gave us, writing "The item name is: #{#item}" only works because String implements to_s. If it didn't, or if you use Item to hold an object that doesn't implement to_s, you'll end up with the object's reference.
Now for the difference between using self and #item in your interpolation. self refers to the current object. When you interpolate self, you're calling the current object's to_s method. When you interpolate #item, you're calling #item's to_s method. That's not a problem in this simple case, but let's look at something a little bit more complex. Say we have two classes, Item and OtherItem (creative names, I know).
class Item
def initialize(item)
#item = item
end
def show
puts "The item name is: #{self}"
end
def to_s
"I'm a chunky monkey!"
end
end
class OtherItem
def initialize(item)
#otherItem = item
end
def to_s
"#{#otherItem}"
end
end
In this scenario, Item's show method uses self, so if we were to write:
Item.new(OtherItem.new("potion")).show
Ruby would call Item.show, which, in turn, would call self.to_s. Since self in that context is an Item, our output would be:
"The item name is: I'm a chunky monkey!"
If, however, we redefined Item.show like this:
def show
puts "The item name is: #{#item}"
end
And tried calling Item.new(OtherItem.new("potion")).show again, Item.show would call #item.to_s, and fill that in instead, so we'd get:
"The item name is: potion"

How can I maintain a variable via closure with define_method?

I am trying to create a macro "has_accessor_for", that accepts a symbol which is used as a parameter for an internal object that it uses (the Accessorizer object). The problem I am having is, when multiple modules do the has_accessors_for, the parameter (scope) ends up being stuck on the last value it was assigned to.
I added a puts prior to the define_method, which shows that it's scope1, and then scope2... But inside the define_method, it's scope2 always. I am looking for a way to basically encapsulate that variable, so that when it the first module calls has_accessor_for, anytime my_wut is called, it will be bound to scope1... and anytime my_bleah is called, it will be bound to scope2. But as I said, right now, both my_bleah and my_wut are bound to scope2-- If I change the order of the includes in MyModel, then they will both be bound to scope1.
class Accessorizer
def initialize(record, scope)
#record = record
#scope = scope
end
def value_for(key)
#record.send key
end
end
module Magic
def has_accessors_for(scope)
accessors = {}
puts "initial: #{scope}"
define_method :get_value_for do |key|
puts "inside method #{scope}"
accessor.value_for key
end
define_method :accessor do
accessors[:scope] ||= Accessorizer.new(self, scope)
end
end
end
module SomeAccessor
extend Magic
has_accessors_for :scope1
def my_wut
get_value_for :wut
end
end
module SomeOtherAccessor
extend Magic
has_accessors_for :scope2
def my_bleah
get_value_for :bleah
end
end
class MyModel
include SomeAccessor
include SomeOtherAccessor
attr_accessor :wut, :bleah
end
m = MyModel.new
m.wut = 'wut'
m.bleah = 'bleah'
m.my_bleah
m.my_wut
output:
initial: scope1
initial: scope2
inside method scope2
inside method scope2
Short answer: the problem is not with the closures.
Long answer:
define_method :get_value_for do |key|
puts "inside method #{scope}"
accessor.value_for key
end
On a given class there can only be one method called get_value_for - the second definition will overwrite the first.
It doesn't matter so much because you're call accessor in both cases, however that method suffers from the same problem - you define it twice and so the second definition overwrites the first and you end up with only one Accessorizer object.
I think you'll need to rethink your design here.

What does send() do in Ruby?

Can someone please tell me what the following snippet
obj.send("#{method_name}")
is and does?
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
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
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
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
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.
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
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"

How are variables bound to the body of a define_method?

While trying to brush up my Ruby skills I keep running across this case which I can't figure out an explanation for by just reading the API docs. An explanation would be greatly appreciated. Here's the example code:
for name in [ :new, :create, :destroy ]
define_method("test_#{name}") do
puts name
end
end
What I want/expect to happen is that the name variable will be bound to the block given to define_method and that when #test_new is called it will output "new". Instead each defined method outputs "destroy" -- the last value assigned to the name variable. What am I misunderstanding about define_method and its blocks? Thanks!
Blocks in Ruby are closures: the block you pass to define_method captures the variable name itself–not its value—so that it remains in scope whenever that block is called. That's the first piece of the puzzle.
The second piece is that the method defined by define_method is the block itself. Basically, it converts a Proc object (the block passed to it) into a Method object, and binds it to the receiver.
So what you end up with is a method that has captured (is closed over) the variable name, which by the time your loop completes is set to :destroy.
Addition: The for ... in construction actually creates a new local variable, which the corresponding [ ... ].each {|name| ... } construction would not do. That is, your for ... in loop is equivalent to the following (in Ruby 1.8 anyway):
name = nil
[ :new, :create, :destroy ].each do |name|
define_method("test_#{name}") do
puts name
end
end
name # => :destroy
for name in [ :new, :create, :destroy ]
local_name = name
define_method("test_#{local_name}") do
puts local_name
end
end
This method will behave as you expect. The reason for the confusion is that 'name' is not created once per iteration of the for loop. It is created once, and incremented. In addition, if I understand correctly, method definitions are not closures like other blocks. They retain variable visibility, but do not close over the current value of the variables.
The problem here is that for loop expressions do not create a new scope. The only things that create new scopes in Ruby are script bodies, module bodies, class bodies, method bodies and blocks.
If you actually look up the behavior of for loop expressions in the Draft ISO Ruby Specification, you will find that a for loop expression gets executed exactly like an each iterator except for the fact that it does not create a new scope.
No Rubyist would ever use a for loop, anyway: they would use an iterator instead, which does take a block and thus creates a new scope.
If you use an idiomatic iterator, everything works as expected:
class Object
%w[new create destroy].each do |name|
define_method "test_#{name}" do
puts name
end
end
end
require 'test/unit'
require 'stringio'
class TestDynamicMethods < Test::Unit::TestCase
def setup; #old_stdout, $> = $>, (#fake_logdest = StringIO.new) end
def teardown; $> = #old_stdout end
def test_that_the_test_create_method_prints_create
Object.new.test_create
assert_equal "create\n", #fake_logdest.string
end
def test_that_the_test_destroy_method_prints_destroy
Object.new.test_destroy
assert_equal "destroy\n", #fake_logdest.string
end
def test_that_the_test_new_method_prints_new
Object.new.test_new
assert_equal "new\n", #fake_logdest.string
end
end

Resources