From 40e2e6d23557b60f9cca02298be742a1b398cb66 Mon Sep 17 00:00:00 2001 From: Steven Soroka Date: Fri, 27 Mar 2009 16:06:56 -0500 Subject: [PATCH] add ignore_nil{} support to rails. much more flexible than try. closes #2364. [Steven Soroka] --- .../lib/active_support/core_ext/object.rb | 1 + .../active_support/core_ext/object/ignore_nil.rb | 36 ++++++++++++++++++++ .../test/core_ext/object_and_class_ext_test.rb | 18 ++++++++++ 3 files changed, 55 insertions(+), 0 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/object/ignore_nil.rb diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 0796a7b..1307316 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -3,3 +3,4 @@ require 'active_support/core_ext/object/extending' require 'active_support/core_ext/object/instance_variables' require 'active_support/core_ext/object/metaclass' require 'active_support/core_ext/object/misc' +require 'active_support/core_ext/object/ignore_nil' \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/object/ignore_nil.rb b/activesupport/lib/active_support/core_ext/object/ignore_nil.rb new file mode 100644 index 0000000..2932a00 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/ignore_nil.rb @@ -0,0 +1,36 @@ +class Object + RESCUE_NIL_EXCEPTIONS = %w(NoMethodError RuntimeError ActionView::TemplateError) + RESCUE_NIL_MESSAGES = [/You have a nil object when you didn't expect it/, + /undefined method `.*?' for nil:NilClass/, + /^Called id for nil/] + # ignore_nil is actually short for "ignore no method error on nil", referring to the error you get if + # you call a method on an object that is unexpectedly nil. This can happen if you chain method calls + # together and one of the methods returns a nil. + # + # ignore_nil{} will return nil if you get a NoMethodError exception on NilClass. + # any other exception type is reraised so that code inside ignore_nil{} blocks don't become a black- + # hole. + # + # ==== Examples + # + # Without ignore_nil: + # @user && @user.address && @user.address.street + # or worse: + # @user.address.street rescue nil + # (worse because it catches everything! even if address doesn't have a street method!) + # + # With ignore_nil: + # ignore_nil { @user.address.street } + # + def ignore_nil(&block) + begin + yield + rescue Exception => e + if RESCUE_NIL_EXCEPTIONS.include?(e.class.name) && RESCUE_NIL_MESSAGES.any?{|exp| e.message =~ exp } + return nil + else + raise e + end + end + end +end diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index b6515e0..dfe1479 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -280,3 +280,21 @@ class ObjectTryTest < Test::Unit::TestCase assert_equal 'false', false.try(:to_s) end end + +class ObjectIgnoreNilTest < Test::Unit::TestCase + def setup + @array = [{:a => "test", :b => "hi there"}] + end + + def test_ignore_nil_returns_actual_value + assert_equal 't', ignore_nil { @array.first[:a].first } + end + + def test_ignore_nil_returns_nil_on_exception + assert_equal nil, ignore_nil { @array.first[:c].first } + end + + def test_ignore_nil_returns_nil_when_nil + assert_equal nil, ignore_nil { @array.first[:c] } + end +end \ No newline at end of file -- 1.5.5