Ruby: why does puts call to_ary? - ruby

I'm learning metaprogramming in Ruby and am just trying out defining missing methods via method_missing and define_method. I'm getting some unexpected behaviour and am wondering if anyone can explain this. Here is my class:
class X
def method_missing(m, *args, &block)
puts "method #{m} not found. Defining it."
self.class.send :define_method, m do
puts "hi from method #{m}"
end
puts "defined method #{m}"
end
end
Now, this code:
x = X.new
x.some_method
puts
x.some_method
puts
puts x
Produces the output:
method some_method not found. Defining it.
defined method some_method
hi from method some_method
method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>
What I don't get is the last part: why is Ruby calling to_ary in a call to puts? Why would Ruby try to convert my object into an array just to print it?
I've Googled around and found these related links:
http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
These also talk about method_missing and to_ary gotchas, but not specifically about why puts would call to_ary.
I should also mention that the behaviour does not change when I define a to_s, e.g.
def to_s
"I'm an instance of X"
end
The output of "puts x" is then:
method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X

puts is a synonym for $stdout.puts. $stdout is an IO class, so look at the documentation for IO.puts:
Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line.
This mean that puts method is intended to write several lines of output. Thus it tries to call to_ary method on an object and if to_ary is defined, then prints each element of the returned Array on a new line, else puts calls to_s method.
to_ary internal usage is really not well documented in the Ruby documentation (Matz points this out in his The Ruby Programming Language book).
Methods print and p on the other hand don't call to_ary, only to_s.
Sidenote: Interesting, that to_ary must return real Array object, not an object defining each method or something else:
class Test
def to_ary
10.downto(1)
end
end
puts Test.new
#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator)
# from (irb):28:in `puts'
# from (irb):28:in `puts'
# from (irb):28

Related

Default method for Ruby class

Is there a way to specify a class method such that when the object is used as if it were a function, that method is called? Something like this:
class MyClass
def some_magic_method(*args)
# stuff happens
end
end
# create object
myob = MyClass.new
# implicitly call some_magic_method
myob 'x'
You could write a command class and make use of a ruby shortcut
class MyClass
def self.call(text)
puts text
end
end
MyClass.('x')
Here MyClass.() defaults to the call class method.
As mentioned by #CarySwoveland in the comments you can use method_missing. A basic example is as follows:
class MyClass
def method_missing(method_name, *args)
if method_name.match?(/[xyz]/)
send(:magic_method, args.first)
else
super
end
end
def magic_method(a)
a = 'none' if a.nil?
"xyz-magic method; argument(s): #{a}"
end
end
myob = MyClass.new
myob.x #=> "xyz-magic method; argument(s): none"
myob.x(1) #=> "xyz-magic method; argument(s): 1"
myob.y #=> "xyz-magic method; argument(s): none"
myob.z #=> "xyz-magic method; argument(s): none"
This captures all methods named x, y or z. Our else branch sends all other undefined methods to the original method_missing:
myob.v #=> test.rb:7:in `method_missing': undefined method `v' for
#<MyClass:0x000000021914f8> (NoMethodError)
#from test.rb:25:in `<main>'
What methods you capture is up to you and is determined by the regex /[xyz]/ in this case.
Key methods: BasicObject#method_missing, Object#send. For further info check out this question, read Eloquent Ruby by Russ Olsen (from which this answer references)
You meant to invoke some class' instance method when the object is invoked as a function. This is already supported: instance method call gets called when you "invoke" an object via the functional invocation method () (for more details, see here How do I reference a function in Ruby?).
class C
def call(x)
puts "Called with #{x}"
end
end
obj = C.new
obj.(88) # Called with 88 => nil
obj (88) # NoMethodError: undefined method `obj' for main:Object
If you do want the latter syntax, a horrible trick is the following one (but works only at the top-level, unless you carry along the bindings):
module Kernel
def method_missing(name,*args)
obj = begin
TOPLEVEL_BINDING.local_variable_get(name)
rescue
nil
end
return super if obj.nil?
obj.send :call, *args
end
end
obj = C.new
obj 88 # Called with OK => nil
This example also wants to communicate that you should always keep in mind
who is the receiver of your method calls, and what syntaxes are available for calling methods (especially when you leave out dots and parentheses).
class D
def obj; C.new end
def f
#(obj) 88 # BAD
(obj).(88)
#obj() 88 # BAD
obj().(88)
end
end
The point is that you do not actually have functions, but methods that get called on objects. If you omit the receiver of a method call, the receiver defaults to self, the current object. But in your example, myob does not appear as an explicit receiver (since there is not following dot as in myob.), hence the current object is looked for a method myob.

What does this '&' mean in ruby? [duplicate]

