Find file path of a given class - ruby-on-rails

I have a reference to the Class object of a given class. I need to find out the file path of the class. The class inherits from ActiveRecord if that is any help.
Any easy way of doing this?
Thanks.

Use source_location on its methods:
YourClass.instance_methods(false).map { |m|
YourClass.instance_method(m).source_location.first
}.uniq
You might get more than one location, as methods might be defined in different places.

There is no way of doing this that works for all class definitions. There are some straightforward ways that work for some cases, and there are some complicated ways that work for other cases.
In Ruby, classes and modules can be reopened for additional definitions (monkey patched) many times. There is no built-in notion of a primary definition for a class or module. There is also no built-in way to list all of the files that contribute to the definition of a class. However, there is a built-in way to list the files that define methods within a class. To find the static definitions that contribute other components (constants, declarations, etc.), one can follow known conventions (if applicable) or apply static source code analysis.
1. Inspecting method locations
A Ruby method only has one definition in one location, which can be determined via Method#source_location. The instance methods of a class or module can be listed (as symbols) via Class#instance_methods and its scoped (public_, protected_, and private_) variants. The singleton methods (a.k.a. class methods) can be listed via Class#singleton_methods. Passing false as the first argument to these methods causes them to omit methods inherited from ancestors. From one of these symbols, one can obtain the corresponding Method via Class#instance_method, then use Method#source_location to obtain the file and line number of the method. This works for methods defined statically (using def) or dynamically (using various means, such as Module#class_eval combined with Module#define_method).
For example, consider these files that define a module M and a class C:
/tmp/m.rb
module M
def self.extended(klass)
klass.class_eval do
define_method(:ifoo) do
'ifoo'
end
define_singleton_method(:cfoo) do
'cfoo'
end
end
end
def has_ibar
self.class_eval do
define_method(:ibar) do
'ibar'
end
end
end
def has_cbar
self.class_eval do
define_singleton_method(:cbar) do
'cbar'
end
end
end
end
/tmp/c.rb
require_relative 'm'
class C
extend M
has_ibar
has_cbar
def im
'm'
end
def self.cm
'cm'
end
end
/tmp/c_ext.rb
class C
def iext
'iext'
end
def self.cext
'cext'
end
end
Given these definitions, one can inspect the class and find its source files as the following Pry session demonstrates.
2.4.0 (main):0 > require '/tmp/c'; require '/tmp/c_ext';
2.4.0 (main):0 > instance_methods_syms = C.instance_methods(false)
=> [:im, :ifoo, :ibar, :iext]
2.4.0 (main):0 > class_methods_syms = C.singleton_methods(false)
=> [:cm, :cfoo, :cbar, :cext]
2.4.0 (main):0 > instance_methods_locs = instance_methods_syms.map { |method_sym| C.instance_method(method_sym).source_location }
=> [["/tmp/c.rb", 9], ["/tmp/m.rb", 4], ["/tmp/m.rb", 16], ["/tmp/c_ext.rb", 2]]
2.4.0 (main):0 > class_methods_locs = class_methods_syms.map { |method_sym| C.singleton_class.instance_method(method_sym).source_location }
=> [["/tmp/c.rb", 13], ["/tmp/m.rb", 8], ["/tmp/m.rb", 24], ["/tmp/c_ext.rb", 6]]
2.4.0 (main):0 > methods_locs = instance_methods_locs + class_methods_locs;
2.4.0 (main):0 > class_files = methods_locs.map(&:first).uniq
=> ["/tmp/c.rb", "/tmp/m.rb", "/tmp/c_ext.rb"]
The order of values returned by Module#instance_methods isn't specified in the documentation and varies between Ruby versions.
1.1 Identifying a primary file
Identifying the primary file for a class among multiple candidate files obtained via Module#instance_methods and Method#source_location is not a simple problem. In the general case, it is impossible.
In the example above, /tmp/c.rb is intuitively the primary file because it is the first required file that defines C. Perhaps this is why, in Ruby 2.3.3 and 2.4.0, Module#instance_methods lists its methods first. However, as mentioned above, the order is undocumented, and it varies between Ruby versions. Notice that the first method defined in C, in order of execution, is #ifoo. Incidentally, with Ruby 1.9.3 through 2.2.6, the first item of instance_methods_syms is :ifoo, and the first item class_files is therefore /tmp/m.rb—clearly not what anyone would intuitively consider the primary file for C.
Furthermore, consider what happens if we remove the method definitions from /tmp/c.rb, leaving only the declarative-style calls extend M, has_ibar, and has_cbar. In this case, /tmp/c.rb is totally absent from the class_files. This is not an unrealistic scenario. For example, in Active Record, the primary definition for a simple model class may consist only of validations and other declarations, leaving everything else up to the framework. This definition will never be found by inspecting the class's method locations.
1.2. Pry command show-source
Pry's show-source (a.k.a. $) command uses a variant of this approach, applying its own logic to the inspection and sorting of methods and class definition files. See Pry::WrappedModule and Pry::Method if you're curious. It works rather well in practice, but because it relies on Method#source_location, it is unable to find class definitions that do not define methods.
2. Following conventions
This approach applies only to scenarios where the class being inspected is defined according to some well-defined conventions. If you know that the class you're inspecting follows such a convention, then you can use it to find its primary definition with certainty.
This approach works even when the method location approach fails—that is, when the primary definition does not contain any method definitions. However, it is limited to class definitions that follow well-defined conventions.
2.1. Convention: Rails model class
In a simple Rails application, the application's model classes are defined within its app/models directory, with a file path that can be derived deterministically from the class name. Given such a model class klass, the file that contains its primary definition is at the following location:
Rails.root.join('app', 'models', "#{klass.name.underscore}.rb").to_s
For example, a model class ProductWidget would be defined in APP_ROOT/app/models/product_widget.rb, where APP_ROOT is the application's root directory path.
To generalize this, one must consider extensions of the simple Rails configuration. In a Rails application that defines custom paths for model definitions, one must consider all of them. Also, since an arbitrary model class may be defined in any Rails engine loaded by the application, one must also look in all loaded engines, taking into account their custom paths. The following code combines these considerations.
candidates = Rails.application.config.paths['app/models'].map do |model_root|
Rails.root.join(model_root, "#{klass.name.underscore}.rb").to_s
end
candidates += Rails::Engine::Railties.engines.flat_map do |engine|
engine.paths['app/models'].map do |model_root|
engine.root.join(model_root, "#{klass.name.underscore}.rb").to_s
end
end
candidates.find { |path| File.exist?(path) }
This example applies specifically to Rails models, but it can be easily adapted to controllers and other classes whose definition locations are subject to the Rails conventions and configuration.
2.2. Convention: Generic Rails autoload resolution
Some classes are autoloaded in a Rails application but cannot be deterministically identified as belonging to one of the standard categories (models, controllers, etc.) whose paths are registered in the Rails path configuration. Still, it is possible to deterministically identify the file containing the primary definition of such a class. The solution is to implement the generic autoload resolution algorithm used by Rails. An example of such an implementation is beyond the scope of this answer.
3. Static source code analysis
If other approaches are not applicable or insufficient, one might try to resort to the brute force approach: look for definitions of the given class in all loaded Ruby source files. Unfortunately, this is both limited and complicated.
Files that are loaded using Kernel#require are listed in $LOADED_FEATURES, so that array of paths can be searched for Ruby files that contain a definition of the class. However, files that are loaded using Kernel#load are not necessarily listed anywhere, so they cannot be searched. One exception to this is files that are loaded via the Rails autoload mechanism when config.cache_classes is false (the default in development mode). In this case, there is a workaround: search in the Rails autoload paths. An efficient search would follow the Rails autoload resolution algorithm, but it is also sufficient to search all autoload paths, which can be obtained via Rails.application.send(:_all_autoload_paths).
Even for class definition files that can be listed, identifying the definition of a given class is not trivial. For a class that is defined with a class statement in the root namespace, this is easy: find lines that match /^\s*class\s+#{klass}[\s$]/. However, for a class whose definition is nested in a module body, or for a class that is defined dynamically using Class::new, this requires parsing each file into an abstract syntax tree (AST) and searching the tree for such definitions. For a class that is defined using any other class generator, the AST search needs to be made aware of that generator. Considering that any such implementation requires reading many files from disk, it would be prudent to cache all discovered class definitions if the intent is to perform more than one class definition lookup. Any such implementation is beyond the scope of this answer.
For class definitions in files that do not follow well-defined conventions, this approach is the most thorough one. However, the implementation is complex, requires reading and parsing all loaded source files, and is still fundamentally limited.

