Skip to content

Commit

Permalink
Limit Array#extract_options! to directl instances of Hash and HWIA. A…
Browse files Browse the repository at this point in the history
…dd extractable_options? to Hash so that subclasses of Hash can opt-into extractable behavior. This fixes an issue where respond_with wasn't working with subclasses of Hash that were provided by other libraries (such as CouchDB or Mashie) [#4145 state:resolved]
  • Loading branch information
wycats committed Mar 27, 2010
1 parent b081948 commit a24a888
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
@@ -1,3 +1,14 @@
class Hash
# By default, only instances of Hash itself are extractable.
# Subclasses of Hash may implement this method and return
# true to declare themselves as extractable. If a Hash
# is extractable, Array#extract_options! pops it from
# the Array when it is the last element of the Array.
def extractable_options?
instance_of?(Hash)
end
end

class Array
# Extracts options from a set of arguments. Removes and returns the last
# element in the array if it's a hash, otherwise returns a blank hash.
Expand All @@ -9,6 +20,10 @@ class Array
# options(1, 2) # => {}
# options(1, 2, :a => :b) # => {:a=>:b}
def extract_options!
last.is_a?(::Hash) ? pop : {}
if last.is_a?(Hash) && last.extractable_options?
pop
else
{}
end
end
end
Expand Up @@ -4,6 +4,10 @@

module ActiveSupport
class HashWithIndifferentAccess < Hash
def extractable_options?
true
end

def initialize(constructor = {})
if constructor.is_a?(Hash)
super()
Expand Down
34 changes: 34 additions & 0 deletions activesupport/test/core_ext/array_ext_test.rb
Expand Up @@ -4,6 +4,7 @@
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'

class ArrayExtAccessTests < Test::Unit::TestCase
def test_from
Expand Down Expand Up @@ -294,12 +295,45 @@ def test_to_xml_dups_options
end

class ArrayExtractOptionsTests < Test::Unit::TestCase
class HashSubclass < Hash
end

class ExtractableHashSubclass < Hash
def extractable_options?
true
end
end

def test_extract_options
assert_equal({}, [].extract_options!)
assert_equal({}, [1].extract_options!)
assert_equal({:a=>:b}, [{:a=>:b}].extract_options!)
assert_equal({:a=>:b}, [1, {:a=>:b}].extract_options!)
end

def test_extract_options_doesnt_extract_hash_subclasses
hash = HashSubclass.new
hash[:foo] = 1
array = [hash]
options = array.extract_options!
assert_equal({}, options)
assert_equal [hash], array
end

def test_extract_options_extracts_extractable_subclass
hash = ExtractableHashSubclass.new
hash[:foo] = 1
array = [hash]
options = array.extract_options!
assert_equal({:foo => 1}, options)
assert_equal [], array
end

def test_extract_options_extracts_hwia
hash = [{:foo => 1}.with_indifferent_access]
options = hash.extract_options!
assert_equal 1, options[:foo]
end
end

class ArrayUniqByTests < Test::Unit::TestCase
Expand Down

0 comments on commit a24a888

Please sign in to comment.