This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 6 years ago.
https://github.com/activeadmin/activeadmin/blob/1c85c5654a2ce1d43d4c64d98b928ff133d46406/lib/active_admin.rb#L95
What does the & (prefixed to ActiveAdmin) in this code mean?
def before_load(&block)
ActiveSupport::Notifications.subscribe(
ActiveAdmin::Application::BeforeLoadEvent,
&ActiveAdmin::Event.wrap_block_for_active_support_notifications(block)
)
end
In functions & 'splats' blocks (also calls to_proc on it), similar to * for regular parameters.
That is somewhat equivalent to
def before_load(&block) # this binds provided block into block variable
ActiveSupport::Notifications.subscribe(ActiveAdmin::Application::BeforeLoadEvent){|some_params_maybe|
ActiveAdmin::Event.wrap_block_for_active_support_notifications(block).call(some_params_maybe)
}
end
usually & calls to_proc method of the object, such as gets.split.map(&:to_i) is used to read line of integers, which is the same as map { |e| e.to_i }
In method arguments, it appears in the last arguments, meaning the code block you passed to, check the following code for details.
def g
puts yield "g"
end
def h(block)
puts block.call "h"
end
def f(&block)
puts block.class # => Proc
puts block.call "f" # => hello f
g &block # => hello g passed as code block
h block # => hello h passed as proc
end
f { |x| "hello " + x }
Following article provides a good explanation on the use of '&' in Ruby:
The Implicit Block
Methods in Ruby can take arguments in all sorts of interesting ways. One case that’s especially interesting is when a Ruby method takes a block.
In fact, all Ruby methods can implicitly take a block, without needing to specify this in the parameter list or having to use the block within the method body e.g.:
def hello
end
hello do
puts "hello"
end
This will execute without any trouble but nothing will be printed out as we’re not executing the block that we’re passing in. We can – of course – easily execute the block by yielding to it:
def hello
yield if block_given?
end
hello do
puts "hello"
end
This time we get some output:
hello
We yielded to the block inside the method, but the fact that the method takes a block is still implicit.
It gets even more interesting since Ruby allows to pass any object to a method and have the method attempt to use this object as its block. If we put an ampersand in front of the last parameter to a method, Ruby will try to treat this parameter as the method’s block. If the parameter is already a Proc object, Ruby will simply associate it with the method as its block.
def hello
yield if block_given?
end
blah = -> {puts "lambda"}
hello(&blah)
lambda
If the parameter is not a Proc, Ruby will try to convert it into one (by calling to_proc on it) before associating it with the method as its block.
def hello
yield if block_given?
end
class FooBar
def to_proc
-> {puts 'converted lambda'}
end
end
hello(&FooBar.new)
converted lambda
All of this seems pretty clear, but what if I want to take a block that was associated with a method and pass it to another method? We need a way to refer to our block.
The Explicit Block
When we write our method definition, we can explicitly state that we expect this method to possibly take a block. Confusingly, Ruby uses the ampersand for this as well:
def hello(&block)
yield if block_given?
end
hello do
puts "hello"
end
Defining our method this way, gives us a name by which we can refer to our block within the method body. And since our block is a Proc object, instead of yielding to it, we can call it:
def hello(&block)
block.call if block_given?
end
hello do
puts "hello"
end
I prefer block.call instead of yield, it makes things clearer. Of course, when we define our method we don’t have to use the name ‘block’, we can do:
def hello(&foo)
foo.call if block_given?
end
hello do
puts "hello"
end
Having said that; ‘block’ is a good convention.

Naive aspect implementation in ruby

