Access Pry's show-source method from Ruby file - ruby-on-rails

Is it possible to access Pry's show-source method from within a Ruby file? If so, how is this done?
For example, if I had this file:
# testing.rb
require 'pry'
def testing
puts 'hi'
end
puts show-source testing
And ran ruby testing.rb, I'd like the output:
Owner: testing.rb
Visibility: public
Number of lines: 3
def testing
puts 'hi'
end
To explain the rationale for this, I have a test stubbing a method, though the original seems to be getting called on occassion and I thought it would be handy to output the source of the call to see where it's coming from. I know there are simpler ways of doing this, though started down this rabbit hole and am interested in seeing whether this can be done :)
Running the slightly head-twisting show-source show-source shows a few methods within the Pry::Command::ShowSource class, which inherits from Pry::Command::ShowInfo.
Pry::Command::ShowSource shows three methods: options, process and content_for, though I've not been able to successfully call any.
My best assumption is the content_for method handles this, working with a code object assigned from the parent class (i.e. Pry::CodeObject.lookup(obj_name, _pry_, :super => opts[:super])), though I've not been able to crack this.
Anyone have any ideas or examples of doing this?

Ruby has the build-in method Method#source_location which can be used to find the location of the source. The method_source gem builds upon this by extracting the source based upon the source location. However this doesn't work for methods defined in the interactive console. Methods must be defined in a file.
Here is an example:
require 'set'
require 'method_source'
puts Set.method(:[]).source_location
# /home/user/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/set.rb
# 74
#=> nil
puts Set.method(:[]).source
# def self.[](*ary)
# new(ary)
# end
#=> nil
Keep in mind that all core Ruby methods are written in C and return nil as source location. 1.method(:+).source_location #=> nil The standard library is written in Ruby itself. Therefore the example above works for Set methods.

You can access source of a method without using pry with a Object#method and Method#source_location as described in this answer: https://stackoverflow.com/a/46966145/580346

Related

Ruby gem won't add method to global scope using extend

I am trying to write a Ruby Gem which, when required, adds a function to the global scope.
I have followed the ideas here:
How can I add a method to the global scope in Ruby?, however, it just doesn't work! (on Ruby 2.4.3 anyway)
Here is my actual source code, but the below also summarises what I've done and what isn't working:
# example.rb
module Example
def self.hello()
puts "Hello"
end
end
extend Example
Then
# app.rb
require 'example' # Having built as a gem
hello() #=> `<main>': undefined method `hello' for main:Object (NoMethodError)
Where did I go wrong?
Sergio solved this for me, though I don't quite understand how!
It was considered good practice to encapsulate the methods in a module, so that users of the gem can use them directly (hello) or scoped (Example::hello) as they pleased.
By removing self. the method can be accessed directly only. By including self. it doesn't work at all. However, by doing:
module Example
extend self
def hello
puts "Hello"
end
end
extend Example
...it does work in both ways.

Override method Ruby

I would like to override the method: authorize_endpoint_url from the Gem in a Rails application: https://github.com/AzureAD/omniauth-azure-activedirectory/blob/master/lib/omniauth/strategies/azure_activedirectory.rb
I tried to do it by adding a file to config/initializers/oauth.rb
With the code:
module OmniAuth
module Strategies
# A strategy for authentication against Azure Active Directory.
class AzureActiveDirectory
def request_phase
debugger
"www.hans.com"
end
end
end
end
But this approach doesn't seem to work, nothing get's actually overwriten. What do I wrong? Thank you
When writing "monkey patch" style alterations you'll want to ensure they're loaded correctly. One way to test this is, after all is said and done, to interrogate Ruby to find out which method is actually being used:
OmniAuth::Strategies::AzureActiveDirectory.instance_method(:‌​request_phase).sourc‌​e_location
The instance_method call returns an object with information about that method and the source_location property tells you where that was defined.
If if's your method, great, you got it loaded right. If not you may need to check that you're hooking in at the correct time.

Where to define method to be accessible directly from rails console

