Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Michael Bleigh
committed
Aug 2, 2011
1 parent
82295cc
commit 3bd7979
Showing
4 changed files
with
183 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
-m markdown |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |