How to get the method which is handling a method call - ruby

I have an app which uses Ruby 1.8. 6 in a Rails 2.2.2 app.
It has many definitions of the method url_for in various parts of the code base (classes, modules etc).
When debugging, I'd like to see in the log which method handles a specific call to url_for. E.g.,
foo = "bar"
baz = url_for(foo)
#would like to log the location of the url_for method which actually
#handled the call, here.
It's fine if the only way to do this is to raise an exception inside url_for and look at the stack trace, eg
baz = url_for(something_which_wont_raise_here_but_will_raise_inside_the_url_for_method)
To go further, what if the method is being called on an instance of a (heavily patched) class:
foo = #baz.bar("qux")
EDIT: I didn't specify that it was Ruby 1.8.6 in my question, and it looks like i can't use the .source_location method. Very sorry about not specifying the version earlier! Does anyone have any other ideas?

It sounds like you want to do
method(:url_for)
to get a Method object, and then use source_location to find out where it was defined

You can use method and source_location methods chain to get the location of the method definition.
For example:

If you do:
method(:url_for).owner
It will give you the module/class in which the method is defined. This should be enough to let you identify the method (unless the same method is redundantly defined in the same module multiple times). You can then look at the definition of url_for in that module/class.

Related

Is it possible to use methods defined in refinements in Sinatra views?