if you are referring to rails model and take the default configuration it should be in app models folder and you could get a path as
File.join Rails.root,"app","models", "#{self.class.name.to_s.underscore}.rb"

Related

Where does Rails include modules in a class from lib?

We have a Rails application. There is a class in lib called PriorityFilter. It's a simple PORO class. It doesn't explicitly inherit from other classes and it doesn't include any modules. However, if I run Rails console I can see that the class includes a number of modules:
irb(main):002:0> PriorityFilter.included_modules
=> [ActiveSupport::ToJsonWithActiveSupportEncoder, ActionView::Helpers::NumberHelper, PP::ObjectMixin, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, Kernel]
The class is used in a view helper, to prepare data for rendering a template. If I place a debugger brake point in the view helper and check included modules, there are even more modules in the list:
(byebug) PriorityFilter.included_modules
[ActiveSupport::ToJsonWithActiveSupportEncoder, ActionView::Helpers::UrlHelper, ApplicationHelper, DateTimeHelper, Aligni::DateTimeFormatter, ActiveJob::TestHelper, UnitHelper, ActionView::Helpers::TextHelper, ActionView::Helpers::TagHelper, ActionView::Helpers::OutputSafetyHelper, ActionView::Helpers::CaptureHelper, ActionView::Helpers::SanitizeHelper, ActionView::Helpers::NumberHelper, PP::ObjectMixin, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, Kernel]
Among them are some helpers, such as ApplicationHelper, DateTimeHelper and UnitHelper that are actually defined in the application code, not Rails.
We checked thoroughly and these helpers are not explicitly included in this class (or other similar classes) anywhere in our application code. Because of that, I suspect that Rails does that. The questions are:
Is it true that Rails does that for all classes in lib?
Where does Rails include those modules?
Are different modules included based on the context where a class is used? Is there a way to find out which modules will be included?
Is there a way to control the list of included modules, via some configuration option? (I understand that modules can be explicitly included and that they can be included/excluded in runtime with methods available on every object.)
Your class, unless specified otherwise, inherits from Object and the ActiveSupport adds its own extensions (also) to Object class, like here, with ActiveSupport::ToJsonWithActiveSupportEncoder for example:
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].reverse_each do |klass|
klass.prepend(ActiveSupport::ToJsonWithActiveSupportEncoder)
end
so to answer your specific questions:
No, if you write class that inherits from BasicObject, none of it will be included, most probably.
In various places, I gave you one example, you can search for another ones.
No, it's based on inheritance chain.
No, as far as I know.
I'm not sure though, how are the helpers included. I'd try something like this to find it out, with the usage of included hook:
module ApplicationHelper
def self.included(base)
binding.pry # or any other debugger
end
end
And then I'd start the application and see the backtrace, maybe it would work.

