activesupport 2.2.3 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (94) hide show
  1. data/CHANGELOG +128 -89
  2. data/lib/active_support.rb +31 -33
  3. data/lib/active_support/backtrace_cleaner.rb +72 -0
  4. data/lib/active_support/buffered_logger.rb +9 -7
  5. data/lib/active_support/cache.rb +13 -8
  6. data/lib/active_support/cache/drb_store.rb +2 -3
  7. data/lib/active_support/cache/mem_cache_store.rb +6 -1
  8. data/lib/active_support/cache/strategy/local_cache.rb +104 -0
  9. data/lib/active_support/callbacks.rb +20 -21
  10. data/lib/active_support/core_ext.rb +1 -1
  11. data/lib/active_support/core_ext/array.rb +2 -0
  12. data/lib/active_support/core_ext/array/conversions.rb +26 -13
  13. data/lib/active_support/core_ext/array/wrapper.rb +24 -0
  14. data/lib/active_support/core_ext/benchmark.rb +13 -6
  15. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +14 -5
  16. data/lib/active_support/core_ext/class/attribute_accessors.rb +24 -24
  17. data/lib/active_support/core_ext/class/delegating_attributes.rb +20 -19
  18. data/lib/active_support/core_ext/class/inheritable_attributes.rb +34 -34
  19. data/lib/active_support/core_ext/date/conversions.rb +3 -3
  20. data/lib/active_support/core_ext/date_time/conversions.rb +1 -1
  21. data/lib/active_support/core_ext/enumerable.rb +9 -0
  22. data/lib/active_support/core_ext/exception.rb +12 -8
  23. data/lib/active_support/core_ext/file/atomic.rb +2 -2
  24. data/lib/active_support/core_ext/hash/conversions.rb +32 -54
  25. data/lib/active_support/core_ext/hash/indifferent_access.rb +6 -0
  26. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  27. data/lib/active_support/core_ext/hash/slice.rb +8 -1
  28. data/lib/active_support/core_ext/logger.rb +8 -6
  29. data/lib/active_support/core_ext/module/aliasing.rb +3 -3
  30. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +4 -4
  31. data/lib/active_support/core_ext/module/attribute_accessors.rb +24 -24
  32. data/lib/active_support/core_ext/module/delegation.rb +29 -3
  33. data/lib/active_support/core_ext/module/synchronization.rb +5 -5
  34. data/lib/active_support/core_ext/object/conversions.rb +2 -1
  35. data/lib/active_support/core_ext/object/misc.rb +16 -0
  36. data/lib/active_support/core_ext/range/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/rexml.rb +29 -24
  38. data/lib/active_support/core_ext/string/inflections.rb +3 -3
  39. data/lib/active_support/core_ext/time/calculations.rb +1 -2
  40. data/lib/active_support/core_ext/time/conversions.rb +1 -1
  41. data/lib/active_support/core_ext/try.rb +36 -0
  42. data/lib/active_support/dependencies.rb +18 -14
  43. data/lib/active_support/deprecation.rb +10 -57
  44. data/lib/active_support/duration.rb +3 -1
  45. data/lib/active_support/inflections.rb +1 -0
  46. data/lib/active_support/inflector.rb +16 -7
  47. data/lib/active_support/json/decoding.rb +21 -3
  48. data/lib/active_support/json/encoders/date.rb +1 -1
  49. data/lib/active_support/json/encoders/date_time.rb +1 -1
  50. data/lib/active_support/json/encoders/hash.rb +10 -11
  51. data/lib/active_support/json/encoders/time.rb +1 -1
  52. data/lib/active_support/json/encoding.rb +23 -29
  53. data/lib/active_support/locale/en.yml +3 -2
  54. data/lib/active_support/memoizable.rb +61 -43
  55. data/lib/active_support/message_encryptor.rb +70 -0
  56. data/lib/active_support/message_verifier.rb +46 -0
  57. data/lib/active_support/multibyte.rb +6 -30
  58. data/lib/active_support/multibyte/chars.rb +30 -9
  59. data/lib/active_support/multibyte/unicode_database.rb +4 -4
  60. data/lib/active_support/option_merger.rb +7 -1
  61. data/lib/active_support/ordered_hash.rb +75 -27
  62. data/lib/active_support/secure_random.rb +8 -6
  63. data/lib/active_support/test_case.rb +32 -17
  64. data/lib/active_support/testing/{core_ext/test/unit/assertions.rb → assertions.rb} +13 -20
  65. data/lib/active_support/testing/declarative.rb +21 -0
  66. data/lib/active_support/testing/deprecation.rb +55 -0
  67. data/lib/active_support/testing/performance.rb +1 -1
  68. data/lib/active_support/testing/setup_and_teardown.rb +57 -86
  69. data/lib/active_support/time_with_zone.rb +8 -6
  70. data/lib/active_support/values/time_zone.rb +1 -0
  71. data/lib/active_support/vendor.rb +6 -11
  72. data/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE +20 -0
  73. data/lib/active_support/vendor/i18n-0.1.3/README.textile +20 -0
  74. data/lib/active_support/vendor/i18n-0.1.3/Rakefile +5 -0
  75. data/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec +27 -0
  76. data/lib/active_support/vendor/{i18n-0.0.1 → i18n-0.1.3/lib}/i18n.rb +42 -37
  77. data/lib/active_support/vendor/{i18n-0.0.1 → i18n-0.1.3/lib}/i18n/backend/simple.rb +37 -39
  78. data/lib/active_support/vendor/{i18n-0.0.1 → i18n-0.1.3/lib}/i18n/exceptions.rb +3 -3
  79. data/lib/active_support/vendor/i18n-0.1.3/test/all.rb +5 -0
  80. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb +100 -0
  81. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +125 -0
  82. data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb +1 -0
  83. data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml +3 -0
  84. data/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +568 -0
  85. data/lib/active_support/vendor/{memcache-client-1.5.1 → memcache-client-1.6.5}/memcache.rb +381 -295
  86. data/lib/active_support/version.rb +2 -2
  87. data/lib/active_support/xml_mini.rb +31 -0
  88. data/lib/active_support/xml_mini/libxml.rb +133 -0
  89. data/lib/active_support/xml_mini/nokogiri.rb +77 -0
  90. data/lib/active_support/xml_mini/rexml.rb +108 -0
  91. metadata +85 -14
  92. data/lib/active_support/multibyte/utils.rb +0 -61
  93. data/lib/active_support/testing/core_ext/test.rb +0 -6
  94. data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +0 -1021