I have a class defined in a gem to which I am adding some methods via refinements (in Ruby 2.3.0). This class turns up in some Sinatra views (haml).
When I refer to these extra methods in a helper, there is not a problem. But in the view, I get an Undefined Method error.
Am I missing a trick, or is it that the using ... statement would need to go somewhere I just can't get to?
(Workaround: I can define helper methods to return the method on the object. But if I wanted to do that then I wouldn't have used refinements...)
The scope of refinement is determined lexically. Unless you rewrite the method inside haml that calls that method so that it becomes within the scope of the using command, you cannot use refinements. But I guess haml is internally using eval or something like that to evaluate the code you write in a haml file. In that case, it is impossible.

The un-googleable ruby's method method

There is apparently a ruby method called method and I can't find any docs or examples on it so far. Could someone please post a few useful annotated examples?
there is docs about that method in ruby api documentation
http://www.ruby-doc.org/core-1.9.3/Object.html#method-i-method
there is also method_defined? which could be also useful if you need method it self
http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-method_defined-3F
I've mostly used it to look up source location of a specific method. (example run in irb on ruby-1.9.2-p290)
class Thing
def foo
end
end
Thing.new.method(:foo).source_location
=> ["(irb)", 2]
Thing.new.method(:foo).owner
=> Thing
It's part of Object and is documented here:
http://www.ruby-doc.org/core-1.9.3/Object.html#method-i-method
The class Method is documented here, and gives some examples:
http://www.ruby-doc.org/core-1.9.3/Method.html
One of the best uses of the bound method object returned by Object#method is for blocks. The return result of that method can be converted to a block argument. Imagine you have:
class Converter
def convert_item(item)
item.transmorph
end
end
Then you could do this
c = Converter.new
elements.map(&c.method(:convert_item))
or when you are within the convertor
elements.map(&method(:convert_item))
which IMO is more elegant than the explicit block creation syntax. It also supports to_proc so you can do this
some_object.callback = my_handler.method(:activate).to_proc
and then in some_object you can do
#callback.call(data)
Object#method, is this what you are looking for?
http://www.ruby-doc.org/core-1.9.3/Object.html#method-i-method

How do I refer to a submodule's "full path" in ruby?

In my app, I have
VeryUniqueModule::Foo
# and…
VeryUniqueModule::Bar
Foo and Bar are each for a different service. Part of my app has to dynamically figure out which module to refer to, which it capably does like so:
def service_api
# #relevant_object.service is a string that is either 'Foo' or 'Bar'
VeryUniqueModule.const_get(#relevant_object.service)
end
More on this later.
I just updated a library, and it now has its own top-level Foo class (which is bad design on its part). Now when I try to invoke #relevant_object.service_api::A_CONSTANT, my app complains that the library's Foo does not have A_CONSTANT.
Back to service_api above -- I thought that const_get was returning the class itself. In fact I know it is. If I fire it up in irb everything is as expected -- the return value is the class itself, and I can invoke things on the class. So…
How is it possible that there's a namespace conflict in the first place? I'm looking for A_CONSTANT on the Class object returned by service_api, not on a string that I'm evaling or something funky like that -- there shouldn't be any namespace issues, I'm referring directly to an object!
If this is indeed a problem, how can I fix service_api so that it will return the, erm, "full path"?
You might try this:
VeryUniqueModule.const_get('::VeryUniqueModule::' + #relevant_object.service)
And if that doesn't work, you could try bypassing service_api and doing this wherever you need A_CONSTANT:
Object.const_get('::VeryUniqueModule::' + #relevant_object.service + '::A_CONSTANT')
Note the :: before VeryUniqueModule. I don't think it's strictly necessary in this case, but it could be useful in that it guarantees Ruby will look for VeryUniqueModule in the global namespace and not inside some other module.

Is this ruby metaprogramming abuse?

I am new to Ruby, and have a gem that I am making to interact with a JSONRPC API and basically all calls and responses are similar enough, that every API call can be handled with one function, like:
Module::api_command('APINamespace.NamespaceMethod')
but I would like to also (for convenience sake) be able to do:
Module::APINamespace.NamespaceMethod
Is there any reason not to do this by using Module.const_missing to return a dummy class that has a method_missing which will allow passing the call from Module::APINamespace.NamespaceMethod to Module::api_command('APINamespace.NamespaceMethod')
Is there a more elegant or civilized way to do this?
Yes, I'm sorry, but to my mind that hack is ridiculous. :)
First of all, i'm assuming that your api_command method is actually invoking methods on the APINamespace module, as implied by this line: Module::api_command('APINamespace.NamespaceMethod')
Given the above, why not just set a constant equal to APINamespace in your module?
MyModule::APINamespace = ::APINamespace
MyModule::APINamespace.NamespaceMethod()
UPDATE:
I'm still not entirely understanding your situation, but perhaps this:
module MyModule
def self.const_missing(c)
Object.const_get(c)
end
end
Now you can invoke any top-level constant as if it was defined on your module; say there was a module called StrangeAPI at top-level, if you use the hack above, you can now invoke its methods as follows:
MyModule::StrangeAPI.Blah()
Is this what you want?

method_missing in "Programming Ruby" over my head

method_missing
*obj.method_missing( symbol h , args i ) → other_obj
Invoked by Ruby when obj is sent a
message it cannot handle. symbol is
the symbol for the method called, and
args are any arguments that were
passed to it. The example below
creates a class Roman, which responds
to methods with names consisting of
roman numerals, returning the
corresponding integer values. A more
typical use of method_missing is to
implement proxies, delegators, and
forwarders.
class Roman
def roman_to_int(str)
# ...
end
def method_missing(method_id)
str = method_id.id2name
roman_to_int(str)
end
end
r = Roman.new
r.iv ! 4
r.xxiii ! 23
r.mm ! 2000
I just heard about method-missing and went to find out more in Programming Ruby but the above explanation quoted from the book is over my head. Does anyone have an easier explanation? More specifically, is method-missing only used by the interpreter or is there ever a need to call it directly in a program (assuming I'm just writing web apps, as opposed to writing code for NASA)?
It's probably best to not think of ruby as having methods. When you call a ruby "method" you are actually sending a message to that instance (or class) and if you have defined a handler for the message, it is used to process and return a value.
So method_missing is a special definition that gets called whenever ruby cannot find an apropriate handler. You could also think of it like a * method.
Ruby doesn't have any type enforcement, and likewise doesn't do any checking as to what methods an object has when the script is first parsed, because this can be dynamically changed as the application runs.
What method_missing does, is let you intercept and handle calls to methods that don't exist for a given object. This provides the under-the-hood power behind pretty much every DSL (domain-specific language) written in Ruby.
In the case of the example, every one of 'r.iv', 'r.mm', and so on is actually a method call to the Roman object. Of course, it doesn't have an 'iv' or an 'mm' method, so instead control is passed to method_missing, which gets the name of the method that was called, as well as whatever arguments were passed.
method_missing then converts the method name from a symbol to a string, and parses it as a Roman number, returning the output as an integer.
It's basically a catch-all for messages that don't match up to any methods. It's used extensively in active record for dynamic finders. It's what lets you write something like this:
SomeModel.find_by_name_and_number(a_name, a_number)
The Model doesn't contain code for that find_by, so method_missing is called which looks at is says - I recognize that format, and carries it out. If it doesn't, then you get a method not found error.
In the Roman example you provide it illustrates how you can extend the functionality of a class without explicitly defining methods.
r.iv is not a method so method_missing catches it and calls roman_to_int on the method id "iv"
It's also useful when you want to handle unrecognized methods elsewhere, like proxies, delegators, and forwarders, as the documentation states.
You do not call "method_missing" (the interpreter calls it). Rather, you define it (override it) for a class which you want to make to be more flexible. As mentioned in other comments, the interpreter will call your version of method_missing when the class (or instance) does not ("explicitly"?) define the requested method. This gives you a chance to make something up, based on the ersatz method/message name.
Have you ever done any "reflection" programming in Java? Using this method would be as if the class to be accessed via reflection could look at the string (excuse me, "symbol") of the method name if a no-such-method exception was thrown, and then make something up as that method's implementation on the fly.
Dynamic programming is kind of a "one-up" on reflection.
Since you mention web apps I'll guess that you are familiar with Ruby on Rails. A good example of how method_missing can be used is the different find_by_<whatever> methods that's available. None of those methods actually exist! They are synthesized during run time. All of this magic happens because of how ruby intercepts invocations of non-existing methods.
You can read more about that and other uses of method_missing here.
ActiveRecord uses method_missing to define find_by methods. But since, method_missing is basically a last resort method, this can be a serious performance bottleneck. What I discovered is that ActiveRecord does some further awesome metaprogramming by defining the new finder method as a class method !! Thus, any further calls to the same finder method would not hit the method_missing because it is now a class method. For details about the actual code snippet from base.rb, click here.

Resources