Ruby "wrapper" class to represent a module

I've found that if I create a module, I have to create a "wrapper class" for it to work in certain circumstances (actually Resque-pool in my case, but I think it's relevant elsewhere too, even though it worked in regular Unicorn without needing it). By wrapper class, I mean if I create:
models/
posts/
selfie.rb
I will get a "Expected selfie.rb to define Posts::Selfie" unless I also create models/posts.rb containing "require posts/selfie.rb". (Based on https://stackoverflow.com/a/11001022/18706)
My question is, should this be done for every module under models, controllers, etc? And is there a name for this kind of class, or any reference info about it?

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

Ruby on Rails: Learning ActionController class - Question on $:.unshift activesupport_path and autoload method

Inside ActionController class (rails/actionpack/lib/action_controller.lib) I found several weird code. I don't really have a mentor to learn Ruby on Rails from, so this forum is my only hope:
Question #1: Could anyone help me explain these lines of codes?
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
Especially the line with $:.unshift activesupport_path
In my thought, it tries to require active_support class, and if that doesn't work, it looks if activesupport_path is a directory, if it is, then . . . I totally lost it.
Question #2: What autoload method is for?
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
.
.
.
Question #3: If I later find a method I don't understand what for, how is the best way to find out? As for that autoload method case, I tried to find it across my project (I have my Rails code frozen there) but couldn't find any clue. I searched for "def autoload". Am I doing things wrong? Is my IDE, TextMate just doesn't cut it?
Thank you!
In order for a file to be required you have to ensure that the path to it is in the Ruby $LOAD_PATH variable. This is has a short-hand version $: for legacy reasons, inheriting this from Perl.
When you call require, the interpreter looks for a .rb file in each of the paths given there until it finds a match. If it finds one, it is loaded. If not you get an exception.
Often you will see lines like this in files:
# script/something
# This appends "script/../lib" to the $LOAD_PATH, but this expands to
# something like "/home/user/project/lib" depending on the details of
# your installation.
$: << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))
You can use standard Array modifiers on $LOAD_PATH like unshift, push, and <<.
The first block of code is attempting to load active_support and only if that fails does it go about modifying the $LOAD_PATH to include the likely location of this file based on the path to the file making the require call. They do this because typically all gems from the Rails bundle are installed in the same base directory.
The reason for using unshift is to put that path at the highest priority, inserted at the front of the list. The << or push method adds to the end, lowest priority.
When you require a file it is loaded in, parsed, and evaluated, an operation which can take a small but measurable amount of time and will consume more memory to hold any class or method definitions inside the file, as well as any data such as string constants that may be declared. Loading in every single element of a library like ActiveRecord using require will require a considerable amount of memory, and this will import every database driver available, not just the ones that are actually used.
Ruby allows you to declare a class and a path to the file where it is defined, but with the advantage of not actually loading it in at that moment. This means that references to that class don't cause script errors in other parts of your application that make use of them.
You will often see declarations like this:
class Foo
# Declare the class Foo::Bar to be defined in foo/bar.rb
autoload(:Bar, 'foo/bar')
end
When using autoload you need to keep in mind that the class name is always defined within the scope of the module or class declaring it. In this example Bar is within Foo, or Foo::Bar using Ruby naming conventions.
When you make use of the Bar class, the foo/bar.rb file will be required. Think of it as creating a stub Bar class that transforms into the real class once it's actually exercised.
This is a great way of keeping a lot of options open, with many different modules ready to use, but without having to load everything into memory up front.
As for the third question, searchable documentation like APIDock will help you try and find more information on methods. The distinction between Ruby and Rails is often blurred, so you may have to check through both to be sure. Rails adds a lot of methods to core Ruby classes, so don't take the listing of methods available to be complete on either side. They work in conjunction.
Sometimes it pays to search for def methodname when trying to find out about where methodname originates, although this covers only conventional declarations. That method may be an alias from a mechanism like method_alias or may have been dynamically created using define_method, you can never really be sure until you dig around. At least 90% of the methods in Rails are declared the conventional way, though, so most of the time a simple search will yield what you want.

