ruby on rails constantize method - ruby-on-rails

I came across the following in the model:
class Search < ActiveRecord::Base
#search different system user by dn
def self.gets(sys, dn)
sys.constantize.search(dn)
end
end
I can see the purpose is to pass in different model name as sys and search by dn in those specific models. However, I searched on constantize in Ruby and couldn't see any detailed explanation about this usage.

Rails documentation (because constantize is a Rails method) says:
Tries to find a constant with the name specified in the argument
string.
For instance, if you have a model called Foo in your application, then you can apply the constantize method to a string, which contains the exact word Foo, and it'll give you a new object with this model. Note this must be capitalized as Rails would work with your model, if you do a bad reference, then you'll get a NameError error:
NameError: wrong constant name foo
How does it do it?, if you go to the method definition, or if you playing with the method get an error, you'll see the source points to activesupport-5.1.5/lib/active_support/inflector/methods.rb:269:in 'const_get', which is the method definition and the error source.
Between the cases the method handles internally depending on what's being received as argument, you'll see the Object.const_get(string) which is the way Ruby (pure) handles a "constantiz-ation", which would be the same as doing
Object.const_get('Foo') # Foo(...)
Object.const_get('foo') # NameError: wrong constant name foo
If thinking on implement this handy method, you could take a look to the Gavin Miller's post from some years ago.

When I type 'foo'.constantize in my editor (RubyMine) and tap Ctrl+B, it takes me to the source, .../activesupport-4.2.8/lib/active_support/core_ext/string/inflections.rb, with this comment:
# +constantize+ tries to find a declared constant with the name specified
# in the string. It raises a NameError when the name is not in CamelCase
# or is not initialized. See ActiveSupport::Inflector.constantize
#
# 'Module'.constantize # => Module
# 'Class'.constantize # => Class
# 'blargle'.constantize # => NameError: wrong constant name blargle
def constantize
ActiveSupport::Inflector.constantize(self)
end
It probably eventually calls Object.const_get(). I recommend you get a better editor, with code browsing, and you start learning your way around the Rails source code.

Related

How to initialize Ruby class instance variable to a new instance of another class?

I am working on a Ruby on Rails 6 project, and I am trying to use a class instance variable on an ActiveRecord model. Here is a basic example:
class Model << ApplicationRecord
#var = AnotherClass.new
class << self
attr_reader :var
end
# ...
end
I would then like to be able to use Model.var to access Model's instance of AnotherClass. There are multiple such models, each of them referring to a different AnotherClass, with all the AnotherClasses being subclasses of some BaseClass.
However, I am encountering the following error:
uninitialized constant Model::AnotherClass
Because of the class << self, Ruby seems to be looking for a nested class.
Is there a way to access AnotherClass directly, or is there a better way in general to set this up?
Edit: I solved this with a completely different approach, however I'm still interested to see how you would get around this issue.
The error you receive:
uninitialized constant Model::AnotherClass
Tells you that AnotherClass is not initialized (not loaded/found). Let me use the following context as an example:
class Model
AnotherClass
end
Ruby will start a constant lookup. This will start from the current namespace (Model) and and if nothing is found move up into the namespace tree. In the above example it will first look for Model::AnotherClass if that cannot be found it will look for AnotherClass, if that cannot be found it will throw the exception you receive.
This error simply tells you that AnotherClass is not loaded.
Anything in th app/ directory is loaded by the autoloader of Rails, however if you use the lib/ directory you have to manually require 'another_class' or add the relevant path to the autoload paths.

Module automatically adding namespace to a method being called

I'm trying to create a gem, my gem requires a different gem that I have added into the gemspec.
My issue is when I try to call a method inside the code, ruby automatically adds the module namespace to the method I am calling, then I get an uninitialized constant error. I put a basic example of what is going on below.
lib/example_gem.rb
module FooModule
def bar
# this is the method I am trying to run
BAZ::Request.execute(123)
end
end
class Test
include FooModule
end
x = Test.new
x.bar
=>>>>>>>> uninitialized constant FooModule::Baz (NameError)
I'm not trying to call FooModule::Baz, I want to call BAZ::Request.execute(123). Any help would be appreciated
Try:
::BAZ::Request.execute(123)
The keyword is "constant lookup operator". I assume BAZ isn't wrapped into another class or module, so you need to look for it on the topmost level. Therefore you prepend ::.
And now you understand why the request (BAZ::Request) needs to be within BAZ.

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

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. :)

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