When does Ruby know that a method exists? - ruby

One question that ran through my mind was how does the Ruby interpreter know that a method exists on a object if the definition is yet to be interpreted? Like, wouldn't it matter whether you define the method first than use it, rather than use it then define it?

It doesn't know, and it doesn't care - until execution. When a method call statement is executed, the interpreter looks to see if the class (object, not code!) has the named function. If it does not, it looks up the ancestor tree. If it does not find any, it calls the method_missing method. If that is not defined, you get your error.
If your function call does not get executed, you will not get any errors.

The interpreter doesn't know about undefined methods ahead of time, for example:
o = Object.new
o.foo # => Raises NoMethodError.
class Object
def foo
puts "Foo!"
end
end
o.foo # => prints "Foo!", since the method is defined.
However, Ruby has a neat feature called method_missing which let's the receiver of a method call take the method name and arguments as separate arguments and handle accordingly as long as no defined method already handles the call.
def o.method_missing(sym, *args)
puts "OK: #{sym}(#{args.inspect})"
# Do something depending on the value of 'sym' and args...
end
o.bar(1, 2, 3) #=> OK: bar(1, 2, 3)
"Method missing" is used by things like active record find methods and other places where it could make sense to have "dynamically defined" functions.

The problem is, the interpreter tried to find it when you use it, and since it won't be there, it may fail.
In ( some ) compiled languages, it doesn't matter, because while compiling, the compiler may say "I'll look for this on a second pass" but I don't think this is the case with Ruby.

Related

Evaluate argument at call site

This is somewhat of an esoteric situation that I am not even sure is possible. I have a method that is called by a lot of methods:
def called_by_a_lot_of_methods
# Work
end
I am introducing a feature in which the called_by_a_lot_of_methods will do stuff depends on the caller method, and my current solution is to change all these methods to pass their names when calling called_by_a_lot_of_methods:
def some_method
called_by_a_lot_of_methods(method_name: some_method)
end
Or, alternatively:
def some_method
called_by_a_lot_of_methods(method_name: __method__)
end
But this is becoming tedious, and I was wondering if I can give the method_name parameter a default value:
def called_by_a_lot_of_methods(method_name: __method__)
# Work
end
This does not work because __method__ evaluates immediately giving called_by_a_lot_of_methods, which is obviously not what I want. The question then is, is there a way in Ruby to defer the evaluation of the argument until a useful time when I know it should give the correct result, which is the outer caller method? Thus saving me from having to pass the argument everywhere?

How can Ruby transform a method into a symbol as argument?

The respond_to? method takes as argument, a method to be checked, but as a symbol.
Why as symbol? And how does ruby convert the method into a symbol?
There's no magic. Methods are attached to objects by their name, which is a symbol. In fact, Math.sin(2) is basically a shorthand for Math.send(:sin, 2) ("send message :sin to Math object, with parameter 2"). You can also access methods itself: Math.method(:sin) will give you the associated Method object (whose name, Math.method(:sin).name, is of course :sin), and Math.methods will list all implemented methods' names. Math.respond_to?(:sin) can basically be rewritten as Math.methods.include?(:sin) (this is simplified, as it ignores respond_to_missing?, but... close enough for this discussion).
Think of it this way. You go to a friend's house, and their mother answers the door. You ask, "Hello, is Tim here?" You don't need to actually drag your friend to the door and ask "Hello, is this individual here?" The name works just as well. :)
EDIT:
Hmmm that's confusing right now for me. What does named mean exactly? I mean maybe with a little example. I call it with array.each. When does the "name" :each come into play then?
I am not sure how to explain it better. Methods have names, just like people have names. When you say array.each, it is sending the message :each to the object that is contained in the variable array, pretty much exactly what array.send(:each) would do. Methods are pieces of code attached to objects via their names: when an object receives a message, it runs the piece of code that is associated with that message.
Specifically, in the standard Ruby implementation, objects of class Array, when they receive the message :each, will invoke the C function rb_ary_each that is defined in the Ruby source code, and linked to the message :each using rb_define_method(rb_cArray, "each", rb_ary_each, 0) (also in the Ruby source code).
Inside Ruby, there are basically two equivalent ways to define methods. These two are equivalent:
class Foo
def bar
puts "hello"
end
end
class Foo
define_method(:bar) do
puts "hello"
end
end
Both of them do the same thing: associate the message :bar with the piece of code do puts "hello" end. When :bar is received by a Foo (whether through Foo.send(:bar) or by Foo.bar), this piece of code is run.

Ruby Koans: blocks and arguments (test_blocks_can_take_arguments)

