aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorRyan Bigg <radarlistener@gmail.com>2008-10-06 11:57:12 +1030
committerRyan Bigg <radarlistener@gmail.com>2008-10-06 11:57:12 +1030
commita21d8f632fe8aa3bf4c1b83accc7a2dd230c28e7 (patch)
treebb0b0e9423d4e259de6041e661bba119f7871faa /activesupport
parentb340337aaff5b59fdf2110207fec3e1c43f1380a (diff)
parent6090513cfb8acb5554a6653a6f2cb87648585d41 (diff)
downloadrails-a21d8f632fe8aa3bf4c1b83accc7a2dd230c28e7.tar.gz
rails-a21d8f632fe8aa3bf4c1b83accc7a2dd230c28e7.tar.bz2
rails-a21d8f632fe8aa3bf4c1b83accc7a2dd230c28e7.zip
Merge branch 'master' of git@github.com:lifo/docrails
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/lib/active_support.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/extending.rb45
-rw-r--r--activesupport/lib/active_support/rescuable.rb108
-rw-r--r--activesupport/lib/active_support/secure_random.rb4
-rw-r--r--activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb11
-rw-r--r--activesupport/test/rescuable_test.rb75
-rw-r--r--activesupport/test/secure_random_test.rb4
-rw-r--r--activesupport/test/test_test.rb18
9 files changed, 251 insertions, 18 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 9700a11531..2c6f4ed582 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
+
* Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]
This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index b30faff06d..0ff09067ec 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -56,6 +56,8 @@ require 'active_support/time_with_zone'
require 'active_support/secure_random'
+require 'active_support/rescuable'
+
I18n.load_path << File.dirname(__FILE__) + '/active_support/locale/en-US.yml'
Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector')
diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb
index 082e98a297..bbf6f8563b 100644
--- a/activesupport/lib/active_support/core_ext/object/extending.rb
+++ b/activesupport/lib/active_support/core_ext/object/extending.rb
@@ -3,22 +3,43 @@ class Object
Class.remove_class(*subclasses_of(*superclasses))
end
- # Exclude this class unless it's a subclass of our supers and is defined.
- # We check defined? in case we find a removed class that has yet to be
- # garbage collected. This also fails for anonymous classes -- please
- # submit a patch if you have a workaround.
- def subclasses_of(*superclasses) #:nodoc:
- subclasses = []
-
- superclasses.each do |sup|
- ObjectSpace.each_object(class << sup; self; end) do |k|
- if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
- subclasses << k
+ begin
+ ObjectSpace.each_object(Class.new) {}
+
+ # Exclude this class unless it's a subclass of our supers and is defined.
+ # We check defined? in case we find a removed class that has yet to be
+ # garbage collected. This also fails for anonymous classes -- please
+ # submit a patch if you have a workaround.
+ def subclasses_of(*superclasses) #:nodoc:
+ subclasses = []
+
+ superclasses.each do |sup|
+ ObjectSpace.each_object(class << sup; self; end) do |k|
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+ subclasses << k
+ end
end
end
+
+ subclasses
end
+ rescue RuntimeError
+ # JRuby and any implementations which cannot handle the objectspace traversal
+ # above fall back to this implementation
+ def subclasses_of(*superclasses) #:nodoc:
+ subclasses = []
- subclasses
+ superclasses.each do |sup|
+ ObjectSpace.each_object(Class) do |k|
+ if superclasses.any? { |superclass| k < superclass } &&
+ (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+ subclasses << k
+ end
+ end
+ subclasses.uniq!
+ end
+ subclasses
+ end
end
def extended_by #:nodoc:
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
new file mode 100644
index 0000000000..f2bc12e832
--- /dev/null
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -0,0 +1,108 @@
+module ActiveSupport
+ # Rescuable module adds support for easier exception handling.
+ module Rescuable
+ def self.included(base) # :nodoc:
+ base.class_inheritable_accessor :rescue_handlers
+ base.rescue_handlers = []
+
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ # Rescue exceptions raised in controller actions.
+ #
+ # <tt>rescue_from</tt> receives a series of exception classes or class
+ # names, and a trailing <tt>:with</tt> option with the name of a method
+ # or a Proc object to be called to handle them. Alternatively a block can
+ # be given.
+ #
+ # Handlers that take one argument will be called with the exception, so
+ # that the exception can be inspected when dealing with it.
+ #
+ # Handlers are inherited. They are searched from right to left, from
+ # bottom to top, and up the hierarchy. The handler of the first class for
+ # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
+ # any.
+ #
+ # class ApplicationController < ActionController::Base
+ # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
+ # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
+ #
+ # rescue_from 'MyAppError::Base' do |exception|
+ # render :xml => exception, :status => 500
+ # end
+ #
+ # protected
+ # def deny_access
+ # ...
+ # end
+ #
+ # def show_errors(exception)
+ # exception.record.new_record? ? ...
+ # end
+ # end
+ def rescue_from(*klasses, &block)
+ options = klasses.extract_options!
+
+ unless options.has_key?(:with)
+ if block_given?
+ options[:with] = block
+ else
+ raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
+ end
+ end
+
+ klasses.each do |klass|
+ key = if klass.is_a?(Class) && klass <= Exception
+ klass.name
+ elsif klass.is_a?(String)
+ klass
+ else
+ raise ArgumentError, "#{klass} is neither an Exception nor a String"
+ end
+
+ # put the new handler at the end because the list is read in reverse
+ rescue_handlers << [key, options[:with]]
+ end
+ end
+ end
+
+ # Tries to rescue the exception by looking up and calling a registered handler.
+ def rescue_with_handler(exception)
+ if handler = handler_for_rescue(exception)
+ handler.arity != 0 ? handler.call(exception) : handler.call
+ true # don't rely on the return value of the handler
+ end
+ end
+
+ def handler_for_rescue(exception)
+ # We go from right to left because pairs are pushed onto rescue_handlers
+ # as rescue_from declarations are found.
+ _, handler = Array(rescue_handlers).reverse.detect do |klass_name, handler|
+ # The purpose of allowing strings in rescue_from is to support the
+ # declaration of handler associations for exception classes whose
+ # definition is yet unknown.
+ #
+ # Since this loop needs the constants it would be inconsistent to
+ # assume they should exist at this point. An early raised exception
+ # could trigger some other handler and the array could include
+ # precisely a string whose corresponding constant has not yet been
+ # seen. This is why we are tolerant to unknown constants.
+ #
+ # Note that this tolerance only matters if the exception was given as
+ # a string, otherwise a NameError will be raised by the interpreter
+ # itself when rescue_from CONSTANT is executed.
+ klass = self.class.const_get(klass_name) rescue nil
+ klass ||= klass_name.constantize rescue nil
+ exception.is_a?(klass) if klass
+ end
+
+ case handler
+ when Symbol
+ method(handler)
+ when Proc
+ handler.bind(self)
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/secure_random.rb b/activesupport/lib/active_support/secure_random.rb
index 688165f9a3..97971e8830 100644
--- a/activesupport/lib/active_support/secure_random.rb
+++ b/activesupport/lib/active_support/secure_random.rb
@@ -164,13 +164,13 @@ module ActiveSupport
hex = n.to_s(16)
hex = '0' + hex if (hex.length & 1) == 1
bin = [hex].pack("H*")
- mask = bin[0].ord
+ mask = bin[0]
mask |= mask >> 1
mask |= mask >> 2
mask |= mask >> 4
begin
rnd = SecureRandom.random_bytes(bin.length)
- rnd[0] = (rnd[0].ord & mask).chr
+ rnd[0] = rnd[0] & mask
end until rnd < bin
rnd.unpack("H*")[0].hex
else
diff --git a/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb
index 63d1ba6507..e5853bf828 100644
--- a/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb
+++ b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb
@@ -37,15 +37,18 @@ module Test
# end
def assert_difference(expressions, difference = 1, message = nil, &block)
expression_evaluations = Array(expressions).map do |expression|
- lambda do
+ [expression, lambda do
eval(expression, block.__send__(:binding))
- end
+ end]
end
- original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
+ original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression[1].call }
yield
expression_evaluations.each_with_index do |expression, i|
- assert_equal original_values[i] + difference, expression.call, message
+ full_message = ""
+ full_message << "#{message}.\n" if message
+ full_message << "<#{expression[0]}> was the expression that failed"
+ assert_equal original_values[i] + difference, expression[1].call, full_message
end
end
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
new file mode 100644
index 0000000000..9f2b783b2e
--- /dev/null
+++ b/activesupport/test/rescuable_test.rb
@@ -0,0 +1,75 @@
+require 'abstract_unit'
+
+class WraithAttack < StandardError
+end
+
+class NuclearExplosion < StandardError
+end
+
+class MadRonon < StandardError
+ attr_accessor :message
+
+ def initialize(message)
+ @message = message
+ super()
+ end
+end
+
+class Stargate
+ attr_accessor :result
+
+ include ActiveSupport::Rescuable
+
+ rescue_from WraithAttack, :with => :sos
+
+ rescue_from NuclearExplosion do
+ @result = 'alldead'
+ end
+
+ rescue_from MadRonon do |e|
+ @result = e.message
+ end
+
+ def dispatch(method)
+ send(method)
+ rescue Exception => e
+ rescue_with_handler(e)
+ end
+
+ def attack
+ raise WraithAttack
+ end
+
+ def nuke
+ raise NuclearExplosion
+ end
+
+ def ronanize
+ raise MadRonon.new("dex")
+ end
+
+ def sos
+ @result = 'killed'
+ end
+end
+
+class RescueableTest < Test::Unit::TestCase
+ def setup
+ @stargate = Stargate.new
+ end
+
+ def test_rescue_from_with_method
+ @stargate.dispatch :attack
+ assert_equal 'killed', @stargate.result
+ end
+
+ def test_rescue_from_with_block
+ @stargate.dispatch :nuke
+ assert_equal 'alldead', @stargate.result
+ end
+
+ def test_rescue_from_with_block_with_args
+ @stargate.dispatch :ronanize
+ assert_equal 'dex', @stargate.result
+ end
+end
diff --git a/activesupport/test/secure_random_test.rb b/activesupport/test/secure_random_test.rb
index b0b6c21a81..44694cd811 100644
--- a/activesupport/test/secure_random_test.rb
+++ b/activesupport/test/secure_random_test.rb
@@ -12,4 +12,8 @@ class SecureRandomTest < Test::Unit::TestCase
b2 = ActiveSupport::SecureRandom.hex(64)
assert_not_equal b1, b2
end
+
+ def test_random_number
+ assert ActiveSupport::SecureRandom.random_number(5000) < 5000
+ end
end
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index 26a45af255..4e253848f6 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -60,6 +60,24 @@ class AssertDifferenceTest < Test::Unit::TestCase
@object.increment
end
end
+
+ def test_array_of_expressions_identify_failure
+ assert_difference ['@object.num', '1 + 1'] do
+ @object.increment
+ end
+ fail 'should not get to here'
+ rescue Test::Unit::AssertionFailedError => e
+ assert_equal "<1 + 1> was the expression that failed.\n<3> expected but was\n<2>.", e.message
+ end
+
+ def test_array_of_expressions_identify_failure_when_message_provided
+ assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do
+ @object.increment
+ end
+ fail 'should not get to here'
+ rescue Test::Unit::AssertionFailedError => e
+ assert_equal "something went wrong.\n<1 + 1> was the expression that failed.\n<3> expected but was\n<2>.", e.message
+ end
else
def default_test; end
end