Calling Methods in Ruby on Rails - ruby-on-rails

I am using Rails 2.3.2 and I am really miss something important in here.
I got some .rb files in my lib folder and in one of them, I got a line saying
User.new(x,y,z)
But when I go to the user.rb which is also sitting in lib folder, I don't find any method with defined with new. When I look at the initializer, it just assigns the incoming attributes to the variables like
def initialize(x,y,z)
#x = x
#y = y
#z = z
end
Can you guys please tell me what I am really missing here. I know I am missing something really important.
Thanks

In Ruby (not only Rails) calling ClassName.new() invokes initialize method from this class to ... well.. initialize the object created. The initialize method will be passed all the arguments that are passed to new()
See here for details: http://ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html

Related

Ruby overriding method from Trie gem - undefined method

I use Trie (https://github.com/tyler/trie) gem in project and love it. But it has one issue that is really anoying.
has_key? method returns nil when key is not found instead of false (as every method ending with ? should)
I've tried opening an issue on their GitHub (https://github.com/tyler/trie/issues/26) but no luck.
So, reasonable next step - try to override the method.
I added this to my project:
class Trie
alias :old_has_key? :has_key?
def has_key?(key)
puts "My new Trie has_key"
old_has_key?(key)
end
end
Just to see if I can get away with it.
Unfortunately, when I run rails console I get:
`<class:Trie>': undefined method `has_key?' for class `Trie' (NameError)
As I found elsewhere, this should work. Any idea why it doesn't?
What I'm missing here? Location of trie.rb? Something else?
From your error message, it seems you are calling the has_key? method on the Trie class, not an instance of it. If you create an instance, your code works just fine:
my_trie = Trie.new
my_trie.has_key?('foo')
# My new Trie has_key
# => nil
So I found the problem.
My new class was nested under /lib folder in my Rails app. As soon as I moved it to /lib/utils/ it worked!
This means that loader picked up my custom class before actual Trie class. Still not sure why.
You can patch a little more simply:
class Trie
def has_key?(key)
!!super
end
end

Expected file to define module, but it is defined... why does ruby disagree?

The first line of text is the LoadError
the second line of text in the image is the folder where the file, shared.rb resides in my rails project.
And the sytax colored stuff is the top part of shared.rb
as you can see, shared.rb defines NamedScope::Shared, so why is RoR saying that it isn't?
using rails 2.3.8
UPDATE:
tried this:
module NamedScope
module Shared
in the same file, shared.rb in {AppRoot}/lib/named_scope/
which also didn't work (same error)
UPDATE 2: This error was caused my a model class not having a Constant defined.
I just had
CONSTANT_NAME
instead of
CONSTANT_NAME = value
this is upsetting, as I feel lied to o.o
Did you define the NamedScope module before? You might need to do:
module NamedScope
module Shared
# do your thing here
end
end
I would need to see more of the code to follow.
I've had that issue when the code uses a Class that is defined somewhere else and the magic name resolution is not strong enough. Like if I have a XyzLoan class that extends the Loan class but I did not require it or use it before. It shouldn't fail but it does.
If that is the case you can do a binary search for the problem by removing halves of the code and try again until you find what is causing it.

Class name is not defined in the module unless it's used in the code

I have created some classes in a module, f.i. Request (app/models/api/request.rb)
class Api::Request
end
And now I want to check in the console does the class exist?
>> Api.const_defined?('Request')
=> false
>> Api::Request
=> Api::Request
>> Api.const_defined?('Request')
=> true
Strange, but the script cannot see the class name unless I use it in the code.
How to fix this? And if you can explain the issue, it will be great.
The project uses Rails 2.3
The problem here is with the rails autoload mechanism. Basically what it does is to react to the method const_missing which you can define on every object. When this method is called, Rails looks for a file which is likely to contain a definition for the missing constant and requires it. The problem is that
Api::Request
triggers const_missing but
Api.const_defined?('Request')
does not.
You can write your own const_defined? like this (the code would go into a rails initializer):
def Module.autoload_const_defined?(name)
self.const_get name
ensure
return self.const_defined?(name)
end
which first tries to autoload the constant.
For anyone coming upon this in future - it's much better style to do something like the following:
def Module.autoload_const_defined?(name)
const_get(name)
rescue NameError => e
const_defined?(name)
end
return inside an ensure block means that any exceptions that occur are silently thrown away, and have the potential to make debugging really tricky.

How do you extend collection(?) to include second, third, etc. functions in Rails?

Pretty self explanatory.
I use array_name.first to get the first element. How do you extend it to get second, third, random, etc?
Thanks.
In Ruby you can just reopen any existing class and add your own functions.
In rails, you already have those methods defined in activesupport
See the source at github: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/array/access.rb
I discovered it is already there, in Ruby 1.9.2 at least.
If it weren't there, I would create a file in config/initializers called array_helper.rb (or whatever) and add the following code:
class Array
def second
self[1]
end
end
Why? Because all classes in Ruby are open and you can extend anything you want.
Here's some tricks to know:
When working in the console, if you want to test the file, be sure to use rails console and not irb. Also, do reload! after creating or updating the above file. Finally, all objects must be recreated to use the new code after reload!.

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