root/trunk/activesupport/lib/active_support/whiny_nil.rb
| Revision 9226, 2.2 kB (checked in by pratik, 3 months ago) |
|---|
| Line | |
|---|---|
| 1 | # Extensions to +nil+ which allow for more helpful error messages for people who |
| 2 | # are new to Rails. |
| 3 | # |
| 4 | # Ruby raises NoMethodError if you invoke a method on an object that does not |
| 5 | # respond to it: |
| 6 | # |
| 7 | # $ ruby -e nil.destroy |
| 8 | # -e:1: undefined method `destroy' for nil:NilClass (NoMethodError) |
| 9 | # |
| 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: |
| 13 | # |
| 14 | # $ script/runner nil.destroy |
| 15 | # ... |
| 16 | # You might have expected an instance of ActiveRecord::Base. |
| 17 | # ... |
| 18 | # |
| 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. |
| 23 | # |
| 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 |
| 26 | # mode. |
| 27 | class NilClass |
| 28 | WHINERS = [::Array] |
| 29 | WHINERS << ::ActiveRecord::Base if defined? ::ActiveRecord |
| 30 | |
| 31 | @@method_class_map = Hash.new |
| 32 | |
| 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 } |
| 37 | end |
| 38 | |
| 39 | # Raises a RuntimeError when you attempt to call +id+ on +nil+. |
| 40 | def id |
| 41 | raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller |
| 42 | end |
| 43 | |
| 44 | private |
| 45 | def method_missing(method, *args, &block) |
| 46 | raise_nil_warning_for @@method_class_map[method], method, caller |
| 47 | end |
| 48 | |
| 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 |
| 54 | |
| 55 | raise NoMethodError, message, with_caller || caller |
| 56 | end |
| 57 | end |
| 58 |
Note: See TracBrowser for help on using the browser.