@@ -1,8 +1,3 @@
1
- begin
2
- require 'openssl'
3
- rescue LoadError
4
- end
5
-
6
1
  begin
7
2
  require 'securerandom'
8
3
  rescue LoadError
@@ -10,7 +5,7 @@ end
10
5
 
11
6
  module ActiveSupport
12
7
  if defined?(::SecureRandom)
13
- # Use Ruby 1.9's SecureRandom library whenever possible.
8
+ # Use Ruby's SecureRandom library if available.
14
9
  SecureRandom = ::SecureRandom # :nodoc:
15
10
  else
16
11
  # = Secure random number generator interface.
@@ -64,6 +59,13 @@ module ActiveSupport
64
59
  def self.random_bytes(n=nil)
65
60
  n ||= 16
66
61
 
62
+ unless defined? OpenSSL
63
+ begin
64
+ require 'openssl'
65
+ rescue LoadError
66
+ end
67
+ end
68
+
67
69
  if defined? OpenSSL::Random
68
70
  return OpenSSL::Random.random_bytes(n)
69
71
  end
@@ -1,24 +1,39 @@
1
- require 'test/unit/testcase'
2
- require 'active_support/testing/default'
3
- require 'active_support/testing/core_ext/test'
1
+ begin
2
+ gem 'mocha', '>= 0.9.3'
3
+ require 'mocha'
4
+ rescue LoadError
5
+ # Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
6
+ Object.const_set :Mocha, Module.new
7
+ Mocha.const_set :ExpectationError, Class.new(StandardError)
8
+ end
4
9
 
10
+ require 'test/unit/testcase'
11
+ require 'active_support/testing/setup_and_teardown'
12
+ require 'active_support/testing/assertions'
13
+ require 'active_support/testing/deprecation'
14
+ require 'active_support/testing/declarative'
5
15
 
6
16
  module ActiveSupport
7
- class TestCase < Test::Unit::TestCase
8
- # test "verify something" do
9
- # ...
10
- # end
11
- def self.test(name, &block)
12
- test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
13
- defined = instance_method(test_name) rescue false
14
- raise "#{test_name} is already defined in #{self}" if defined
15
- if block_given?
16
- define_method(test_name, &block)
17
- else
18
- define_method(test_name) do
19
- flunk "No implementation provided for #{name}"
20
- end
17
+ class TestCase < ::Test::Unit::TestCase
18
+ if defined? MiniTest
19
+ Assertion = MiniTest::Assertion
20
+ alias_method :method_name, :name
21
+ else
22
+ # TODO: Figure out how to get the Rails::BacktraceFilter into minitest/unit
23
+ if defined?(Rails) && ENV['BACKTRACE'].nil?
24
+ require 'rails/backtrace_cleaner'
25
+ Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit }
21
26
  end