Get all models having implemented a certain method

I have many models created with the standard rails model generator.
Some of the models have then got a method called foo().
Is there a simple way to figure out all the class names of the generated models having the foo() method implemented ?
I mean programmaticaly, from a rails controller, not from the console grepping the sourcecode.
Rails doesn't keep an index of your models, so you'll simply have to walk your app/models directory.
Here's an example:
# Open the model directory
models_dir = Dir.open("#{RAILS_ROOT}/app/models")
# Walk directory entries
models = models_dir.collect do |filename|
# Get the name without extension.
# (And skip anything that isn't a Ruby file.)
next if not filename =~ /^(.+)\.rb$/
basename = $~[1]
# Now, get the model class
klass = basename.camelize.constantize
# And return it, if it implements our method
klass if klass.method_defined? :foo
end
# Remove nils
models.compact!
Rails loads your models and controllers lazily, the first time they are referenced. This is done using Ruby's const_missing method, and you can see all the magic happening in ActiveSupport in active_support/dependencies.rb.
To elaborate a bit one what's happening above, you also need to know that class names and filenames are linked. Rails expects a ThingyBob model to live in thingy_bob.rb. The way to convert between those two names is using the String method camelize. (The reverse would be the underscore method.) These String extensions are also part of ActiveSupport.
Finally, with the ActiveSupport method constantize, which is also a String extension, we dereference a string as a constant. So basically "ThingyBob".constantize is the same as just writing ThingyBob; we simply get the ThingyBob class back. In the example above,constantize triggers the regular dependency loading magic too.
Hope that helps demystify some things. :)

Resources