1 # Extensions to +nil+ which allow for more helpful error messages for people who
4 # Ruby raises NoMethodError if you invoke a method on an object that does not
7 # $ ruby -e nil.destroy
8 # -e:1: undefined method `destroy' for nil:NilClass (NoMethodError)
10 # With these extensions, if the method belongs to the public interface of the
11 # classes in NilClass::WHINERS the error message suggests which could be the
12 # actual intended class:
14 # $ script/runner nil.destroy
16 # You might have expected an instance of ActiveRecord::Base.
19 # NilClass#id exists in Ruby 1.8 (though it is deprecated). Since +id+ is a fundamental
20 # method of Active Record models NilClass#id is redefined as well to raise a RuntimeError
21 # and warn the user. She probably wanted a model database identifier and the 4
22 # returned by the original method could result in obscure bugs.
24 # The flag <tt>config.whiny_nils</tt> determines whether this feature is enabled.
25 # By default it is on in development and test modes, and it is off in production
29 WHINERS
<< ::ActiveRecord::Base if defined? ::ActiveRecord
31 METHOD_CLASS_MAP
= Hash
.new
33 WHINERS
.each
do |klass
|
34 methods
= klass
.public_instance_methods
- public_instance_methods
35 class_name
= klass
.name
36 methods
.each
{ |method
| METHOD_CLASS_MAP
[method
.to_sym
] = class_name
}
39 # Raises a RuntimeError when you attempt to call +id+ on +nil+.
41 raise RuntimeError
, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller
45 def method_missing(method
, *args
, &block
)
46 raise_nil_warning_for METHOD_CLASS_MAP
[method
], method
, caller
49 # Raises a NoMethodError when you attempt to call a method on +nil+.
50 def raise_nil_warning_for(class_name
= nil, selector
= nil, with_caller
= nil)
51 message
= "You have a nil object when you didn't expect it!"
52 message
<< "\nYou might have expected an instance of #{class_name}." if class_name
53 message
<< "\nThe error occurred while evaluating nil.#{selector}" if selector
55 raise NoMethodError
, message
, with_caller
|| caller