So idea is to define
def foo
puts "Works!"
end
and directly from the console without loading anything I write
irb(main):001:0>foo()
=> "Works!"
irb(main):002:0>
I am using 1.9.3 on Windows. I want to use this in order to have a method which will reload lib/* so that I don't need to restart the console. Thank you.
I think this is what you're asking... I have the following code in an initializer:
if defined?(Rails::Console)
require "util/console_extensions"
include ConsoleExtensions
end
and any extra methods I want in the console defined in lib/util/console_extensions.rb
module ConsoleExtensions
def foo
puts "Works!"
end
end
This automatically requires and includes the ConsoleExtension module when loading the rails console and makes the methods defined in it available without the need to manually load anything.
If this is only for testing purpose then define those files inside models :) and afterwords move them to lib directory

Finding out where methods are defined in Ruby/Rails (as opposed to Java)

I am just getting started with Ruby on Rails. Coming from the Java world, one thing that I am wondering is how do Ruby/Rails developers find out where methods are actually defined.
I am used to just clicking on the method in Eclipse to find where is is defined even in third party libraries (supposing I have the source code).
A concrete example: I am trying to find out how the Authlogic gem apparently changes the constructor of my User class to require an additional parameter (called :password_confirmation) even though the User class doesn't even inherit from anything related to Authlogic.
Probably I am just overlooking something really obvious here (or maybe I still can't wrap my head around the whole "convention over configuration" thing ;-))
It's slightly difficult to quickly find the method location for dynamic languages like Ruby.
You can use object.methods or object.instance_methods to quickly find out the methods.
If you are using Ruby 1.9, you can do something like this:
object.method(:method_name).source_location
For more information on source_location - click here
The Pry gem is designed precisely for this kind of explorative use-case.
Pry is an interactive shell that lets you navigate your way around a program's source-code using shell-like commands such as cd and ls.
You can pull the documentation for any method you encounter and even view the source code, including the native C code in some cases (with the pry-doc plugin). You can even jump directly to the file/line where a particular method is defined with the edit-method command. The show-method and show-doc commands also display the precise location of the method they're acting on.
Watch the railscast screencast for more information.
Here are some examples below:
pry(main)> show-doc OpenStruct#initialize
From: /Users/john/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/ostruct.rb # line 46:
Number of lines: 11
visibility: private
signature: initialize(hash=?)
Create a new OpenStruct object. The optional hash, if given, will
generate attributes and values. For example.
require 'ostruct'
hash = { "country" => "Australia", :population => 20_000_000 }
data = OpenStruct.new(hash)
p data # -> <OpenStruct country="Australia" population=20000000>
By default, the resulting OpenStruct object will have no attributes.
pry(main)>
You can also look up sourcecode with the show-method command:
pry(main)> show-method OpenStruct#initialize
From: /Users/john/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/ostruct.rb # line 46:
Number of lines: 9
def initialize(hash=nil)
#table = {}
if hash
for k,v in hash
#table[k.to_sym] = v
new_ostruct_member(k)
end
end
end
pry(main)>
See http://pry.github.com for more information :)
None of people advising Pry gem mentionned the method called find-method, which is probably what author was looking for.
Here's the example:
pry(main)> find-method current_user
Devise::Controllers::Helpers
Devise::Controllers::Helpers#current_user
WebsocketRails::ConnectionAdapters::Base
WebsocketRails::ConnectionAdapters::Base#current_user_responds_to?
Then, you can browse the method code by following #banister's tips.
You could use something like pry. See its railscast also.
There are several ways to change an existing class. E.g. if you want to modify the String class write:
class String
def my_custom_method
puts "hello!"
end
end
But there are other options like mixing in modules or adding/modifying methods by using meta-programming.
Anyhow, having some object you can always:
puts obj.methods.inspect
Either do it in your code or use the debugger.
The other option is to read the code. In particular you should read the gem's unit tests (./spec, ...). There are quite a lot of authors stating that unit tests make documentation obsolete.
In Ruby you can also add both class and instance methods to a given class by using mixins.
Essentially if you have a module you can add its methods to a given class using both include and extend class methods. A brief example on how those works is the following
Module A
def foo
"foo"
end
end
Module B
def bar
"bar"
end
end
Class YourClass
include A
extend B
end
p YourClass.new.foo # gives "foo" because the foo method is added as instance method
p YourClass.bar # gives "baz" because the bar method is added as class method
Because Ruby is a dynamic language, these statements can be used everywhere. So to come to your question there is no need to extend an authlogic class to get its methods. Many plugins uses this instruction when loaded
ActiveRecord::Base.send :include, ModuleName
In this way they tell to every AR object to include some plugin defined module and you get all the methods in AR objects.
Another technique used by many acts_as plugins is to include their modules only when the acts_as call is used in the base class.
Other useful references
What is the difference between include and extend in Ruby?
A quick tutorial about mixins

How to find where a method is defined at runtime?

We recently had a problem where, after a series of commits had occurred, a backend process failed to run. Now, we were good little boys and girls and ran rake test after every check-in but, due to some oddities in Rails' library loading, it only occurred when we ran it directly from Mongrel in production mode.
I tracked the bug down and it was due to a new Rails gem overwriting a method in the String class in a way that broke one narrow use in the runtime Rails code.
Anyway, long story short, is there a way, at runtime, to ask Ruby where a method has been defined? Something like whereami( :foo ) that returns /path/to/some/file.rb line #45? In this case, telling me that it was defined in class String would be unhelpful, because it was overloaded by some library.
I cannot guarantee the source lives in my project, so grepping for 'def foo' won't necessarily give me what I need, not to mention if I have many def foo's, sometimes I don't know until runtime which one I may be using.
This is really late, but here's how you can find where a method is defined:
http://gist.github.com/76951
# How to find out where a method comes from.
# Learned this from Dave Thomas while teaching Advanced Ruby Studio
# Makes the case for separating method definitions into
# modules, especially when enhancing built-in classes.
module Perpetrator
def crime
end
end
class Fixnum
include Perpetrator
end
p 2.method(:crime) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>
If you're on Ruby 1.9+, you can use source_location
require 'csv'
p CSV.new('string').method(:flock)
# => #<Method: CSV#flock>
CSV.new('string').method(:flock).source_location
# => ["/path/to/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]
Note that this won't work on everything, like native compiled code. The Method class has some neat functions, too, like Method#owner which returns the file where the method is defined.
EDIT: Also see the __file__ and __line__ and notes for REE in the other answer, they're handy too. -- wg
You can actually go a bit further than the solution above. For Ruby 1.8 Enterprise Edition, there is the __file__ and __line__ methods on Method instances:
require 'rubygems'
require 'activesupport'
m = 2.days.method(:ago)
# => #<Method: Fixnum(ActiveSupport::CoreExtensions::Numeric::Time)#ago>
m.__file__
# => "/Users/james/.rvm/gems/ree-1.8.7-2010.01/gems/activesupport-2.3.8/lib/active_support/core_ext/numeric/time.rb"
m.__line__
# => 64
For Ruby 1.9 and beyond, there is source_location (thanks Jonathan!):
require 'active_support/all'
m = 2.days.method(:ago)
# => #<Method: Fixnum(Numeric)#ago> # comes from the Numeric module
m.source_location # show file and line
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]
I'm coming late to this thread, and am surprised that nobody mentioned Method#owner.
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Copying my answer from a newer similar question that adds new information to this problem.
Ruby 1.9 has method called source_location:
Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)
This has been backported to 1.8.7 by this gem:
ruby18_source_location
So you can request for the method:
m = Foo::Bar.method(:create)
And then ask for the source_location of that method:
m.source_location
This will return an array with filename and line number.
E.g for ActiveRecord::Base#validates this returns:
ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0#arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]
For classes and modules, Ruby does not offer built in support, but there is an excellent Gist out there that builds upon source_location to return file for a given method or first file for a class if no method was specified:
ruby where_is module
In action:
where_is(ActiveRecord::Base, :validates)
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0#arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]
On Macs with TextMate installed, this also pops up the editor at the specified location.
Maybe the #source_location can help to find where is the method come from.
ex:
ModelName.method(:has_one).source_location
Return
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]
OR
ModelName.new.method(:valid?).source_location
Return
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]
This may help but you would have to code it yourself. Pasted from the blog:
Ruby provides a method_added()
callback that is invoked every time a
method is added or redefined within a
class. It’s part of the Module class,
and every Class is a Module. There are
also two related callbacks called
method_removed() and
method_undefined().
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
If you can crash the method, you'll get a backtrace which will tell you exactly where it is.
Unfortunately, if you can't crash it then you can't find out where it has been defined. If you attempt to monkey with the method by overwriting it or overriding it, then any crash will come from your overwritten or overridden method, and it won't be any use.
Useful ways of crashing methods:
Pass nil where it forbids it - a lot of the time the method will raise an ArgumentError or the ever-present NoMethodError on a nil class.
If you have inside knowledge of the method, and you know that the method in turn calls some other method, then you can overrwrite the other method, and raise inside that.
Very late answer :) But earlier answers did not help me
set_trace_func proc{ |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
# call your method
set_trace_func nil
You might be able to do something like this:
foo_finder.rb:
class String
def String.method_added(name)
if (name==:foo)
puts "defining #{name} in:\n\t"
puts caller.join("\n\t")
end
end
end
Then ensure foo_finder is loaded first with something like
ruby -r foo_finder.rb railsapp
(I've only messed with rails, so I don't know exactly, but I imagine there's a way to start it sort of like this.)
This will show you all the re-definitions of String#foo. With a little meta-programming, you could generalize it for whatever function you want. But it does need to be loaded BEFORE the file that actually does the re-definition.
You can always get a backtrace of where you are by using caller().

Resources