Ruby Koans has the following exercise in about_blocks.rb:
def method_with_block_arguments
yield("Jim")
end
def test_blocks_can_take_arguments
method_with_block_arguments do |argument|
assert_equal __, argument
end
end
I know the answer is assert_equal "Jim", argument, but I'm struggling to understand what is happening. Specifically:
Is argument or assert_equal... the block?
What is yield doing given that method_with_block_arguments returns "Jim" without yield?
I think some of the above commenters are correct in saying that you currently don't have a very deep understanding of Ruby, but don't let that discourage you. It just takes time to learn. When I was first learning Ruby, the concept of blocks and their syntax did take some time to wrap my head around. Once you get it the syntax is very simple, but you until you reach that point...
Anywho, this is my attempt to help you out. :)
argument is a block variable. All the stuff between do and end is the block. assert_equal is just a regular method call, nothing to do with blocks.
What yield does is the key to understanding how blocks work. What yield does it that it "yields" control to the calling function. You may think of it as a callback. When you say "yield" in the middle of a function, you are essentially saying "in the middle of this function, I want to allow someone else to plug in their code and make decisions about what should happen." If you use yield with no arguments, no data from your method gets passed back to the caller.
In essence, yield is a way of "yielding" control to somebody else, in this case the caller of your function.
When you call yield with one or more arguments, you are passing data from the your function back up to the caller. So when you say yield("Jim") you are handing the String "Jim" back to whoever calls method_with_block_arguments.
Lastly, you have to understand that in Ruby, methods always return the result of whatever was the last expression in a particular method. That's why you usually don't need an explicit return statement.
For instance, this method will return 42.
def foo
42
end
That's because 42 is a valid expression in Ruby. It's just an identity, but it's valid Ruby, so Ruby just says "okay, you said 42 and that's the last thing in this method declaration. So when people call 'foo' they get 42 back".
I hope this helps. I think at this point you should assume that you're still pretty early on in terms of your Ruby learning, but you're on the right track investigating blocks. Once you get them you'll understand one of the most powerful parts of Ruby.
Is argument or assert_equal... the block?
No, neither argument nor assert_equal is a block, argument is the variable and anything between do and end is the block. assert_equal is a normal method call.
What is yield doing given that method_with_block_arguments returns "Jim" without yield?
Yield is what makes it special. It calls the block (ie. everything between do and end) and executes it. "Jim" is the argument to the block.
Here is a gist that I copied from Paul while I was learning ruby. That should help in learning about closures in ruby.

DSL block without argument in ruby

I'm writing a simple dsl in ruby. Few weeks ago I stumbled upon some blog post, which show how to transform code like:
some_method argument do |book|
book.some_method_on_book
book.some_other_method_on_book :with => argument
end
into cleaner code:
some_method argument do
some_method_on_book
some_other_method_on_book :with => argument
end
I can't remember how to do this and I'm not sure about downsides but cleaner syntax is tempting. Does anyone have a clue about this transformation?
def some_method argument, &blk
#...
book.instance_eval &blk
#...
end
UPDATE: However, that omits book but don't let you use the argument. To use it transparently you must transport it someway. I suggest to do it on book itself:
class Book
attr_accessor :argument
end
def some_method argument, &blk
#...
book.argument = argument
book.instance_eval &blk
#...
end
some_method 'argument' do
some_method_on_book
some_other_method_on_book argument
end
Take a look at this article http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation — there is an overview of the method (specifically stated in the context of its downsides and possible solution to them), plus there're several useful links for further reading.
Basically, it's about using instance_eval to execute the block in the desirable context.
Speaking about downside of this technique:
So what's the problem with it? Well, the problem is that blocks are
generally closures. And you expect them to actually be full closures.
And it's not obvious from the point where you write the block that
that block might not be a full closure. That's what happens when you
use instance_eval: you reset the self of that block into something
else - this means that the block is still a closure over all local
variables outside the block, but NOT for method calls. I don't even
know if constant lookup is changed or not.
Using instance_eval changes the rules for the language in a way that
is not obvious when reading a block. You need to think an extra step
to figure out exactly why a method call that you can lexically see
around the block can actually not be called from inside of the block.
Check out the docile gem. It takes care of all the sharp edges, making this very easy for you.

Question about inheritance in Ruby

I've been attempting to teach myself Ruby over the past while and I've been trying to get something like the following to work but I'm getting the following error...
file.rb:44:infunc': undefined local variable or method number' #<classname:0xb75d7840 #array=[]> (NameError)
The code that's giving me this error is...
class A
def func
file = File.new("file", "r")
file.each_line {|line| #numbers << line.chomp.to_i}
#number = #array[0]
end
end
class B < A
def func
super number
puts number
end
end
Could someone please tell me what I'm doing wrong?
edit// Just a clarification that I want number in class B to inherit the value of #number in class A.
Just like it's telling you, you're calling super and puts with an the argument number — but this number (whatever it's supposed to be) hasn't been defined anywhere. Define number to be something meaningful and the code will almost work. Almost.
The other mistake, which you'll discover after you've fixed that one, is that you're calling super number, which calls A's func method with this mysterious number object as the argument — but A#func doesn't take any arguments.
You forgot the '#' symbol to reference the instance level variable.
It is a really bad design anyway. What if there are no lines in 'file'? #numbers is never initialized. You also wipe out #number completely on the next line with a variable (#array) that has never been defined. Stop trying to fit everything in as few lines as possible and properly initialize your variables.
EDIT: Also, as Chuck noticed, you are passing an argument to a method that takes no arguments.
Your problem is that while you use the instance variable #number, calling super number (which isn't what you want, as this calls the superclass version of whatever method you're in, passing number as an argument) and puts number look up the method number. If you really do want to just look up the instance variable, you just need #number; if you want to define such a method, put one of the following lines in your class:
class A
attr_accessor :number # Define reader (number) and writer (number=)
attr_reader :number # Define only a reader
attr_writer :number # Define only a writer; won't be useful here
# ...
end
And as Ed Swangren said, you ought to clean up A: initialize variables in initialize, make sure you define everything before using it, etc.
Edit 1: Corrected the description of the behavior of super.

Resources