How do I find where a ruby method is declared? - ruby-on-rails

I have a ruby method (deactivate!) that is on an activeRecord class. However, I can't seem to find where that method is declared.
There have been numerous developers on this project, so it could be anywhere. There is a deactivate! on an unrelated class, but it doesn't seem to get called.
Any ideas how to find all the superclasses for an instace, or where to find the code for deactivate!?

First question would be: is it an actual method? Does obj.method(:deactivate!) raise an error?
If it doesn't, then you can use Method#source_location(in Ruby 1.9 only, and backports can't support it):
obj.method(:deactivate!).source_location
If it does raise a NoMethodError, it is handled via method_missing. This makes it hard to track. If it accepts arguments, I'd try sending the wrong type and using the backtrace of the raised exception.
Are you using state_machine? If you have an event transition called :deactivate, the model will have the method #deactivate! created automatically.

When I need to find where a method is declared on some class, say 'Model', I do
Model.ancestors.find {|c| c.instance_methods(false).include? :deactivate! }
This searches the ancestor tree in the same order that ruby does for the first that has the method in instance_methods(false), which only includes non-inherited methods.
Note: before ruby 1.9, the methods were listed as strings not symbols, so it would be
Model.ancestors.find {|c| c.instance_methods(false).include?('deactivate!') }

As a first stab, try the Jump to Declaration feature of your IDE. Depending on how good your IDE's static type inference is, it should take you right there.
If that doesn't work, set a breakpoint on that call, fire up the debugger and step into the method.

Related

Rails - Call a method form a model or helper with an alias

I have a helper called GlobalHelper.
There are a couple of constants (e.g. GLOBAL_URL) and methods (e.g. self.get_url) in it.
I'm trying to make an alias or an equivalent of the macros in C (#define URL) to access the constant and methods from any of my views and controllers easily.
For now I have to do it like this:
GlobalHelper.get_url(GlobalHelper::URL_TYPE_PAGE, ["page1"])
And I want to get rid of the GlobalHelper everywhere with alias/macro to get a code like this instead:
aliasmethod(aliasconstant, ["page1"])
How can I do that?
ruby is not c, so you should take a look how to write ideomatic ruby code before trying to bring such concepts into your project.
since i don't know what exactly your get_url method does, i can only give limited recommendations.
one thing that would probably result in much nicer code is using symbols instead of constants:
GlobalHelper.get_url(:page, ["page1"])
when using a Module instead of Class you can include it and call the method directly:
include GlobalHelper
get_url(...)
these are just examples of what could be done. not saying that this is better than explicitly calling GlobalHelper directly. nothing wrong with that at all.

How to analyze quickly the code definition from a given code

Let's say I'm in a really huge project and am curious how this line works:
authorize! :read_pipeline_schedule, user_project
authorize! is supposed to be method name. Is it a private function in the class, or DSL provided from a parent block, or including, or declared in a gem? Or maybe none of them?
I was using grep to find internal code and Google for external code such as gems, however I guess it's useful if I can see the call stack of the method. puts caller is printing from the place where I am although it can not be used for analyzing the above case.
I'd like to know the best practice.
You can grep your code and installed gems and, if name is unique enough, you'll quickly locate its definition and associated usages. However, if a name is a common one, like call, then this approach is useless.
Ruby is a dynamic language and, as such, is a nightmare for static analysis. You may guess where this comes from, but you just can't know for sure.
The only way to know is runtime introspection. So, in your example, put a breakpoint right before that line
binding.pry
authorize! :read_pipeline_schedule, user_project
You'll drop out to pry shell. Now you can do
show-source authorize!
And see where exactly this method is defined.
The easiest way is to ask the method itself:
method_object = method(:authorize!)
method_object.owner
# => returns module in which the method resides
method_object.source_location
# => returns file name and line number of source code where the method is defined
Note that source_location may return nil for methods which have no Ruby source code, e.g. dynamically generated methods or methods implemented in the interpreter internals (C functions in YARV, Java methods in JRuby, etc.)
I like caller(0), caller_locations, and the ever-incredible show-source

Mocha on Ruby: Check a stubbed function called once

It should be straight forward, but it doesn't work for me.
I'm stubbing a function call, and I want to make sure it is called once, so I did:
MyClass.stubs(:record).returns(true).expect(:record).once
MyClass.run
but I keep getting:
expected exactly once, not yet invoked: allowed any number of times, invoked once: MyClass.record(any_parameters).record(any_parameters)
What am I doing wrong?
Are you trying to set expectations for 2 separate invocations on record?
stubs is just a syntactic sugar for expects, specifying that you expect an invocation zero or more times.
You could probably rewrite your example as such:
MyClass.expects(:record).returns(true)
Keep in mind that expects is by default implying the once part although you could add it if you think that it adds to your code's clarity.

See where a symbol is defined in irb

I work on a pretty large rails project at work. Sometimes I need to hunt down class / constant definitions. Is there some built-in method in Ruby to do this for me? Example:
irb> SOME_CONSTANT.__file__
=> /some/path/to/a/file
This isn't exactly what you're looking for, but methods do have a .source_location method on them. You can use this to find out where a class is actually implemented. (Since ruby lets you reopen classes, this could be in multiple places)
for example, given an instance of an object, i:
i.methods.map do |method_name|
method_obj = i.method(method_name)
file, line = method_obj.source_location
file #map down to the file name
end.uniq
will give you a list of all the files where i's methods are implemented.
This will work for classes that have at least 1 method implemented in ruby. It won't work for constants, though.
At the very beginning before any file is loaded, insert a line that defines the class/constant that you want to check as something other than a module. For example, suppose you have class or other kind of constant A within your code, and want to know where it is defined. Then, at the very beginning of the main file, write
A = nil
Then, when the program is run, whenever it first meets the definition of class/constant A, it will show something like
some_path_to_a_file:line_number in `some_method': A is not a class (TypeError)
or
some_path_to_a_file:line_number: warning: already initialized constant A
Then, some_path_to_a_file:line_number will be the location where A is defined.
If you're using Ruby 1.9.2, #YenTheFirst's answer is correct: call #source_location on a Method object.
If you're using Ruby 1.8.7, then #source_location doesn't exist (yet). You'll need something like this implementation of a method. (There's another one or two floating around, but I can't find the other one real quick).

How to DRY up a ruby conditional structure needed for Rails

I'm finding I often have to use a structure to avoid a Rails error of undefined method 'name' for nil:NilClass.
The structure looks like this:
if country.state
country.state.name
end
It seems like a classic case of repeating oneself with country.state appearing twice in one simple block. Is there any way to DRY this up?
Rails adds a try method to object that mimics object#send but does not raise an exception if the object returns nil.
I think the syntax is
country.try(:state).name
Well not really. One option is to install the andand gem, but introducing a dependency for this may be a little much.
Other than using the slightly more concise syntax of:
country.state.name unless country.state.nil?
I don't think there's a DRY way to do this with the information given. I would argue that if you can't be sure whether country.state is nil or not, you may want to look at the code responsible for setting that value and determine whether that's a normal case or whether a validator upstream should be catching that.

Resources