Adds IndifferentAccess.
This commit is contained in:
parent
82295cc65f
commit
3bd79790bc
|
@ -65,6 +65,9 @@ counterparts. You can also include just stringify or just symbolize with
|
||||||
|
|
||||||
### MergeInitializer
|
### MergeInitializer
|
||||||
|
|
||||||
|
The MergeInitializer extension simply makes it possible to initialize a
|
||||||
|
Hash subclass with another Hash, giving you a quick short-hand.
|
||||||
|
|
||||||
### MethodAccess
|
### MethodAccess
|
||||||
|
|
||||||
The MethodAccess extension allows you to quickly build method-based
|
The MethodAccess extension allows you to quickly build method-based
|
||||||
|
@ -81,17 +84,24 @@ included as individual modules, i.e. `Hashie::Extensions::MethodReader`,
|
||||||
h.abc # => 'def'
|
h.abc # => 'def'
|
||||||
h.abc? # => true
|
h.abc? # => true
|
||||||
|
|
||||||
|
### IndifferentAccess
|
||||||
|
|
||||||
|
This extension can be mixed in to instantly give you indifferent access
|
||||||
|
to your Hash subclass. This works just like the params hash in Rails and
|
||||||
|
other frameworks where whether you provide symbols or strings to access
|
||||||
|
keys, you will get the same results.
|
||||||
|
|
||||||
|
A unique feature of Hashie's IndifferentAccess mixin is that it will
|
||||||
|
inject itself recursively into subhashes *without* reinitializing the
|
||||||
|
hash in question. This means you can safely merge together indifferent
|
||||||
|
and non-indifferent hashes arbitrarily deeply without worrying about
|
||||||
|
whether you'll be able to `hash[:other][:another]` properly.
|
||||||
|
|
||||||
### DeepMerge (Unimplemented)
|
### DeepMerge (Unimplemented)
|
||||||
|
|
||||||
This extension *will* allow you to easily include a recursive merging
|
This extension *will* allow you to easily include a recursive merging
|
||||||
system to any Hash descendant.
|
system to any Hash descendant.
|
||||||
|
|
||||||
### IndifferentAccess (Unimplemented)
|
|
||||||
|
|
||||||
This extension *will* allow you to easily give a hash rules for
|
|
||||||
normalizing keys, for instance to allow symbol or string keys both to
|
|
||||||
reach the intended value.
|
|
||||||
|
|
||||||
## Mash
|
## Mash
|
||||||
|
|
||||||
Mash is an extended Hash that gives simple pseudo-object functionality
|
Mash is an extended Hash that gives simple pseudo-object functionality
|
||||||
|
|
|
@ -1,7 +1,110 @@
|
||||||
module Hashie
|
module Hashie
|
||||||
module Extensions
|
module Extensions
|
||||||
|
# IndifferentAccess gives you the ability to not care
|
||||||
|
# whether your hash has string or symbol keys. Made famous
|
||||||
|
# in Rails for accessing query and POST parameters, this
|
||||||
|
# is a handy tool for making sure your hash has maximum
|
||||||
|
# utility.
|
||||||
|
#
|
||||||
|
# One unique feature of this mixin is that it will recursively
|
||||||
|
# inject itself into sub-hash instances without modifying
|
||||||
|
# the actual class of the sub-hash.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# class MyHash < Hash
|
||||||
|
# include Hashie::Extensions::MergeInitializer
|
||||||
|
# include Hashie::Extensions::IndifferentAccess
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# h = MyHash.new(:foo => 'bar', 'baz' => 'blip')
|
||||||
|
# h['foo'] # => 'bar'
|
||||||
|
# h[:foo] # => 'bar'
|
||||||
|
# h[:baz] # => 'blip'
|
||||||
|
# h['baz'] # => 'blip'
|
||||||
|
#
|
||||||
module IndifferentAccess
|
module IndifferentAccess
|
||||||
# TODO: Implement indifferent access.
|
def self.included(base)
|
||||||
|
base.class_eval do
|
||||||
|
alias_method :regular_writer, :[]=
|
||||||
|
alias_method :[]=, :indifferent_writer
|
||||||
|
%w(default update fetch delete key? values_at).each do |m|
|
||||||
|
alias_method "regular_#{m}", m
|
||||||
|
alias_method m, "indifferent_#{m}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This will inject indifferent access into an instance of
|
||||||
|
# a hash without modifying the actual class. This is what
|
||||||
|
# allows IndifferentAccess to spread to sub-hashes.
|
||||||
|
def self.inject!(hash)
|
||||||
|
hash.singleton_class.send :include, Hashie::Extensions::IndifferentAccess
|
||||||
|
hash.convert!
|
||||||
|
end
|
||||||
|
|
||||||
|
# Injects indifferent access into a duplicate of the hash
|
||||||
|
# provided. See #inject!
|
||||||
|
def self.inject(hash)
|
||||||
|
inject!(hash.dup)
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_key(key)
|
||||||
|
key.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Iterates through the keys and values, reconverting them to
|
||||||
|
# their proper indifferent state. Used when IndifferentAccess
|
||||||
|
# is injecting itself into member hashes.
|
||||||
|
def convert!
|
||||||
|
keys.each do |k|
|
||||||
|
regular_writer convert_key(k), convert_value(self.regular_delete(k))
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_value(value)
|
||||||
|
if hash_lacking_indifference?(value)
|
||||||
|
Hashie::Extensions::IndifferentAccess.inject(value.dup)
|
||||||
|
elsif value.is_a?(::Array)
|
||||||
|
value.dup.replace(value.map { |e| convert_value(e) })
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def indifferent_default(key = nil)
|
||||||
|
return self[convert_key(key)] if key?(key)
|
||||||
|
regular_default(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def indifferent_update(other_hash)
|
||||||
|
return regular_update(other_hash) if hash_with_indifference?(other_hash)
|
||||||
|
other_hash.each_pair do |k,v|
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def indifferent_writer(key, value); regular_writer convert_key(key), convert_value(value) end
|
||||||
|
def indifferent_fetch(key, *args); regular_fetch convert_key(key), *args end
|
||||||
|
def indifferent_delete(key); regular_delete convert_key(key) end
|
||||||
|
def indifferent_key?(key); regular_key? convert_key(key) end
|
||||||
|
def indifferent_values_at(*indices); indices.map{|i| self[i] } end
|
||||||
|
|
||||||
|
def indifferent_access?; true end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def hash_lacking_indifference?(other)
|
||||||
|
other.is_a?(::Hash) &&
|
||||||
|
!(other.respond_to?(:indifferent_access?) &&
|
||||||
|
other.indifferent_access?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash_with_indifference?(other)
|
||||||
|
other.is_a?(::Hash) &&
|
||||||
|
other.respond_to?(:indifferent_access?) &&
|
||||||
|
other.indifference_access?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,66 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Hashie::Extensions::IndifferentAccess do
|
describe Hashie::Extensions::IndifferentAccess do
|
||||||
|
class IndifferentHash < Hash
|
||||||
|
include Hashie::Extensions::MergeInitializer
|
||||||
|
include Hashie::Extensions::IndifferentAccess
|
||||||
|
end
|
||||||
|
subject{ IndifferentHash }
|
||||||
|
|
||||||
|
it 'should be able to access via string or symbol' do
|
||||||
|
h = subject.new(:abc => 123)
|
||||||
|
h[:abc].should == 123
|
||||||
|
h['abc'].should == 123
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#values_at' do
|
||||||
|
it 'should indifferently find values' do
|
||||||
|
h = subject.new(:foo => 'bar', 'baz' => 'qux')
|
||||||
|
h.values_at('foo', :baz).should == %w(bar qux)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#fetch' do
|
||||||
|
it 'should work like normal fetch, but indifferent' do
|
||||||
|
h = subject.new(:foo => 'bar')
|
||||||
|
h.fetch(:foo).should == h.fetch('foo')
|
||||||
|
h.fetch(:foo).should == 'bar'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#delete' do
|
||||||
|
it 'should delete indifferently' do
|
||||||
|
h = subject.new(:foo => 'bar', 'baz' => 'qux')
|
||||||
|
h.delete('foo')
|
||||||
|
h.delete(:baz)
|
||||||
|
h.should be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#key?' do
|
||||||
|
it 'should find it indifferently' do
|
||||||
|
h = subject.new(:foo => 'bar')
|
||||||
|
h.should be_key(:foo)
|
||||||
|
h.should be_key('foo')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update' do
|
||||||
|
subject{ IndifferentHash.new(:foo => 'bar') }
|
||||||
|
it 'should allow keys to be indifferent still' do
|
||||||
|
subject.update(:baz => 'qux')
|
||||||
|
subject['foo'].should == 'bar'
|
||||||
|
subject['baz'].should == 'qux'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should recursively inject indifference into sub-hashes' do
|
||||||
|
subject.update(:baz => {:qux => 'abc'})
|
||||||
|
subject['baz']['qux'].should == 'abc'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not change the ancestors of the injected object class' do
|
||||||
|
subject.update(:baz => {:qux => 'abc'})
|
||||||
|
Hash.new.should_not be_respond_to(:indifferent_access?)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue