diff options
Diffstat (limited to 'activesupport')
23 files changed, 190 insertions, 112 deletions
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 0a12ba6cdd..23e2ce0b03 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,16 @@ ## Rails 4.0.0 (unreleased) ## +* Hash#extract! returns only those keys that present in the receiver. + + {:a => 1, :b => 2}.extract!(:a, :x) # => {:a => 1} + + *Mikhail Dieterle* + +* Hash#extract! returns the same subclass, that the receiver is. I.e. + HashWithIndifferentAccess#extract! returns HashWithIndifferentAccess instance. + + *Mikhail Dieterle* + * Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. *Brian Durand* * Tests tag the Rails log with the current test class and test case: diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 5aa78cc9f3..17450fe4d0 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -132,6 +132,10 @@ module ActiveSupport method = options && options[:unless_exist] ? :add : :set value = options[:raw] ? entry.value.to_s : entry expires_in = options[:expires_in].to_i + if expires_in > 0 && !options[:raw] + # Set the memcache expire a few minutes in the future to support race condition ttls on read + expires_in += 5.minutes + end @data.send(method, escape_key(key), value, expires_in, options) rescue Dalli::DalliError => e logger.error("DalliError (#{e}): #{e.message}") if logger diff --git a/activesupport/lib/active_support/core_ext/array/extract_options.rb b/activesupport/lib/active_support/core_ext/array/extract_options.rb index 9008a0df2a..5f153a2cc3 100644 --- a/activesupport/lib/active_support/core_ext/array/extract_options.rb +++ b/activesupport/lib/active_support/core_ext/array/extract_options.rb @@ -18,7 +18,7 @@ class Array # end # # options(1, 2) # => {} - # options(1, 2, a: :b) # => {:a=>:b} + # options(1, 2, a: :b) # => {a: :b} def extract_options! if last.is_a?(Hash) && last.extractable_options? pop diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb index 7bf28b2f27..ffaf6ef024 100644 --- a/activesupport/lib/active_support/core_ext/array/wrap.rb +++ b/activesupport/lib/active_support/core_ext/array/wrap.rb @@ -23,7 +23,7 @@ class Array # The last point is particularly worth comparing for some enumerables: # # Array(foo: :bar) # => [[:foo, :bar]] - # Array.wrap(foo: :bar) # => [{:foo => :bar}] + # Array.wrap(foo: :bar) # => [{foo: :bar}] # # There's also a related idiom that uses the splat operator: # diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 828f03ad62..f5e3a9b842 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -131,7 +131,7 @@ class Hash else xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }] - # Turn { :files => { :file => #<StringIO> } } into { :files => #<StringIO> } so it is compatible with + # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with # how multipart uploaded files from HTML appear xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value end diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb index 83f0c87b04..6ce2847ab5 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb @@ -4,10 +4,10 @@ class Hash # h1 = { x: { y: [4,5,6] }, z: [7,8,9] } # h2 = { x: { y: [7,8,9] }, z: 'xyz' } # - # h1.deep_merge(h2) #=> {:x => {:y => [7, 8, 9]}, :z => "xyz"} - # h2.deep_merge(h1) #=> {:x => {:y => [4, 5, 6]}, :z => [7, 8, 9]} + # h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"} + # h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]} # h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) } - # #=> {:x => {:y => [4, 5, 6, 7, 8, 9]}, :z => [7, 8, 9, "xyz"]} + # #=> {x: {y: [4, 5, 6, 7, 8, 9]}, z: [7, 8, 9, "xyz"]} def deep_merge(other_hash, &block) dup.deep_merge!(other_hash, &block) end diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 45fec57009..a92c5968ed 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -21,7 +21,7 @@ class Hash # Returns a hash containing the removed key/value pairs. # # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b) - # # => {:c => 3, :d => 4} + # # => {c: 3, d: 4} def slice!(*keys) keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) omit = slice(*self.keys - keys) @@ -32,9 +32,9 @@ class Hash # Removes and returns the key/value pairs matching the given keys. # - # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) - # # => {:a => 1, :b => 2} + # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => { a: 1, b: 2 } + # { a: 1, b: 2 }.extract!(:a, :x) # => { a: 1 } def extract!(*keys) - keys.each_with_object({}) { |key, result| result[key] = delete(key) } + keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) } end end diff --git a/activesupport/lib/active_support/core_ext/module/deprecation.rb b/activesupport/lib/active_support/core_ext/module/deprecation.rb index 34ec6a3d8f..cc45cee5b8 100644 --- a/activesupport/lib/active_support/core_ext/module/deprecation.rb +++ b/activesupport/lib/active_support/core_ext/module/deprecation.rb @@ -2,13 +2,13 @@ require 'active_support/deprecation/method_wrappers' class Module # deprecate :foo - # deprecate :bar => 'message' - # deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!' + # deprecate bar: 'message' + # deprecate :foo, :bar, baz: 'warning!', qux: 'gone!' # # You can also use custom deprecator instance: # - # deprecate :foo, :deprecator => MyLib::Deprecator.new - # deprecate :foo, :bar => "warning!", :deprecator => MyLib::Deprecator.new + # deprecate :foo, deprecator: MyLib::Deprecator.new + # deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new # # \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt> # method where you can implement your custom warning behavior. diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index ab01d7787a..ec2157221f 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -1,11 +1,14 @@ require 'active_support/core_ext/object/acts_like' require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/object/deep_dup' require 'active_support/core_ext/object/duplicable' +require 'active_support/core_ext/object/deep_dup' +require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/inclusion' + +require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/instance_variables' + require 'active_support/core_ext/object/to_json' require 'active_support/core_ext/object/to_param' require 'active_support/core_ext/object/to_query' -require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/with_options' diff --git a/activesupport/lib/active_support/core_ext/object/conversions.rb b/activesupport/lib/active_support/core_ext/object/conversions.rb new file mode 100644 index 0000000000..540f7aadb0 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/conversions.rb @@ -0,0 +1,4 @@ +require 'active_support/core_ext/object/to_param' +require 'active_support/core_ext/object/to_query' +require 'active_support/core_ext/array/conversions' +require 'active_support/core_ext/hash/conversions' diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index e3665cd896..931851d40e 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -135,7 +135,7 @@ class Time # Returns a new Time representing the start of the day (0:00) def beginning_of_day - #(self - seconds_since_midnight).change(:usec => 0) + #(self - seconds_since_midnight).change(usec: 0) change(:hour => 0) end alias :midnight :beginning_of_day diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb index e2a8b4d4e3..27861e01d0 100644 --- a/activesupport/lib/active_support/descendants_tracker.rb +++ b/activesupport/lib/active_support/descendants_tracker.rb @@ -2,35 +2,50 @@ module ActiveSupport # This module provides an internal implementation to track descendants # which is faster than iterating through ObjectSpace. module DescendantsTracker - @@direct_descendants = Hash.new { |h, k| h[k] = [] } + @@direct_descendants = {} - def self.direct_descendants(klass) - @@direct_descendants[klass] - end + class << self + def direct_descendants(klass) + @@direct_descendants[klass] || [] + end - def self.descendants(klass) - @@direct_descendants[klass].inject([]) do |descendants, _klass| - descendants << _klass - descendants.concat _klass.descendants + def descendants(klass) + arr = [] + accumulate_descendants(klass, arr) + arr end - end - def self.clear - if defined? ActiveSupport::Dependencies - @@direct_descendants.each do |klass, descendants| - if ActiveSupport::Dependencies.autoloaded?(klass) - @@direct_descendants.delete(klass) - else - descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) } + def clear + if defined? ActiveSupport::Dependencies + @@direct_descendants.each do |klass, descendants| + if ActiveSupport::Dependencies.autoloaded?(klass) + @@direct_descendants.delete(klass) + else + descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) } + end end + else + @@direct_descendants.clear + end + end + + # This is the only method that is not thread safe, but is only ever called + # during the eager loading phase. + def store_inherited(klass, descendant) + (@@direct_descendants[klass] ||= []) << descendant + end + + private + def accumulate_descendants(klass, acc) + if direct_descendants = @@direct_descendants[klass] + acc.concat(direct_descendants) + direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) } end - else - @@direct_descendants.clear end end def inherited(base) - self.direct_descendants << base + DescendantsTracker.store_inherited(self, base) super end diff --git a/activesupport/lib/active_support/queueing.rb b/activesupport/lib/active_support/queueing.rb index 7c352886f3..c8ba28021d 100644 --- a/activesupport/lib/active_support/queueing.rb +++ b/activesupport/lib/active_support/queueing.rb @@ -56,32 +56,6 @@ module ActiveSupport end end - # A container for multiple queues. This class delegates to a default Queue - # so that <tt>Rails.queue.push</tt> and friends will Just Work. To use this class - # with multiple queues: - # - # # In your configuration: - # Rails.queue[:image_queue] = SomeQueue.new - # Rails.queue[:mail_queue] = SomeQueue.new - # - # # In your app code: - # Rails.queue[:mail_queue].push SomeJob.new - # - class QueueContainer < DelegateClass(::Queue) - def initialize(default_queue) - @queues = { :default => default_queue } - super(default_queue) - end - - def [](queue_name) - @queues[queue_name] - end - - def []=(queue_name, queue) - @queues[queue_name] = queue - end - end - # The threaded consumer will run jobs in a background thread in # development mode or in a VM where running jobs on a thread in # production mode makes sense. @@ -90,10 +64,6 @@ module ActiveSupport # queue and joins the thread, which will ensure that all jobs # are executed before the process finally dies. class ThreadedQueueConsumer - def self.start(*args) - new(*args).start - end - def initialize(queue, options = {}) @queue = queue @logger = options[:logger] diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 12aef0d7fe..7c149df1e4 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -2,7 +2,7 @@ begin require 'ruby-prof' rescue LoadError $stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.' - exit + raise end module ActiveSupport diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index febf0eeeff..9f76f4c90b 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -3,6 +3,20 @@ require 'abstract_unit' require 'active_support/cache' class CacheKeyTest < ActiveSupport::TestCase + def test_entry_legacy_optional_ivars + legacy = Class.new(ActiveSupport::Cache::Entry) do + def initialize(value, options = {}) + @value = value + @expires_in = nil + @created_at = nil + super + end + end + + entry = legacy.new 'foo' + assert_equal 'foo', entry.value + end + def test_expand_cache_key assert_equal '1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true]) assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index b7c3b130c3..8810302f40 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -120,7 +120,7 @@ module CallbacksTest end class Child < ParentController - skip_callback :dispatch, :before, :log, :if => proc {|c| c.action_name == :update} + skip_callback :dispatch, :before, :log, :if => proc {|c| c.action_name == :update} skip_callback :dispatch, :after, :log2 end diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 0b33a63460..9dfa2cbf11 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/core_ext/array' require 'active_support/core_ext/big_decimal' +require 'active_support/core_ext/object/conversions' require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions require 'active_support/hash_with_indifferent_access' diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 53ea2aad16..7cfe7b0ea7 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/hash' require 'bigdecimal' require 'active_support/core_ext/string/access' require 'active_support/ordered_hash' +require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/deep_dup' require 'active_support/inflections' @@ -723,8 +724,32 @@ class HashExtTest < ActiveSupport::TestCase def test_extract original = {:a => 1, :b => 2, :c => 3, :d => 4} expected = {:a => 1, :b => 2} + remaining = {:c => 3, :d => 4} - assert_equal expected, original.extract!(:a, :b) + assert_equal expected, original.extract!(:a, :b, :x) + assert_equal remaining, original + end + + def test_extract_nils + original = {:a => nil, :b => nil} + expected = {:a => nil} + extracted = original.extract!(:a, :x) + + assert_equal expected, extracted + assert_equal nil, extracted[:a] + assert_equal nil, extracted[:x] + end + + def test_indifferent_extract + original = {:a => 1, 'b' => 2, :c => 3, 'd' => 4}.with_indifferent_access + expected = {:a => 1, :b => 2}.with_indifferent_access + remaining = {:c => 3, :d => 4}.with_indifferent_access + + [['a', 'b'], [:a, :b]].each do |keys| + copy = original.dup + assert_equal expected, copy.extract!(*keys) + assert_equal remaining, copy + end end def test_except diff --git a/activesupport/test/descendants_tracker_test_cases.rb b/activesupport/test/descendants_tracker_test_cases.rb index 066ec8549b..69e046998e 100644 --- a/activesupport/test/descendants_tracker_test_cases.rb +++ b/activesupport/test/descendants_tracker_test_cases.rb @@ -1,3 +1,5 @@ +require 'set' + module DescendantsTrackerTestCases class Parent extend ActiveSupport::DescendantsTracker @@ -18,15 +20,15 @@ module DescendantsTrackerTestCases ALL = [Parent, Child1, Child2, Grandchild1, Grandchild2] def test_descendants - assert_equal [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants - assert_equal [Grandchild1, Grandchild2], Child1.descendants - assert_equal [], Child2.descendants + assert_equal_sets [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants + assert_equal_sets [Grandchild1, Grandchild2], Child1.descendants + assert_equal_sets [], Child2.descendants end def test_direct_descendants - assert_equal [Child1, Child2], Parent.direct_descendants - assert_equal [Grandchild1, Grandchild2], Child1.direct_descendants - assert_equal [], Child2.direct_descendants + assert_equal_sets [Child1, Child2], Parent.direct_descendants + assert_equal_sets [Grandchild1, Grandchild2], Child1.direct_descendants + assert_equal_sets [], Child2.direct_descendants end def test_clear @@ -40,6 +42,10 @@ module DescendantsTrackerTestCases protected + def assert_equal_sets(expected, actual) + assert_equal Set.new(expected), Set.new(actual) + end + def mark_as_autoloaded(*klasses) # If ActiveSupport::Dependencies is not loaded, forget about autoloading. # This allows using AS::DescendantsTracker without AS::Dependencies. @@ -56,4 +62,4 @@ module DescendantsTrackerTestCases ActiveSupport::Dependencies.autoloaded_constants = old_autoloaded if defined? ActiveSupport::Dependencies ActiveSupport::DescendantsTracker.class_eval("@@direct_descendants").replace(old_descendants) end -end
\ No newline at end of file +end diff --git a/activesupport/test/descendants_tracker_with_autoloading_test.rb b/activesupport/test/descendants_tracker_with_autoloading_test.rb index 9180f1f977..ab1be296f8 100644 --- a/activesupport/test/descendants_tracker_with_autoloading_test.rb +++ b/activesupport/test/descendants_tracker_with_autoloading_test.rb @@ -18,17 +18,17 @@ class DescendantsTrackerWithAutoloadingTest < ActiveSupport::TestCase def test_clear_with_autoloaded_children_and_granchildren mark_as_autoloaded Child1, Grandchild1, Grandchild2 do ActiveSupport::DescendantsTracker.clear - assert_equal [Child2], Parent.descendants - assert_equal [], Child2.descendants + assert_equal_sets [Child2], Parent.descendants + assert_equal_sets [], Child2.descendants end end def test_clear_with_autoloaded_granchildren mark_as_autoloaded Grandchild1, Grandchild2 do ActiveSupport::DescendantsTracker.clear - assert_equal [Child1, Child2], Parent.descendants - assert_equal [], Child1.descendants - assert_equal [], Child2.descendants + assert_equal_sets [Child1, Child2], Parent.descendants + assert_equal_sets [], Child1.descendants + assert_equal_sets [], Child2.descendants end end end diff --git a/activesupport/test/queueing/container_test.rb b/activesupport/test/queueing/container_test.rb deleted file mode 100644 index 7afc11e7a9..0000000000 --- a/activesupport/test/queueing/container_test.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'abstract_unit' -require 'active_support/queueing' - -module ActiveSupport - class ContainerTest < ActiveSupport::TestCase - def test_delegates_to_default - q = Queue.new - container = QueueContainer.new q - job = Object.new - - container.push job - assert_equal job, q.pop - end - - def test_access_default - q = Queue.new - container = QueueContainer.new q - assert_equal q, container[:default] - end - - def test_assign_queue - container = QueueContainer.new Object.new - q = Object.new - container[:foo] = q - assert_equal q, container[:foo] - end - end -end diff --git a/activesupport/test/queueing/test_queue_test.rb b/activesupport/test/queueing/test_queue_test.rb index e398a48bea..451fb68d3e 100644 --- a/activesupport/test/queueing/test_queue_test.rb +++ b/activesupport/test/queueing/test_queue_test.rb @@ -99,4 +99,48 @@ class TestQueueTest < ActiveSupport::TestCase assert job.ran?, "The job runs synchronously when the queue is drained" assert_equal job.thread_id, Thread.current.object_id end + + class IdentifiableJob + def initialize(id) + @id = id + end + + def ==(other) + other.same_id?(@id) + end + + def same_id?(other_id) + other_id == @id + end + + def run + end + end + + def test_queue_can_be_observed + jobs = (1..10).map do |id| + IdentifiableJob.new(id) + end + + jobs.each do |job| + @queue.push job + end + + assert_equal jobs, @queue.jobs + end + + def test_adding_an_unmarshallable_job + anonymous_class_instance = Struct.new(:run).new + + assert_raises TypeError do + @queue.push anonymous_class_instance + end + end + + def test_attempting_to_add_a_reference_to_itself + job = {reference: @queue} + assert_raises TypeError do + @queue.push job + end + end end diff --git a/activesupport/test/testing/performance_test.rb b/activesupport/test/testing/performance_test.rb index 53073cb8db..6918110cce 100644 --- a/activesupport/test/testing/performance_test.rb +++ b/activesupport/test/testing/performance_test.rb @@ -1,10 +1,19 @@ require 'abstract_unit' -require 'active_support/testing/performance' - module ActiveSupport module Testing class PerformanceTest < ActiveSupport::TestCase + begin + require 'active_support/testing/performance' + HAVE_RUBYPROF = true + rescue LoadError + HAVE_RUBYPROF = false + end + + def setup + skip "no rubyprof" unless HAVE_RUBYPROF + end + def test_amount_format amount_metric = ActiveSupport::Testing::Performance::Metrics[:amount].new assert_equal "0", amount_metric.format(0) @@ -56,4 +65,4 @@ module ActiveSupport end end end -end
\ No newline at end of file +end |