27
+
28
+ Assertion = Test::Unit::AssertionFailedError
29
+
30
+ require 'active_support/testing/default'
31
+ include ActiveSupport::Testing::Default
22
32
  end
33
+
34
+ include ActiveSupport::Testing::SetupAndTeardown
35
+ include ActiveSupport::Testing::Assertions
36
+ include ActiveSupport::Testing::Deprecation
37
+ extend ActiveSupport::Testing::Declarative
23
38
  end
24
39
  end
@@ -1,9 +1,5 @@
1
- require 'test/unit/assertions'
2
- module Test
3
- module Unit
4
- #--
5
- # FIXME: no Proc#binding in Ruby 2, must change this API
6
- #++
1
+ module ActiveSupport
2
+ module Testing
7
3
  module Assertions
8
4
  # Test numeric difference between the return value of an expression as a result of what is evaluated
9
5
  # in the yielded block.
@@ -35,20 +31,17 @@ module Test
35
31
  # assert_difference 'Article.count', -1, "An Article should be destroyed" do
36
32
  # post :delete, :id => ...
37
33
  # end
38
- def assert_difference(expressions, difference = 1, message = nil, &block)
39
- expression_evaluations = Array(expressions).map do |expression|
40
- [expression, lambda do
41
- eval(expression, block.__send__(:binding))
42
- end]
43
- end
34
+ def assert_difference(expression, difference = 1, message = nil, &block)
35
+ b = block.send(:binding)
36
+ exps = Array.wrap(expression)
37
+ before = exps.map { |e| eval(e, b) }
44
38
 
45
- original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression[1].call }
46
39
  yield
47
- expression_evaluations.each_with_index do |expression, i|
48
- full_message = ""
49
- full_message << "#{message}.\n" if message
50
- full_message << "<#{expression[0]}> was the expression that failed"
51
- assert_equal original_values[i] + difference, expression[1].call, full_message
40
+
41
+ exps.each_with_index do |e, i|
42
+ error = "#{e.inspect} didn't change by #{difference}"
43
+ error = "#{message}.\n#{error}" if message
44
+ assert_equal(before[i] + difference, eval(e, b), error)
52
45
  end
53
46
  end
54
47
 
@@ -64,8 +57,8 @@ module Test
64
57
  # assert_no_difference 'Article.count', "An Article should not be destroyed" do
65
58
  # post :create, :article => invalid_attributes
66
59
  # end
67
- def assert_no_difference(expressions, message = nil, &block)
68
- assert_difference expressions, 0, message, &block
60
+ def assert_no_difference(expression, message = nil, &block)
61
+ assert_difference expression, 0, message, &block
69
62
  end
70
63
  end
71
64
  end
@@ -0,0 +1,21 @@
1
+ module ActiveSupport
2
+ module Testing
3
+ module Declarative
4
+ # test "verify something" do
5
+ # ...
6
+ # end
7
+ def test(name, &block)
8
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
9
+ defined = instance_method(test_name) rescue false
10
+ raise "#{test_name} is already defined in #{self}" if defined
11
+ if block_given?
12
+ define_method(test_name, &block)
13
+ else
14
+ define_method(test_name) do
15
+ flunk "No implementation provided for #{name}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,55 @@
1
+ module ActiveSupport
2
+ module Testing
3
+ module Deprecation #:nodoc:
4
+ def assert_deprecated(match = nil, &block)
5
+ result, warnings = collect_deprecations(&block)
6
+ assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
7
+ if match
8
+ match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
9
+ assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
10
+ end
11
+ result
12
+ end
13
+
14
+ def assert_not_deprecated(&block)
15
+ result, deprecations = collect_deprecations(&block)
16
+ assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
17
+ result
18
+ end
19
+
20
+ private
21
+ def collect_deprecations
22
+ old_behavior = ActiveSupport::Deprecation.behavior
23
+ deprecations = []
24
+ ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
25
+ deprecations << message
26
+ end
27
+ result = yield
28
+ [result, deprecations]
29
+ ensure
30
+ ActiveSupport::Deprecation.behavior = old_behavior
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ begin
37
+ require 'test/unit/error'
38
+
39
+ module Test
40
+ module Unit
41
+ class Error # :nodoc:
42
+ # Silence warnings when reporting test errors.
43
+ def message_with_silenced_deprecation
44
+ ActiveSupport::Deprecation.silence do
45
+ message_without_silenced_deprecation
46
+ end
47
+ end
48
+
49
+ alias_method_chain :message, :silenced_deprecation
50
+ end
51
+ end
52
+ end
53
+ rescue LoadError
54
+ # Using miniunit, ignore.
55
+ end
@@ -12,7 +12,7 @@ module ActiveSupport
12
12
  if benchmark = ARGV.include?('--benchmark') # HAX for rake test
