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
|
||||
|
||||
The MergeInitializer extension simply makes it possible to initialize a
|
||||
Hash subclass with another Hash, giving you a quick short-hand.
|
||||
|
||||
### MethodAccess
|
||||
|
||||
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? # => 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)
|
||||
|
||||
This extension *will* allow you to easily include a recursive merging
|
||||
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 is an extended Hash that gives simple pseudo-object functionality
|
||||
|
|
|
@ -1,7 +1,110 @@
|
|||
module Hashie
|
||||
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
|
||||
# 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
|
||||
|
|
|
@ -1,5 +1,66 @@
|
|||
require 'spec_helper'
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue