Why ActiveSupport core extension not added as module? - ruby-on-rails

I was reading the ActiveSupport core_ext source, and saw that it directly open and extend the core ruby class, e.g: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/string/conversions.rb
. Doesn't that make it harder for us to know wether some method is from activesupport or actually provided by ruby itself (e.g via Method#owner)?
Why doesn't it use something like prepending/including a module to give its added functionality? E.g:
module StringConversionExtension
def to_time
# some implementation
end
end
String.prepend(StringExtension)
Is there any historical/performance reason the implementation is as it is now?

Why doesn't it use something like prepending/including a module to give its added functionality? E.g:
Possibly to either:
Minimize the amount of code in Rails (since your proposed approach is a bit more verbose), or
Minimize the number of methods/constants/modules in existence as well as in the core classes, since in your proposed approach the core String class would have the new method and it would now be including the StringExtensions module.

Related

Is it possible to use concerns outside of Rails

I like the idea of ActiveSupport::Concerns but wondering if possible to use in a vanilla Ruby app. Or is it only in a Rails app? I'm thinking of the example using ActiveRecord in a Sinatra app.
Edit
Looks like you can by requiring 'active_support', though I am not sure if any nuance about this doesn't work.
Like:
require 'active_support'
module Printable
include ActiveSupport::Concern
def print
puts "I will print here"
end
end
class User
include Printable
def initialize(name)
#name = name
end
def say_my_name
puts "my name: #{#name}"
end
end
Yes, you can (and it seems like you've discovered how). Much of ActiveSupport is written in a way that it can be added by itself to other projects.
You can even require just Concern with require "active_support/concern"
Opinion
I'm generally against using ActiveSupport::Concern though:
violates Composition Over Inheritance (see Nothing is Something by Sandi Metz)
95% of functionality is provided by vanilla Ruby already, in only 0-3 more lines of code (zero meaning you didn't need Concern at all)
Examples:
The code snippet you've pasted works perfectly fine with a bare Ruby module
Rail's own documentation on Concern explains usage with vanilla Ruby examples of the same length and complexity
In that documentation, it says: "Given a Foo module and a Bar module which depends on the former...", as if you'd want to deal with mixin hell

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.

How do ActiveRecord dynamic attribute-based finders work?

I've been trying to find the source code so I can understand how ActiveRecord dynamic attribute-based finders work. I haven't been able to find where in the source code the definitions live.
Could someone put me to the location in the file(s) that define this functionality? Thanks!
Using Rails 3.2.5
I looked in ActiveRecord::Model and noticed that there was a module included called DynamicMatchers. It looks like this is where all the dynamic attribute finders come from. Essentially it's using method missing and reflecting back on the associated model:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/dynamic_matchers.rb

Find file path of a given class

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"

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

Resources