13
13
  { :benchmark => true,
14
14
  :runs => 4,
15
- :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time],
15
+ :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time],
16
16
  :output => 'tmp/performance' }
17
17
  else
18
18
  { :benchmark => false,
@@ -1,119 +1,90 @@
1
+ require 'active_support/callbacks'
2
+
1
3
  module ActiveSupport
2
4
  module Testing
3
5
  module SetupAndTeardown
4
- # For compatibility with Ruby < 1.8.6
5
- PASSTHROUGH_EXCEPTIONS =
6
- if defined?(Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS)
7
- Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS
8
- else
9
- [NoMemoryError, SignalException, Interrupt, SystemExit]
10
- end
11
-
12
6
  def self.included(base)
13
7
  base.class_eval do
14
8
  include ActiveSupport::Callbacks
15
9
  define_callbacks :setup, :teardown
16
10
 
17
- if defined?(::Mini)
18
- undef_method :run
19
- alias_method :run, :run_with_callbacks_and_miniunit
11
+ if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
12
+ include ForMiniTest
20
13
  else
21
- begin
22
- require 'mocha'
23
- undef_method :run
24
- alias_method :run, :run_with_callbacks_and_mocha
25
- rescue LoadError
26
- undef_method :run
27
- alias_method :run, :run_with_callbacks_and_testunit
28
- end
14
+ include ForClassicTestUnit
29
15
  end
30
16
  end
31
17
  end
32
18
 
33
- def run_with_callbacks_and_miniunit(runner)
34
- result = '.'
35
- begin
36
- run_callbacks :setup
37
- result = super
38
- rescue Exception => e
39
- result = runner.puke(self.class, self.name, e)
40
- ensure
19
+ module ForMiniTest
20
+ def run(runner)
21
+ result = '.'
41
22
  begin
42
- teardown
43
- run_callbacks :teardown, :enumerator => :reverse_each
23
+ run_callbacks :setup
24
+ result = super
44
25
  rescue Exception => e
45
26
  result = runner.puke(self.class, self.name, e)
27
+ ensure
28
+ begin
29
+ run_callbacks :teardown, :enumerator => :reverse_each
30
+ rescue Exception => e
31
+ result = runner.puke(self.class, self.name, e)
32
+ end
46
33
  end
34
+ result
47
35
  end
48
- result
49
36
  end
50
37
 
51
- # This redefinition is unfortunate but test/unit shows us no alternative.
52
- def run_with_callbacks_and_testunit(result) #:nodoc:
53
- return if @method_name.to_s == "default_test"
38
+ module ForClassicTestUnit
39
+ # For compatibility with Ruby < 1.8.6
40
+ PASSTHROUGH_EXCEPTIONS = Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS rescue [NoMemoryError, SignalException, Interrupt, SystemExit]
54
41
 
55
- yield(Test::Unit::TestCase::STARTED, name)
56
- @_result = result
57
- begin
58
- run_callbacks :setup
59
- setup
60
- __send__(@method_name)
61
- rescue Test::Unit::AssertionFailedError => e
62
- add_failure(e.message, e.backtrace)
63
- rescue *PASSTHROUGH_EXCEPTIONS
64
- raise
65
- rescue Exception
66
- add_error($!)
67
- ensure
68
- begin
69
- teardown
70
- run_callbacks :teardown, :enumerator => :reverse_each
71
- rescue Test::Unit::AssertionFailedError => e
72
- add_failure(e.message, e.backtrace)
73
- rescue *PASSTHROUGH_EXCEPTIONS
74
- raise
75
- rescue Exception
76
- add_error($!)
77
- end
78
- end
79
- result.add_run
80
- yield(Test::Unit::TestCase::FINISHED, name)
81
- end
42
+ # This redefinition is unfortunate but test/unit shows us no alternative.
43
+ # Doubly unfortunate: hax to support Mocha's hax.
44
+ def run(result)
45
+ return if @method_name.to_s == "default_test"
82
46
 
83
- # Doubly unfortunate: mocha does the same so we have to hax their hax.
84
- def run_with_callbacks_and_mocha(result)
85
- return if @method_name.to_s == "default_test"
47
+ if using_mocha = respond_to?(:mocha_verify)
48
+ assertion_counter_klass = if defined?(Mocha::TestCaseAdapter::AssertionCounter)
49
+ Mocha::TestCaseAdapter::AssertionCounter
50
+ else
51
+ Mocha::Integration::TestUnit::AssertionCounter
52
+ end
53
+ assertion_counter = assertion_counter_klass.new(result)
54
+ end
86
55
 
87
- yield(Test::Unit::TestCase::STARTED, name)
88
- @_result = result
89
- begin
90
- mocha_setup
56
+ yield(Test::Unit::TestCase::STARTED, name)
57
+ @_result = result
91
58
  begin
92
- run_callbacks :setup
93
- setup
94
- __send__(@method_name)
95
- mocha_verify { add_assertion }
96
- rescue Mocha::ExpectationError => e
97
- add_failure(e.message, e.backtrace)
98
- rescue Test::Unit::AssertionFailedError => e
99
- add_failure(e.message, e.backtrace)
100
- rescue StandardError, ScriptError
101
- add_error($!)
102
- ensure
103
59
  begin
104
- teardown
105
- run_callbacks :teardown, :enumerator => :reverse_each
60
+ run_callbacks :setup
61
+ setup
62
+ __send__(@method_name)
63
+ mocha_verify(assertion_counter) if using_mocha
64
+ rescue Mocha::ExpectationError => e
65
+ add_failure(e.message, e.backtrace)
106
66
  rescue Test::Unit::AssertionFailedError => e
107
67
  add_failure(e.message, e.backtrace)
108
- rescue StandardError, ScriptError
109
- add_error($!)
68
+ rescue Exception => e
69
+ raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
70
+ add_error(e)
71
+ ensure
72
+ begin
73
+ teardown
74
+ run_callbacks :teardown, :enumerator => :reverse_each
75
+ rescue Test::Unit::AssertionFailedError => e
76
+ add_failure(e.message, e.backtrace)
77
+ rescue Exception => e
78
+ raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
79
+ add_error(e)
80
+ end
110
81
  end
82
+ ensure
83
+ mocha_teardown if using_mocha
111
84
  end
112
- ensure
113
- mocha_teardown
85
+ result.add_run
86
+ yield(Test::Unit::TestCase::FINISHED, name)
114
87
  end
115
- result.add_run
116
- yield(Test::Unit::TestCase::FINISHED, name)
117
88
  end
118
89
  end
119
90
  end
@@ -1,4 +1,5 @@
1
1
  require 'tzinfo'
2
+
2
3
  module ActiveSupport
3
4
  # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
4
5
  # limited to UTC and the system's <tt>ENV['TZ']</tt> zone.
@@ -110,7 +111,7 @@ module ActiveSupport
110
111
  # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to
111
112
  # true, the ISO 8601 format is used.
112
113
  #
113
- # ==== Examples:
114
+ # ==== Examples
114
115
  #
115
116
  # # With ActiveSupport.use_standard_json_time_format = true
116
117
  # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
@@ -154,6 +155,7 @@ module ActiveSupport
154
155
  "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
155
156
  end
156
157
  end
158
+ alias_method :to_formatted_s, :to_s
157
159
 
158
160
  # Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and +formatted_offset+, respectively, before passing to
159
161
  # Time#strftime, so that zone information is correct
@@ -228,7 +230,7 @@ module ActiveSupport
228
230
  def advance(options)
229
231
  # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
230
232
  # otherwise advance from #utc, for accuracy when moving across DST boundaries
231
- if options.detect {|k,v| [:years, :weeks, :months, :days].include? k}
233
+ if options.values_at(:years, :weeks, :months, :days).any?
232
234
  method_missing(:advance, options)
233
235
  else
234
236
  utc.advance(options).in_time_zone(time_zone)
@@ -237,9 +239,9 @@ module ActiveSupport
237
239
 
238
240
  %w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
239
241
  class_eval <<-EOV
240
- def #{method_name}
241
- time.#{method_name}
242
- end
242
+ def #{method_name} # def year
243
+ time.#{method_name} # time.year
244
+ end # end
243
245
  EOV
244
246
  end
245
247
 
@@ -326,7 +328,7 @@ module ActiveSupport
326
328
  end
327
329
 
328
330
  def duration_of_variable_length?(obj)
329
- ActiveSupport::Duration === obj && obj.parts.flatten.detect {|p| [:years, :months, :days].include? p }
331
+ ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include? p[0] }
330
332
  end
331
333
  end
332
334
  end