I am trying to make a simplistic implementation of AOP in ruby. I was able to implement before and after advices, I got stuck with around advice.
This is the target class that is going to be advised:
class MyClass
def method
puts "running method"
end
end
This is the Aspect class to instantiate objects capable of making advices:
class Aspect
def advise(class_name, method, type, &block)
class_name.send(:alias_method, :proceed, :method)
class_name.send(:define_method, :method) do
case type
when :before
yield
proceed
when :after
proceed
yield
when :around
yield(proceed) # * proceed is the old version of the method
end
end
end
end
(*) Yield should execute the block around MyClass#proceed on the current object when method is invoked.
Creating the target and the aspect:
mc = MyClass.new
a = Aspect.new()
Invoking the method without advising it:
puts mc.method
Advising MyClass#method with around:
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
puts mc.method
(*) I am not being able to pass something to identify the call of proceed, that is the invocation of the old method without the advice.
The output should be:
First
running method
Last
In Ruby, a method call looks like this:
receiver.method(arguments)
Or, you can leave off the receiver if the receiver is self.
So, to call a method named proceed on some receiver, you would write
receiver.proceed
However, in your implementation, you don't keep track of what the receiver should be, so since you don't know the receiver, you simply cannot call the method.
Note that there are lots of other problems with your approach as well. For example, if you advise multiple methods, you will alias them all to the same method, overwriting each other.
I believe there are two things going wrong here.
This section of code
when :around
yield(proceed) # * proceed is the old version of the method
end
Calls the block given to advise providing the output of the proceed method as an argument.
So your output probably looks something like:
running method
First
Last
This block
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
Just evaluates the argument given as proceed. If a method is given it does not call it. So taking problem 1 into consideration in your case the original definition of method (aliased to proceed) returns nil (output of return) which will be passed as the value to the proceed argument in the block when yielded. the block ends up evaluating to something like
puts "First"
nil
puts "Last"
mc.method is called.
To address the second part, you may want to consider using send. Because the inner workings of your aspect may not be known to your code that calls it. It may change over time, so what ever calls Aspect.advise shouldn't make assumptions that the original method will still be accessible. Instead, it should take an argument (the new method name) and send it to the object. Making the block passed to advise:
a.advise(MyClass, :method, :around) do |aliased_method_name|
puts "First"
send(aliased_method_name)
puts "Last"
end
And adjusting the around item added to your class when advise is called to the following:
when :around
yield(:proceed) # * proceed is the old version of the method
end
If you do both of these things, your around section will calls the provided block, using the symbol for the new alias for the overridden method.
N.B.: This approach won't work for methods that require any arguments.
This is what I did. In the definition of Aspect#advise now I use a Proc, like this:
when :around
yield Proc.new { proceed }
end
And when calling the method to advise MyClass#method with :around parameter I use this:
a.advise(MyClass, :method, :around) do |original|
puts "First"
original.call
puts "Last"
end
I got:
First
running method
Last
Here's the fixed version that will work for arguments, and avoid clobbering.
class Aspect
##count = 0
def self.advise(class_name, method, type=nil, &block)
old_method = :"__aspect_#{method}_#{##count += 1}"
class_name.send(:alias_method, old_method, method)
class_name.send(:define_method, method) do |*args, &callblock|
case type
when :before
yield
send(old_method, *args, &callblock)
when :after
send(old_method, *args, &callblock)
yield
when :around, nil
yield lambda {
send(old_method, *args, &callblock)
}
end
end
end
end
class Foo
def foo(what)
puts "Hello, #{what}!"
end
end
Aspect.advise(Foo, :foo) do |y|
puts "before around"
y.yield
puts "after around"
end
Aspect.advise(Foo, :foo, :before) do
puts "before"
end
Aspect.advise(Foo, :foo, :after) do
puts "after"
end
Foo.new.foo("world")
# before
# before around
# Hello, world!
# after around
# after

Ruby automatically calls to_s method when object is created

class A
def initialize(string, number)
#string = string
#number = number
end
def to_s
"In to_s:\n #{#string}, #{#number}\n"
end
def to_a
"In to_a:\n #{#string}, #{#number}\n"
end
end
puts a = A.new("hello world", 5)
output is
In to_s:
hello world, 5
How is the to_s method called automatically?
Why isn't another method called automatically such as to_a?
Since I did not write puts in the to_s method, why is output printed.
You're sending it to puts, which will try to render the object as a string using to_s.
If you changed your last line to: puts A.new("hello world", 5).to_a, it would instead call to_s on the returned Array and A's to_s would not be called.
puts generally prints the result of applying to_s on an object
read more
here
In addition to #numbers1311407 answer
whenever you try any code in irb
it calls to_s implicitly.
and as #numbers1311407 answer explains.
puts call to_s implicitly

Ruby rescue-like syntax

I would like to implement something with a rescue-like syntax.
begin
raise "Failed"
rescue Exception => e
puts e
end
This works, and e is assigned to the corresponding value. But used in a method, this will raise an exception saying that e is undefined. In other words, how can rescue assigns something to e this way without throwing an undefined error?
class MyClass
def to_s
"hello"
end
end
def my_method
puts e.to_s
end
my_method(MyClass => e)
#=> undefined local variable or method `e' for main:Object
Perhaps what you are looking for is:
class MyClass
def self.hello
puts "This is a class method."
end
def bye
puts "This is an instance method."
end
end
def my_method(params)
klass = params[:class]
puts klass.hello # Call a class method
inst = klass.new # Create an instance
puts inst.bye # Call an instance method
end
my_method(:class => MyClass)
Three things to note:
Although the rescue syntax and the "named parameter" syntax look the same, all they have in common is the => operator. In the first case, you are telling to rescue the Exception "into" the variable e, effectively storing it in that variable. In the second case, you are telling Ruby to collect all parameters passed to the method and store them in a hash, using the supplied key/value pairs. Effectively, you are storing MyClass in the params hash, under the key :class.
In your above example, the to_s definition will not be callable on MyClass itself, because you defined it as an instance method. Instance methods are only available when you create an "instance" of the class with inst = MyClass.new. Then, you can call inst.to_s. Think of the class as an abstract "type" of thing, and of the instance as a concrete thing of that type. If you want the method to be available on the class, not the instances, you need to prefix it with self. I have illustrated the two different syntaxes above.
Again in your example, you are using def MyClass, which Ruby will interpret as "define a method with the name MyClass". If you want to define a class, you need to use class MyClass instead.
Hope this clarifies things a bit.

Resources