hashie-2.0.3/0000755000004100000410000000000012133445414013006 5ustar www-datawww-datahashie-2.0.3/.travis.yml0000644000004100000410000000011312133445414015112 0ustar www-datawww-datarvm: - 2.0.0 - 1.8.7 - 1.9.2 - rbx - ree - ruby-head - jruby hashie-2.0.3/CONTRIBUTING.md0000644000004100000410000000231712133445414015242 0ustar www-datawww-data## Note on Patches/Pull Requests Thanks for taking the time to contribute back! To make it easier for us to review your changes, try to follow these guidelines: * Keep changesets small and on topic. Itching to refactor or clean something up? Do it in a separate branch. * Stay consistent with existing code conventions. * Break changes into smaller logical commits. To propose a change: * [Fork the project.](https://help.github.com/articles/fork-a-repo) * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * [Send me a pull request](https://help.github.com/articles/using-pull-requests). Bonus points for topic branches. * [Check that your pull request passes the build](https://travis-ci.org/intridea/hashie/pull_requests). ## Bug triage Have a problem? File an [issue here](https://github.com/intridea/hashie/issues). To make bug squashing easier, include the following in your issue: * What version of hashie are you using? * Is it still a problem in master?hashie-2.0.3/LICENSE0000644000004100000410000000204212133445414014011 0ustar www-datawww-dataCopyright (c) 2009 Intridea, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hashie-2.0.3/Rakefile0000644000004100000410000000037312133445414014456 0ustar www-datawww-datarequire 'rubygems' require 'bundler' Bundler.setup Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new do |spec| # spec.libs << 'lib' << 'spec' spec.pattern = 'spec/**/*_spec.rb' end task :default => :spec hashie-2.0.3/hashie.gemspec0000644000004100000410000000207312133445414015616 0ustar www-datawww-datarequire File.expand_path('../lib/hashie/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["Michael Bleigh", "Jerry Cheung"] gem.email = ["michael@intridea.com", "jollyjerry@gmail.com"] gem.description = %q{Hashie is a small collection of tools that make hashes more powerful. Currently includes Mash (Mocking Hash) and Dash (Discrete Hash).} gem.summary = %q{Your friendly neighborhood hash toolkit.} gem.homepage = 'https://github.com/intridea/hashie' gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") gem.name = "hashie" gem.require_paths = ['lib'] gem.version = Hashie::VERSION gem.license = "MIT" gem.add_development_dependency 'rake', '~> 0.9.2' gem.add_development_dependency 'rspec', '~> 2.5' gem.add_development_dependency 'guard' gem.add_development_dependency 'guard-rspec' gem.add_development_dependency 'growl' end hashie-2.0.3/spec/0000755000004100000410000000000012133445414013740 5ustar www-datawww-datahashie-2.0.3/spec/spec.opts0000644000004100000410000000004612133445414015601 0ustar www-datawww-data--colour --format progress --backtracehashie-2.0.3/spec/hashie/0000755000004100000410000000000012133445414015201 5ustar www-datawww-datahashie-2.0.3/spec/hashie/trash_spec.rb0000644000004100000410000001143312133445414017663 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Trash do class TrashTest < Hashie::Trash property :first_name, :from => :firstName end let(:trash) { TrashTest.new } describe 'translating properties' do it 'adds the property to the list' do TrashTest.properties.should include(:first_name) end it 'creates a method for reading the property' do trash.should respond_to(:first_name) end it 'creates a method for writing the property' do trash.should respond_to(:first_name=) end it 'creates a method for writing the translated property' do trash.should respond_to(:firstName=) end it 'does not create a method for reading the translated property' do trash.should_not respond_to(:firstName) end end describe 'writing to properties' do it 'does not write to a non-existent property using []=' do lambda{trash['abc'] = 123}.should raise_error(NoMethodError) end it 'writes to an existing property using []=' do lambda{trash['first_name'] = 'Bob'}.should_not raise_error end it 'writes to a translated property using []=' do lambda{trash['firstName'] = 'Bob'}.should_not raise_error end it 'reads/writes to an existing property using a method call' do trash.first_name = 'Franklin' trash.first_name.should == 'Franklin' end it 'writes to an translated property using a method call' do trash.firstName = 'Franklin' trash.first_name.should == 'Franklin' end it 'writes to a translated property using #replace' do trash.replace(:firstName => 'Franklin') trash.first_name.should == 'Franklin' end it 'writes to a non-translated property using #replace' do trash.replace(:first_name => 'Franklin') trash.first_name.should == 'Franklin' end end describe ' initializing with a Hash' do it 'does not initialize non-existent properties' do lambda{TrashTest.new(:bork => 'abc')}.should raise_error(NoMethodError) end it 'sets the desired properties' do TrashTest.new(:first_name => 'Michael').first_name.should == 'Michael' end context "with both the translated property and the property" do it 'sets the desired properties' do TrashTest.new(:first_name => 'Michael', :firstName=>'Maeve').first_name.should == 'Michael' end end it 'sets the translated properties' do TrashTest.new(:firstName => 'Michael').first_name.should == 'Michael' end end describe 'translating properties using a proc' do class TrashLambdaTest < Hashie::Trash property :first_name, :from => :firstName, :with => lambda { |value| value.reverse } end let(:lambda_trash) { TrashLambdaTest.new } it 'should translate the value given on initialization with the given lambda' do TrashLambdaTest.new(:firstName => 'Michael').first_name.should == 'Michael'.reverse end it 'should not translate the value if given with the right property' do TrashTest.new(:first_name => 'Michael').first_name.should == 'Michael' end it 'should translate the value given as property with the given lambda' do lambda_trash.firstName = 'Michael' lambda_trash.first_name.should == 'Michael'.reverse end it 'should not translate the value given as right property' do lambda_trash.first_name = 'Michael' lambda_trash.first_name.should == 'Michael' end end describe 'translating properties without from option using a proc' do class TrashLambdaTest2 < Hashie::Trash property :first_name, :transform_with => lambda { |value| value.reverse } end let(:lambda_trash) { TrashLambdaTest2.new } it 'should translate the value given as property with the given lambda' do lambda_trash.first_name = 'Michael' lambda_trash.first_name.should == 'Michael'.reverse end it 'should transform the value when given in constructor' do TrashLambdaTest2.new(:first_name => 'Michael').first_name.should == 'Michael'.reverse end context "when :from option is given" do class TrashLambdaTest3 < Hashie::Trash property :first_name, :from => :firstName, :transform_with => lambda { |value| value.reverse } end it 'should not override the :from option in the constructor' do TrashLambdaTest3.new(:first_name => 'Michael').first_name.should == 'Michael' end it 'should not override the :from option when given as property' do t = TrashLambdaTest3.new t.first_name = 'Michael' t.first_name.should == 'Michael' end end end it "should raise an error when :from have the same value as property" do expect { class WrongTrash < Hashie::Trash property :first_name, :from => :first_name end }.to raise_error(ArgumentError) end end hashie-2.0.3/spec/hashie/clash_spec.rb0000644000004100000410000000263112133445414017634 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Clash do before do @c = Hashie::Clash.new end it 'should be able to set an attribute via method_missing' do @c.foo('bar') @c[:foo].should == 'bar' end it 'should be able to set multiple attributes' do @c.foo('bar').baz('wok') @c.should == {:foo => 'bar', :baz => 'wok'} end it 'should convert multiple arguments into an array' do @c.foo(1, 2, 3) @c[:foo].should == [1,2,3] end it 'should be able to use bang notation to create a new Clash on a key' do @c.foo! @c[:foo].should be_kind_of(Hashie::Clash) end it 'should be able to chain onto the new Clash when using bang notation' do @c.foo!.bar('abc').baz(123) @c.should == {:foo => {:bar => 'abc', :baz => 123}} end it 'should be able to jump back up to the parent in the chain with #_end!' do @c.foo!.bar('abc')._end!.baz(123) @c.should == {:foo => {:bar => 'abc'}, :baz => 123} end it 'should merge rather than replace existing keys' do @c.where(:abc => 'def').where(:hgi => 123) @c.should == {:where => {:abc => 'def', :hgi => 123}} end it 'should be able to replace all of its own keys with #replace' do @c.foo(:bar).hello(:world) @c.replace(:baz => 123, :hgi => 123).should == {:baz => 123, :hgi => 123} @c.should == {:baz => 123, :hgi => 123} @c[:foo].should be_nil @c[:hello].should be_nil end end hashie-2.0.3/spec/hashie/mash_spec.rb0000644000004100000410000003102112133445414017465 0ustar www-datawww-datarequire 'spec_helper' require 'delegate' describe Hashie::Mash do before(:each) do @mash = Hashie::Mash.new end it "should inherit from hash" do @mash.is_a?(Hash).should be_true end it "should be able to set hash values through method= calls" do @mash.test = "abc" @mash["test"].should == "abc" end it "should be able to retrieve set values through method calls" do @mash["test"] = "abc" @mash.test.should == "abc" end it "should be able to retrieve set values through blocks" do @mash["test"] = "abc" value = nil @mash.[]("test") { |v| value = v } value.should == "abc" end it "should be able to retrieve set values through blocks with method calls" do @mash["test"] = "abc" value = nil @mash.test { |v| value = v } value.should == "abc" end it "should test for already set values when passed a ? method" do @mash.test?.should be_false @mash.test = "abc" @mash.test?.should be_true end it "should return false on a ? method if a value has been set to nil or false" do @mash.test = nil @mash.should_not be_test @mash.test = false @mash.should_not be_test end it "should make all [] and []= into strings for consistency" do @mash["abc"] = 123 @mash.key?('abc').should be_true @mash["abc"].should == 123 end it "should have a to_s that is identical to its inspect" do @mash.abc = 123 @mash.to_s.should == @mash.inspect end it "should return nil instead of raising an error for attribute-esque method calls" do @mash.abc.should be_nil end it "should return a Hashie::Mash when passed a bang method to a non-existenct key" do @mash.abc!.is_a?(Hashie::Mash).should be_true end it "should return the existing value when passed a bang method for an existing key" do @mash.name = "Bob" @mash.name!.should == "Bob" end it "should return a Hashie::Mash when passed an under bang method to a non-existenct key" do @mash.abc_.is_a?(Hashie::Mash).should be_true end it "should return the existing value when passed an under bang method for an existing key" do @mash.name = "Bob" @mash.name_.should == "Bob" end it "#initializing_reader should return a Hashie::Mash when passed a non-existent key" do @mash.initializing_reader(:abc).is_a?(Hashie::Mash).should be_true end it "should allow for multi-level assignment through bang methods" do @mash.author!.name = "Michael Bleigh" @mash.author.should == Hashie::Mash.new(:name => "Michael Bleigh") @mash.author!.website!.url = "http://www.mbleigh.com/" @mash.author.website.should == Hashie::Mash.new(:url => "http://www.mbleigh.com/") end it "should allow for multi-level under bang testing" do @mash.author_.website_.url.should be_nil @mash.author_.website_.url?.should == false @mash.author.should be_nil end it "should not call super if id is not a key" do @mash.id.should == nil end it "should return the value if id is a key" do @mash.id = "Steve" @mash.id.should == "Steve" end it "should not call super if type is not a key" do @mash.type.should == nil end it "should return the value if type is a key" do @mash.type = "Steve" @mash.type.should == "Steve" end context "updating" do subject { described_class.new :first_name => "Michael", :last_name => "Bleigh", :details => {:email => "michael@asf.com", :address => "Nowhere road"} } describe "#deep_update" do it "should recursively Hashie::Mash Hashie::Mashes and hashes together" do subject.deep_update(:details => {:email => "michael@intridea.com", :city => "Imagineton"}) subject.first_name.should == "Michael" subject.details.email.should == "michael@intridea.com" subject.details.address.should == "Nowhere road" subject.details.city.should == "Imagineton" end it "should make #update deep by default" do subject.update(:details => {:address => "Fake street"}).should eql(subject) subject.details.address.should == "Fake street" subject.details.email.should == "michael@asf.com" end it "should clone before a #deep_merge" do duped = subject.deep_merge(:details => {:address => "Fake street"}) duped.should_not eql(subject) duped.details.address.should == "Fake street" subject.details.address.should == "Nowhere road" duped.details.email.should == "michael@asf.com" end it "regular #merge should be deep" do duped = subject.merge(:details => {:email => "michael@intridea.com"}) duped.should_not eql(subject) duped.details.email.should == "michael@intridea.com" duped.details.address.should == "Nowhere road" end # http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-update it "accepts a block" do duped = subject.merge(:details => {:address => "Pasadena CA"}) {|key, oldv, newv| [oldv, newv].join(', ')} duped.details.address.should == 'Nowhere road, Pasadena CA' end end describe "shallow update" do it "should shallowly Hashie::Mash Hashie::Mashes and hashes together" do subject.shallow_update(:details => { :email => "michael@intridea.com", :city => "Imagineton" }).should eql(subject) subject.first_name.should == "Michael" subject.details.email.should == "michael@intridea.com" subject.details.address.should be_nil subject.details.city.should == "Imagineton" end it "should clone before a #regular_merge" do duped = subject.shallow_merge(:details => {:address => "Fake street"}) duped.should_not eql(subject) end it "regular merge should be shallow" do duped = subject.shallow_merge(:details => {:address => "Fake street"}) duped.details.address.should == "Fake street" subject.details.address.should == "Nowhere road" duped.details.email.should be_nil end end describe '#replace' do before do subject.replace(:middle_name => "Cain", :details => {:city => "Imagination"}) end it 'return self' do subject.replace(:foo => "bar").to_hash.should == {"foo" => "bar"} end it 'sets all specified keys to their corresponding values' do subject.middle_name?.should be_true subject.details?.should be_true subject.middle_name.should == "Cain" subject.details.city?.should be_true subject.details.city.should == "Imagination" end it 'leaves only specified keys' do subject.keys.sort.should == ['details', 'middle_name'] subject.first_name?.should be_false subject.should_not respond_to(:first_name) subject.last_name?.should be_false subject.should_not respond_to(:last_name) end end describe 'delete' do it 'should delete with String key' do subject.delete('details') subject.details.should be_nil subject.should_not be_respond_to :details end it 'should delete with Symbol key' do subject.delete(:details) subject.details.should be_nil subject.should_not be_respond_to :details end end end it "should convert hash assignments into Hashie::Mashes" do @mash.details = {:email => 'randy@asf.com', :address => {:state => 'TX'} } @mash.details.email.should == 'randy@asf.com' @mash.details.address.state.should == 'TX' end it "should not convert the type of Hashie::Mashes childs to Hashie::Mash" do class MyMash < Hashie::Mash end record = MyMash.new record.son = MyMash.new record.son.class.should == MyMash end it "should not change the class of Mashes when converted" do class SubMash < Hashie::Mash end record = Hashie::Mash.new son = SubMash.new record['submash'] = son record['submash'].should be_kind_of(SubMash) end it "should respect the class when passed a bang method for a non-existent key" do record = Hashie::Mash.new record.non_existent!.should be_kind_of(Hashie::Mash) class SubMash < Hashie::Mash end son = SubMash.new son.non_existent!.should be_kind_of(SubMash) end it "should respect the class when passed an under bang method for a non-existent key" do record = Hashie::Mash.new record.non_existent_.should be_kind_of(Hashie::Mash) class SubMash < Hashie::Mash end son = SubMash.new son.non_existent_.should be_kind_of(SubMash) end it "should respect the class when converting the value" do record = Hashie::Mash.new record.details = Hashie::Mash.new({:email => "randy@asf.com"}) record.details.should be_kind_of(Hashie::Mash) end it "should respect another subclass when converting the value" do record = Hashie::Mash.new class SubMash < Hashie::Mash end son = SubMash.new({:email => "foo@bar.com"}) record.details = son record.details.should be_kind_of(SubMash) end describe "#respond_to?" do it 'should respond to a normal method' do Hashie::Mash.new.should be_respond_to(:key?) end it 'should respond to a set key' do Hashie::Mash.new(:abc => 'def').should be_respond_to(:abc) end it 'should respond to a set key with a suffix' do %w(= ? ! _).each do |suffix| Hashie::Mash.new(:abc => 'def').should be_respond_to(:"abc#{suffix}") end end it 'should respond to an unknown key with a suffix' do %w(= ? ! _).each do |suffix| Hashie::Mash.new(:abc => 'def').should be_respond_to(:"xyz#{suffix}") end end it "should not respond to an unknown key without a suffix" do Hashie::Mash.new(:abc => 'def').should_not be_respond_to(:xyz) end end context "#initialize" do it "should convert an existing hash to a Hashie::Mash" do converted = Hashie::Mash.new({:abc => 123, :name => "Bob"}) converted.abc.should == 123 converted.name.should == "Bob" end it "should convert hashes recursively into Hashie::Mashes" do converted = Hashie::Mash.new({:a => {:b => 1, :c => {:d => 23}}}) converted.a.is_a?(Hashie::Mash).should be_true converted.a.b.should == 1 converted.a.c.d.should == 23 end it "should convert hashes in arrays into Hashie::Mashes" do converted = Hashie::Mash.new({:a => [{:b => 12}, 23]}) converted.a.first.b.should == 12 converted.a.last.should == 23 end it "should convert an existing Hashie::Mash into a Hashie::Mash" do initial = Hashie::Mash.new(:name => 'randy', :address => {:state => 'TX'}) copy = Hashie::Mash.new(initial) initial.name.should == copy.name initial.__id__.should_not == copy.__id__ copy.address.state.should == 'TX' copy.address.state = 'MI' initial.address.state.should == 'TX' copy.address.__id__.should_not == initial.address.__id__ end it "should accept a default block" do initial = Hashie::Mash.new { |h,i| h[i] = []} initial.default_proc.should_not be_nil initial.default.should be_nil initial.test.should == [] initial.test?.should be_true end it "should convert Hashie::Mashes within Arrays back to Hashes" do initial_hash = {"a" => [{"b" => 12, "c" =>["d" => 50, "e" => 51]}, 23]} converted = Hashie::Mash.new(initial_hash) converted.to_hash["a"].first.is_a?(Hashie::Mash).should be_false converted.to_hash["a"].first.is_a?(Hash).should be_true converted.to_hash["a"].first["c"].first.is_a?(Hashie::Mash).should be_false converted.to_hash({:symbolize_keys => true}).keys[0].should == :a end end describe "#fetch" do let(:hash) { {:one => 1} } let(:mash) { Hashie::Mash.new(hash) } context "when key exists" do it "returns the value" do mash.fetch(:one).should eql(1) end context "when key has other than original but acceptable type" do it "returns the value" do mash.fetch('one').should eql(1) end end end context "when key does not exist" do it "should raise KeyError" do error = RUBY_VERSION =~ /1.8/ ? IndexError : KeyError expect { mash.fetch(:two) }.to raise_error(error) end context "with default value given" do it "returns default value" do mash.fetch(:two, 8).should eql(8) end end context "with block given" do it "returns default value" do mash.fetch(:two) {|key| "block default value" }.should eql("block default value") end end end end end hashie-2.0.3/spec/hashie/dash_spec.rb0000644000004100000410000001664012133445414017466 0ustar www-datawww-datarequire 'spec_helper' Hashie::Hash.class_eval do def self.inherited(klass) klass.instance_variable_set('@inheritance_test', true) end end class DashTest < Hashie::Dash property :first_name, :required => true property :email property :count, :default => 0 end class DashNoRequiredTest < Hashie::Dash property :first_name property :email property :count, :default => 0 end class Subclassed < DashTest property :last_name, :required => true end class DashDefaultTest < Hashie::Dash property :aliases, :default => ["Snake"] end class DeferredTest < Hashie::Dash property :created_at, :default => Proc.new { Time.now } end describe DashTest do subject { DashTest.new(:first_name => 'Bob', :email => 'bob@example.com') } it('subclasses Hashie::Hash') { should respond_to(:to_mash) } its(:to_s) { should == '#' } it 'lists all set properties in inspect' do subject.first_name = 'Bob' subject.email = 'bob@example.com' subject.inspect.should == '#' end its(:count) { should be_zero } it { should respond_to(:first_name) } it { should respond_to(:first_name=) } it { should_not respond_to(:nonexistent) } it 'errors out for a non-existent property' do lambda { subject['nonexistent'] }.should raise_error(NoMethodError) end it 'errors out when attempting to set a required property to nil' do lambda { subject.first_name = nil }.should raise_error(ArgumentError) end context 'writing to properties' do it 'fails writing a required property to nil' do lambda { subject.first_name = nil }.should raise_error(ArgumentError) end it 'fails writing a required property to nil using []=' do lambda { subject['first_name'] = nil }.should raise_error(ArgumentError) end it 'fails writing to a non-existent property using []=' do lambda { subject['nonexistent'] = 123 }.should raise_error(NoMethodError) end it 'works for an existing property using []=' do subject['first_name'] = 'Bob' subject['first_name'].should == 'Bob' subject[:first_name].should == 'Bob' end it 'works for an existing property using a method call' do subject.first_name = 'Franklin' subject.first_name.should == 'Franklin' end end context 'reading from properties' do it 'fails reading from a non-existent property using []' do lambda { subject['nonexistent'] }.should raise_error(NoMethodError) end it "should be able to retrieve properties through blocks" do subject["first_name"] = "Aiden" value = nil subject.[]("first_name") { |v| value = v } value.should == "Aiden" end it "should be able to retrieve properties through blocks with method calls" do subject["first_name"] = "Frodo" value = nil subject.first_name { |v| value = v } value.should == "Frodo" end end context 'reading from deferred properties' do it 'should evaluate proc after initial read' do DeferredTest.new['created_at'].should be_instance_of(Time) end it "should not evalute proc after subsequent reads" do deferred = DeferredTest.new deferred['created_at'].object_id.should == deferred['created_at'].object_id end end describe '.new' do it 'fails with non-existent properties' do lambda { described_class.new(:bork => '') }.should raise_error(NoMethodError) end it 'should set properties that it is able to' do obj = described_class.new :first_name => 'Michael' obj.first_name.should == 'Michael' end it 'accepts nil' do lambda { DashNoRequiredTest.new(nil) }.should_not raise_error end it 'accepts block to define a global default' do obj = described_class.new { |hash, key| key.to_s.upcase } obj.first_name.should == 'FIRST_NAME' obj.count.should be_zero end it "fails when required values are missing" do expect { DashTest.new }.to raise_error(ArgumentError) end it "does not overwrite default values" do obj1 = DashDefaultTest.new obj1.aliases << "El Rey" obj2 = DashDefaultTest.new obj2.aliases.should_not include "El Rey" end end describe 'properties' do it 'lists defined properties' do described_class.properties.should == Set.new([:first_name, :email, :count]) end it 'checks if a property exists' do described_class.property?('first_name').should be_true described_class.property?(:first_name).should be_true end it 'checks if a property is required' do described_class.required?('first_name').should be_true described_class.required?(:first_name).should be_true end it 'doesnt include property from subclass' do described_class.property?(:last_name).should be_false end it 'lists declared defaults' do described_class.defaults.should == { :count => 0 } end end describe '#replace' do before { subject.replace(:first_name => "Cain") } it 'return self' do subject.replace(:email => "bar").to_hash. should == {"email" => "bar", "count" => 0} end it 'sets all specified keys to their corresponding values' do subject.first_name.should == "Cain" end it 'leaves only specified keys and keys with default values' do subject.keys.sort.should == ['count', 'first_name'] subject.email.should be_nil subject.count.should == 0 end context 'when replacing keys with default values' do before { subject.replace(:count => 3) } it 'sets all specified keys to their corresponding values' do subject.count.should == 3 end end end end describe Hashie::Dash, 'inheritance' do before do @top = Class.new(Hashie::Dash) @middle = Class.new(@top) @bottom = Class.new(@middle) end it 'reports empty properties when nothing defined' do @top.properties.should be_empty @top.defaults.should be_empty end it 'inherits properties downwards' do @top.property :echo @middle.properties.should include(:echo) @bottom.properties.should include(:echo) end it 'doesnt inherit properties upwards' do @middle.property :echo @top.properties.should_not include(:echo) @bottom.properties.should include(:echo) end it 'allows overriding a default on an existing property' do @top.property :echo @middle.property :echo, :default => 123 @bottom.properties.to_a.should == [:echo] @bottom.new.echo.should == 123 end it 'allows clearing an existing default' do @top.property :echo @middle.property :echo, :default => 123 @bottom.property :echo @bottom.properties.to_a.should == [:echo] @bottom.new.echo.should be_nil end it 'should allow nil defaults' do @bottom.property :echo, :default => nil @bottom.new.should have_key('echo') end end describe Subclassed do subject { Subclassed.new(:first_name => 'Bob', :last_name => 'McNob', :email => 'bob@example.com') } its(:count) { should be_zero } it { should respond_to(:first_name) } it { should respond_to(:first_name=) } it { should respond_to(:last_name) } it { should respond_to(:last_name=) } it 'has one additional property' do described_class.property?(:last_name).should be_true end it "didn't override superclass inheritance logic" do described_class.instance_variable_get('@inheritance_test').should be_true end end hashie-2.0.3/spec/hashie/hash_spec.rb0000644000004100000410000000134012133445414017461 0ustar www-datawww-datarequire 'spec_helper' describe Hash do it "should be convertible to a Hashie::Mash" do mash = Hashie::Hash[:some => "hash"].to_mash mash.is_a?(Hashie::Mash).should be_true mash.some.should == "hash" end it "#stringify_keys! should turn all keys into strings" do hash = Hashie::Hash[:a => "hey", 123 => "bob"] hash.stringify_keys! hash.should == Hashie::Hash["a" => "hey", "123" => "bob"] end it "#stringify_keys should return a hash with stringified keys" do hash = Hashie::Hash[:a => "hey", 123 => "bob"] stringified_hash = hash.stringify_keys hash.should == Hashie::Hash[:a => "hey", 123 => "bob"] stringified_hash.should == Hashie::Hash["a" => "hey", "123" => "bob"] end endhashie-2.0.3/spec/hashie/extensions/0000755000004100000410000000000012133445414017400 5ustar www-datawww-datahashie-2.0.3/spec/hashie/extensions/method_access_spec.rb0000644000004100000410000000622212133445414023542 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Extensions::MethodReader do class ReaderHash < Hash def initialize(hash = {}); self.update(hash) end include Hashie::Extensions::MethodReader end subject{ ReaderHash } it 'should read string keys from the method' do subject.new('awesome' => 'sauce').awesome.should == 'sauce' end it 'should read symbol keys from the method' do subject.new(:awesome => 'sauce').awesome.should == 'sauce' end it 'should read nil and false values out properly' do h = subject.new(:nil => nil, :false => false) h.nil.should == nil h.false.should == false end it 'should raise a NoMethodError for undefined keys' do lambda{ subject.new.awesome }.should raise_error(NoMethodError) end describe '#respond_to?' do it 'should be true for string keys' do subject.new('awesome' => 'sauce').should be_respond_to(:awesome) end it 'should be true for symbol keys' do subject.new(:awesome => 'sauce').should be_respond_to(:awesome) end it 'should be false for non-keys' do subject.new.should_not be_respond_to(:awesome) end end end describe Hashie::Extensions::MethodWriter do class WriterHash < Hash include Hashie::Extensions::MethodWriter end subject{ WriterHash.new } it 'should write from a method call' do subject.awesome = 'sauce' subject['awesome'].should == 'sauce' end it 'should convert the key using the #convert_key method' do subject.stub!(:convert_key).and_return(:awesome) subject.awesome = 'sauce' subject[:awesome].should == 'sauce' end it 'should still NoMethodError on non equals-ending methods' do lambda{ subject.awesome }.should raise_error(NoMethodError) end it 'should #respond_to? properly' do subject.should be_respond_to(:abc=) subject.should_not be_respond_to(:abc) end end describe Hashie::Extensions::MethodQuery do class QueryHash < Hash def initialize(hash = {}); self.update(hash) end include Hashie::Extensions::MethodQuery end subject{ QueryHash } it 'should be true for non-nil string key values' do subject.new('abc' => 123).should be_abc end it 'should be true for non-nil symbol key values' do subject.new(:abc => 123).should be_abc end it 'should be false for nil key values' do subject.new(:abc => false).should_not be_abc end it 'should raise a NoMethodError for non-set keys' do lambda{ subject.new.abc? }.should raise_error(NoMethodError) end it 'should respond_to? for existing string keys' do subject.new('abc' => 'def').should be_respond_to('abc?') end it 'should respond_to? for existing symbol keys' do subject.new(:abc => 'def').should be_respond_to(:abc?) end it 'should not respond_to? for non-existent keys' do subject.new.should_not be_respond_to('abc?') end end describe Hashie::Extensions::MethodAccess do it 'should include all of the other method mixins' do klass = Class.new(Hash) klass.send :include, Hashie::Extensions::MethodAccess (klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodWriter, Hashie::Extensions::MethodQuery]).size.should == 3 end end hashie-2.0.3/spec/hashie/extensions/deep_merge_spec.rb0000644000004100000410000000125412133445414023035 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Extensions::DeepMerge do class DeepMergeHash < Hash; include Hashie::Extensions::DeepMerge end subject{ DeepMergeHash } let(:h1) { subject.new.merge(:a => "a", :b => "b", :c => { :c1 => "c1", :c2 => "c2", :c3 => { :d1 => "d1" } }) } let(:h2) { { :a => 1, :c => { :c1 => 2, :c3 => { :d2 => "d2" } } } } let(:expected_hash) { { :a => 1, :b => "b", :c => { :c1 => 2, :c2 => "c2", :c3 => { :d1 => "d1", :d2 => "d2" } } } } it 'should deep merge two hashes' do h1.deep_merge(h2).should == expected_hash end it 'should deep merge two hashes with bang method' do h1.deep_merge!(h2) h1.should == expected_hash end end hashie-2.0.3/spec/hashie/extensions/key_conversion_spec.rb0000644000004100000410000000547112133445414024003 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Extensions::KeyConversion do subject do klass = Class.new(::Hash) klass.send :include, Hashie::Extensions::KeyConversion klass end let(:instance){ subject.new } describe '#stringify_keys!' do it 'should convert keys to strings' do instance[:abc] = 'abc' instance[123] = '123' instance.stringify_keys! (instance.keys & %w(abc 123)).size.should == 2 end it 'should do deep conversion within nested hashes' do instance[:ab] = subject.new instance[:ab][:cd] = subject.new instance[:ab][:cd][:ef] = 'abcdef' instance.stringify_keys! instance.should == {'ab' => {'cd' => {'ef' => 'abcdef'}}} end it 'should do deep conversion within nested arrays' do instance[:ab] = [] instance[:ab] << subject.new instance[:ab] << subject.new instance[:ab][0][:cd] = 'abcd' instance[:ab][1][:ef] = 'abef' instance.stringify_keys! instance.should == {'ab' => [{'cd' => 'abcd'}, {'ef' => 'abef'}]} end it 'should return itself' do instance.stringify_keys!.should == instance end end describe '#stringify_keys' do it 'should convert keys to strings' do instance[:abc] = 'def' copy = instance.stringify_keys copy['abc'].should == 'def' end it 'should not alter the original' do instance[:abc] = 'def' copy = instance.stringify_keys instance.keys.should == [:abc] copy.keys.should == %w(abc) end end describe '#symbolize_keys!' do it 'should convert keys to symbols' do instance['abc'] = 'abc' instance['def'] = 'def' instance.symbolize_keys! (instance.keys & [:abc, :def]).size.should == 2 end it 'should do deep conversion within nested hashes' do instance['ab'] = subject.new instance['ab']['cd'] = subject.new instance['ab']['cd']['ef'] = 'abcdef' instance.symbolize_keys! instance.should == {:ab => {:cd => {:ef => 'abcdef'}}} end it 'should do deep conversion within nested arrays' do instance['ab'] = [] instance['ab'] << subject.new instance['ab'] << subject.new instance['ab'][0]['cd'] = 'abcd' instance['ab'][1]['ef'] = 'abef' instance.symbolize_keys! instance.should == {:ab => [{:cd => 'abcd'}, {:ef => 'abef'}]} end it 'should return itself' do instance.symbolize_keys!.should == instance end end describe '#symbolize_keys' do it 'should convert keys to symbols' do instance['abc'] = 'def' copy = instance.symbolize_keys copy[:abc].should == 'def' end it 'should not alter the original' do instance['abc'] = 'def' copy = instance.symbolize_keys instance.keys.should == ['abc'] copy.keys.should == [:abc] end end end hashie-2.0.3/spec/hashie/extensions/merge_initializer_spec.rb0000644000004100000410000000104412133445414024440 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Extensions::MergeInitializer do class MergeInitializerHash < Hash; include Hashie::Extensions::MergeInitializer end subject{ MergeInitializerHash } it 'should initialize fine with no arguments' do subject.new.should == {} end it 'should initialize with a hash' do subject.new(:abc => 'def').should == {:abc => 'def'} end it 'should initialize with a hash and a default' do h = subject.new({:abc => 'def'}, 'bar') h[:foo].should == 'bar' h[:abc].should == 'def' end end hashie-2.0.3/spec/hashie/extensions/indifferent_access_spec.rb0000644000004100000410000000516012133445414024557 0ustar www-datawww-datarequire '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 let(:h) { subject.new(:foo => 'bar') } it 'should find it indifferently' do h.should be_key(:foo) h.should be_key('foo') end %w(include? member? has_key?).each do |key_alias| it "should be aliased as #{key_alias}" do h.send(key_alias.to_sym, :foo).should be(true) h.send(key_alias.to_sym, 'foo').should be(true) end 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 describe '#replace' do subject do IndifferentHash.new(:foo => 'bar').replace(:bar => 'baz', :hi => 'bye') end it 'returns self' do subject.should be_a(IndifferentHash) end it 'should remove old keys' do [:foo, 'foo'].each do |k| subject[k].should be_nil subject.key?(k).should be_false end end it 'creates new keys with indifferent access' do [:bar, 'bar', :hi, 'hi'].each { |k| subject.key?(k).should be_true } subject[:bar].should == 'baz' subject['bar'].should == 'baz' subject[:hi].should == 'bye' subject['hi'].should == 'bye' end end end hashie-2.0.3/spec/hashie/extensions/coercion_spec.rb0000644000004100000410000000610612133445414022543 0ustar www-datawww-datarequire 'spec_helper' describe Hashie::Extensions::Coercion do class Initializable def initialize(obj, coerced = false) @coerced = coerced @value = obj.class.to_s end def coerced?; @coerced end attr_reader :value end class Coercable < Initializable def self.coerce(obj) new(obj, true) end end before(:each) do class ExampleCoercableHash < Hash include Hashie::Extensions::Coercion include Hashie::Extensions::MergeInitializer end end subject { ExampleCoercableHash } let(:instance){ subject.new } describe '.coerce_key' do it { subject.should be_respond_to(:coerce_key) } it 'should run through coerce on a specified key' do subject.coerce_key :foo, Coercable instance[:foo] = "bar" instance[:foo].should be_coerced end it "should support an array of keys" do subject.coerce_keys :foo, :bar, Coercable instance[:foo] = "bar" instance[:bar] = "bax" instance[:foo].should be_coerced instance[:bar].should be_coerced end it 'should just call #new if no coerce method is available' do subject.coerce_key :foo, Initializable instance[:foo] = "bar" instance[:foo].value.should == "String" instance[:foo].should_not be_coerced end it "should coerce when the merge initializer is used" do subject.coerce_key :foo, Coercable instance = subject.new(:foo => "bar") instance[:foo].should be_coerced end context 'when #replace is used' do before { subject.coerce_key :foo, :bar, Coercable } let(:instance) do subject.new(:foo => "bar"). replace(:foo => "foz", :bar => "baz", :hi => "bye") end it "should coerce relevant keys" do instance[:foo].should be_coerced instance[:bar].should be_coerced instance[:hi].should_not respond_to(:coerced?) end it "should set correct values" do instance[:hi].should == "bye" end end end describe '.coerce_value' do context 'with :strict => true' do it 'should coerce any value of the exact right class' do subject.coerce_value String, Coercable instance[:foo] = "bar" instance[:bar] = "bax" instance[:hi] = :bye instance[:foo].should be_coerced instance[:bar].should be_coerced instance[:hi].should_not respond_to(:coerced?) end it 'should coerce values from a #replace call' do subject.coerce_value String, Coercable instance[:foo] = :bar instance.replace(:foo => "bar", :bar => "bax") instance[:foo].should be_coerced instance[:bar].should be_coerced end it 'should not coerce superclasses' do klass = Class.new(String) subject.coerce_value klass, Coercable instance[:foo] = "bar" instance[:foo].should_not be_kind_of(Coercable) instance[:foo] = klass.new instance[:foo].should be_kind_of(Coercable) end end end after(:each) do Object.send(:remove_const, :ExampleCoercableHash) end end hashie-2.0.3/spec/spec_helper.rb0000644000004100000410000000033612133445414016560 0ustar www-datawww-datarequire 'rubygems' $LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'hashie' require 'rspec' require 'rspec/autorun' RSpec.configure do |config| end hashie-2.0.3/.rspec0000644000004100000410000000003112133445414014115 0ustar www-datawww-data--colour --format=nested hashie-2.0.3/README.markdown0000644000004100000410000001721512133445414015515 0ustar www-datawww-data**Note:** This documentation is for the unreleased version 2.0 of Hashie. See the [1-1-stable branch](https://github.com/intridea/hashie/tree/1-1-stable) for documentation of the released version. # Hashie [![Build Status](https://secure.travis-ci.org/intridea/hashie.png)](http://travis-ci.org/intridea/hashie) [![Dependency Status](https://gemnasium.com/intridea/hashie.png)](https://gemnasium.com/intridea/hashie) Hashie is a growing collection of tools that extend Hashes and make them more useful. ## Installation Hashie is available as a RubyGem: gem install hashie ## Hash Extensions New to version 2.0 of Hashie, the library has been broken up into a number of atomically includeable Hash extension modules as described below. This provides maximum flexibility for users to mix and match functionality while maintaining feature parity with earlier versions of Hashie. Any of the extensions listed below can be mixed into a class by `include`-ing `Hashie::Extensions::ExtensionName`. ### Coercion Coercions allow you to set up "coercion rules" based either on the key or the value type to massage data as it's being inserted into the Hash. Key coercions might be used, for example, in lightweight data modeling applications such as an API client: class Tweet < Hash include Hashie::Extensions::Coercion coerce_key :user, User end user_hash = {:name => "Bob"} Tweet.new(:user => user_hash) # => automatically calls User.coerce(user_hash) or # User.new(user_hash) if that isn't present. Value coercions, on the other hand, will coerce values based on the type of the value being inserted. This is useful if you are trying to build a Hash-like class that is self-propagating. class SpecialHash < Hash include Hashie::Extensions::Coercion coerce_value Hash, SpecialHash def initialize(hash = {}) super hash.each_pair do |k,v| self[k] = v end end end ### KeyConversion The KeyConversion extension gives you the convenience methods of `symbolize_keys` and `stringify_keys` along with their bang counterparts. You can also include just stringify or just symbolize with `Hashie::Extensions::StringifyKeys` or `Hashie::Extensions::SymbolizeKeys`. ### 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 reading, writing, and querying into your Hash descendant. It can also be included as individual modules, i.e. `Hashie::Extensions::MethodReader`, `Hashie::Extensions::MethodWriter` and `Hashie::Extensions::MethodQuery` class MyHash < Hash include Hashie::Extensions::MethodAccess end h = MyHash.new h.abc = 'def' 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 This extension allow you to easily include a recursive merging system to any Hash descendant: class MyHash < Hash include Hashie::Extensions::DeepMerge end h1 = MyHash.new h2 = MyHash.new 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] } ## Mash Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended. It is designed to be used in RESTful API libraries to provide easy object-like access to JSON and XML parsed hashes. ### Example: mash = Hashie::Mash.new mash.name? # => false mash.name # => nil mash.name = "My Mash" mash.name # => "My Mash" mash.name? # => true mash.inspect # => mash = Mash.new # use bang methods for multi-level assignment mash.author!.name = "Michael Bleigh" mash.author # => mash = Mash.new # use under-bang methods for multi-level testing mash.author_.name? # => false mash.inspect # => **Note:** The `?` method will return false if a key has been set to false or nil. In order to check if a key has been set at all, use the `mash.key?('some_key')` method instead. ## Dash Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset. ### Example: class Person < Hashie::Dash property :name, :required => true property :email property :occupation, :default => 'Rubyist' end p = Person.new # => ArgumentError: The property 'name' is required for this Dash. p = Person.new(:name => "Bob") p.name # => 'Bob' p.name = nil # => ArgumentError: The property 'name' is required for this Dash. p.email = 'abc@def.com' p.occupation # => 'Rubyist' p.email # => 'abc@def.com' p[:awesome] # => NoMethodError p[:occupation] # => 'Rubyist' ## Trash A Trash is a Dash that allows you to translate keys on initialization. It is used like so: class Person < Hashie::Trash property :first_name, :from => :firstName end This will automatically translate the firstName key to first_name when it is initialized using a hash such as through: Person.new(:firstName => 'Bob') Trash also supports translations using lambda, this could be useful when dealing with external API's. You can use it in this way: class Result < Hashie::Trash property :id, :transform_with => lambda { |v| v.to_i } property :created_at, :from => :creation_date, :with => lambda { |v| Time.parse(v) } end this will produce the following result = Result.new(:id => '123', :creation_date => '2012-03-30 17:23:28') result.id.class # => Fixnum result.created_at.class # => Time ## Clash Clash is a Chainable Lazy Hash that allows you to easily construct complex hashes using method notation chaining. This will allow you to use a more action-oriented approach to building options hashes. Essentially, a Clash is a generalized way to provide much of the same kind of "chainability" that libraries like Arel or Rails 2.x's named_scopes provide. ### Example c = Hashie::Clash.new c.where(:abc => 'def').order(:created_at) c # => {:where => {:abc => 'def'}, :order => :created_at} # You can also use bang notation to chain into sub-hashes, # jumping back up the chain with _end! c = Hashie::Clash.new c.where!.abc('def').ghi(123)._end!.order(:created_at) c # => {:where => {:abc => 'def', :ghi => 123}, :order => :created_at} # Multiple hashes are merged automatically c = Hashie::Clash.new c.where(:abc => 'def').where(:hgi => 123) c # => {:where => {:abc => 'def', :hgi => 123}} ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) ## Authors * Michael Bleigh ## Copyright Copyright (c) 2009-2011 Intridea, Inc. (http://intridea.com/). See LICENSE for details. hashie-2.0.3/.document0000644000004100000410000000007412133445414014626 0ustar www-datawww-dataREADME.rdoc lib/**/*.rb bin/* features/**/*.feature LICENSE hashie-2.0.3/metadata.yml0000644000004100000410000001120712133445414015312 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: hashie version: !ruby/object:Gem::Version version: 2.0.3 prerelease: platform: ruby authors: - Michael Bleigh - Jerry Cheung autorequire: bindir: bin cert_chain: [] date: 2013-03-18 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: 0.9.2 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: 0.9.2 - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '2.5' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version version: '2.5' - !ruby/object:Gem::Dependency name: guard requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: guard-rspec requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: growl requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' description: Hashie is a small collection of tools that make hashes more powerful. Currently includes Mash (Mocking Hash) and Dash (Discrete Hash). email: - michael@intridea.com - jollyjerry@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - .document - .gitignore - .rspec - .travis.yml - .yardopts - CHANGELOG.md - CONTRIBUTING.md - Gemfile - Guardfile - LICENSE - README.markdown - Rakefile - hashie.gemspec - lib/hashie.rb - lib/hashie/clash.rb - lib/hashie/dash.rb - lib/hashie/extensions/coercion.rb - lib/hashie/extensions/deep_merge.rb - lib/hashie/extensions/indifferent_access.rb - lib/hashie/extensions/key_conversion.rb - lib/hashie/extensions/merge_initializer.rb - lib/hashie/extensions/method_access.rb - lib/hashie/extensions/structure.rb - lib/hashie/hash.rb - lib/hashie/hash_extensions.rb - lib/hashie/mash.rb - lib/hashie/trash.rb - lib/hashie/version.rb - spec/hashie/clash_spec.rb - spec/hashie/dash_spec.rb - spec/hashie/extensions/coercion_spec.rb - spec/hashie/extensions/deep_merge_spec.rb - spec/hashie/extensions/indifferent_access_spec.rb - spec/hashie/extensions/key_conversion_spec.rb - spec/hashie/extensions/merge_initializer_spec.rb - spec/hashie/extensions/method_access_spec.rb - spec/hashie/hash_spec.rb - spec/hashie/mash_spec.rb - spec/hashie/trash_spec.rb - spec/spec.opts - spec/spec_helper.rb homepage: https://github.com/intridea/hashie licenses: - MIT post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' segments: - 0 hash: -486186307453190267 required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' segments: - 0 hash: -486186307453190267 requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: Your friendly neighborhood hash toolkit. test_files: - spec/hashie/clash_spec.rb - spec/hashie/dash_spec.rb - spec/hashie/extensions/coercion_spec.rb - spec/hashie/extensions/deep_merge_spec.rb - spec/hashie/extensions/indifferent_access_spec.rb - spec/hashie/extensions/key_conversion_spec.rb - spec/hashie/extensions/merge_initializer_spec.rb - spec/hashie/extensions/method_access_spec.rb - spec/hashie/hash_spec.rb - spec/hashie/mash_spec.rb - spec/hashie/trash_spec.rb - spec/spec.opts - spec/spec_helper.rb hashie-2.0.3/.yardopts0000644000004100000410000000001412133445414014647 0ustar www-datawww-data-m markdown hashie-2.0.3/CHANGELOG.md0000644000004100000410000000174312133445414014624 0ustar www-datawww-data# CHANGELOG ## 2.0.3 * Hashie::Mash.new(abc: true).respond_to?(:abc?) works 7even #88 * Fix #replace jimeh #68 ## 2.0.2 * adding symbolize_keys back to to_hash cromulus #85 ## 2.0.1 * remove Mash#object_id override matschaffer #81 * gem cleanup: remove VERSION, Gemfile.lock jch, mbleigh ## 2.0.0 * update gemspec with license info jordimassaguerpla #72 * fix readme typo jcamenisch #71 * initialize with merge coerces values mattfawcett #27 * Hashie::Extensions::Coercion coerce_keys takes arguments mattfawcett #28 * Trash removes translated values on initialization sleverbor #39 * Mash#fetch works with symbol or string keys arthwood #66 * Hashie::Hash inherits from ::Hash to avoid ambiguity meh, orend #49 * update respond_to? method signature to match ruby core definition dlupu #62 * DeepMerge extension nashby #41 * Dash defaults are dup'ed before assigned ohrite #63 * remove id, type, and object_id as special allowable keys jch #77 * merge and update accepts a block jch #78 hashie-2.0.3/Gemfile0000644000004100000410000000004512133445414014300 0ustar www-datawww-datasource 'http://rubygems.org' gemspec hashie-2.0.3/Guardfile0000644000004100000410000000023412133445414014632 0ustar www-datawww-dataguard 'rspec' do watch(%r{^spec/.+_spec\.rb}) watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" } watch('spec/spec_helper.rb') { "spec" } end hashie-2.0.3/.gitignore0000644000004100000410000000010312133445414014770 0ustar www-datawww-data*.sw? .DS_Store coverage rdoc pkg *.gem .bundle .rvmrc Gemfile.lockhashie-2.0.3/lib/0000755000004100000410000000000012133445414013554 5ustar www-datawww-datahashie-2.0.3/lib/hashie/0000755000004100000410000000000012133445414015015 5ustar www-datawww-datahashie-2.0.3/lib/hashie/version.rb0000644000004100000410000000004612133445414017027 0ustar www-datawww-datamodule Hashie VERSION = '2.0.3' end hashie-2.0.3/lib/hashie/clash.rb0000644000004100000410000000514212133445414016436 0ustar www-datawww-datarequire 'hashie/hash' module Hashie # # A Clash is a "Chainable Lazy Hash". Inspired by libraries such as Arel, # a Clash allows you to chain together method arguments to build a # hash, something that's especially useful if you're doing something # like constructing a complex options hash. Here's a basic example: # # c = Hashie::Clash.new.conditions(:foo => 'bar').order(:created_at) # c # => {:conditions => {:foo => 'bar'}, :order => :created_at} # # Clash provides another way to create sub-hashes by using bang notation. # You can dive into a sub-hash by providing a key with a bang and dive # back out again with the _end! method. Example: # # c = Hashie::Clash.new.conditions!.foo('bar').baz(123)._end!.order(:created_at) # c # => {:conditions => {:foo => 'bar', :baz => 123}, :order => :created_at} # # Because the primary functionality of Clash is to build options objects, # all keys are converted to symbols since many libraries expect symbols explicitly # for keys. # class Clash < ::Hash class ChainError < ::StandardError; end # The parent Clash if this Clash was created via chaining. attr_reader :_parent # Initialize a new clash by passing in a Hash to # convert and, optionally, the parent to which this # Clash is chained. def initialize(other_hash = {}, parent = nil) @_parent = parent other_hash.each_pair do |k, v| self[k.to_sym] = v end end # Jump back up a level if you are using bang method # chaining. For example: # # c = Hashie::Clash.new.foo('bar') # c.baz!.foo(123) # => c[:baz] # c.baz!._end! # => c def _end! self._parent end def id(*args) #:nodoc: method_missing(:id, *args) end def merge_store(key, *args) #:nodoc: case args.length when 1 val = args.first val = self[key].merge(val) if self[key].is_a?(::Hash) && val.is_a?(::Hash) else val = args end self[key.to_sym] = val self end def method_missing(name, *args) #:nodoc: name = name.to_s if name.match(/!$/) && args.empty? key = name[0...-1].to_sym if self[key].nil? self[key] = Clash.new({}, self) elsif self[key].is_a?(::Hash) && !self[key].is_a?(Clash) self[key] = Clash.new(self[key], self) else raise ChainError, "Tried to chain into a non-hash key." end self[key] elsif args.any? key = name.to_sym self.merge_store(key, *args) end end end endhashie-2.0.3/lib/hashie/dash.rb0000644000004100000410000001222212133445414016260 0ustar www-datawww-datarequire 'hashie/hash' require 'set' module Hashie # A Dash is a 'defined' or 'discrete' Hash, that is, a Hash # that has a set of defined keys that are accessible (with # optional defaults) and only those keys may be set or read. # # Dashes are useful when you need to create a very simple # lightweight data object that needs even fewer options and # resources than something like a DataMapper resource. # # It is preferrable to a Struct because of the in-class # API for defining properties as well as per-property defaults. class Dash < Hash include PrettyInspect alias_method :to_s, :inspect # Defines a property on the Dash. Options are # as follows: # # * :default - Specify a default value for this property, # to be returned before a value is set on the property in a new # Dash. # # * :required - Specify the value as required for this # property, to raise an error if a value is unset in a new or # existing Dash. # def self.property(property_name, options = {}) property_name = property_name.to_sym self.properties << property_name if options.has_key?(:default) self.defaults[property_name] = options[:default] elsif self.defaults.has_key?(property_name) self.defaults.delete property_name end unless instance_methods.map { |m| m.to_s }.include?("#{property_name}=") class_eval <<-ACCESSORS def #{property_name}(&block) self.[](#{property_name.to_s.inspect}, &block) end def #{property_name}=(value) self.[]=(#{property_name.to_s.inspect}, value) end ACCESSORS end if defined? @subclasses @subclasses.each { |klass| klass.property(property_name, options) } end required_properties << property_name if options.delete(:required) end class << self attr_reader :properties, :defaults attr_reader :required_properties end instance_variable_set('@properties', Set.new) instance_variable_set('@defaults', {}) instance_variable_set('@required_properties', Set.new) def self.inherited(klass) super (@subclasses ||= Set.new) << klass klass.instance_variable_set('@properties', self.properties.dup) klass.instance_variable_set('@defaults', self.defaults.dup) klass.instance_variable_set('@required_properties', self.required_properties.dup) end # Check to see if the specified property has already been # defined. def self.property?(name) properties.include? name.to_sym end # Check to see if the specified property is # required. def self.required?(name) required_properties.include? name.to_sym end # You may initialize a Dash with an attributes hash # just like you would many other kinds of data objects. def initialize(attributes = {}, &block) super(&block) self.class.defaults.each_pair do |prop, value| self[prop] = begin value.dup rescue TypeError value end end initialize_attributes(attributes) assert_required_properties_set! end alias_method :_regular_reader, :[] alias_method :_regular_writer, :[]= private :_regular_reader, :_regular_writer # Retrieve a value from the Dash (will return the # property's default value if it hasn't been set). def [](property) assert_property_exists! property value = super(property.to_s) # If the value is a lambda, proc, or whatever answers to call, eval the thing! if value.is_a? Proc self[property] = value.call # Set the result of the call as a value else yield value if block_given? value end end # Set a value on the Dash in a Hash-like way. Only works # on pre-existing properties. def []=(property, value) assert_property_required! property, value assert_property_exists! property super(property.to_s, value) end def replace(other_hash) other_hash = self.class.defaults.merge(other_hash) (keys - other_hash.keys).each { |key| delete(key) } other_hash.each { |key, value| self[key] = value } self end private def initialize_attributes(attributes) attributes.each_pair do |att, value| self[att] = value end if attributes end def assert_property_exists!(property) unless self.class.property?(property) raise NoMethodError, "The property '#{property}' is not defined for this Dash." end end def assert_required_properties_set! self.class.required_properties.each do |required_property| assert_property_set!(required_property) end end def assert_property_set!(property) if send(property).nil? raise ArgumentError, "The property '#{property}' is required for this Dash." end end def assert_property_required!(property, value) if self.class.required?(property) && value.nil? raise ArgumentError, "The property '#{property}' is required for this Dash." end end end end hashie-2.0.3/lib/hashie/hash.rb0000644000004100000410000000165012133445414016267 0ustar www-datawww-datarequire 'hashie/hash_extensions' module Hashie # A Hashie Hash is simply a Hash that has convenience # functions baked in such as stringify_keys that may # not be available in all libraries. class Hash < ::Hash include HashExtensions # Converts a mash back to a hash (with stringified keys) def to_hash(options={}) out = {} keys.each do |k| if self[k].is_a?(Array) k = options[:symbolize_keys] ? k.to_sym : k.to_s out[k] ||= [] self[k].each do |array_object| out[k] << (Hash === array_object ? array_object.to_hash : array_object) end else k = options[:symbolize_keys] ? k.to_sym : k.to_s out[k] = Hash === self[k] ? self[k].to_hash : self[k] end end out end # The C geneartor for the json gem doesn't like mashies def to_json(*args) to_hash.to_json(*args) end end end hashie-2.0.3/lib/hashie/hash_extensions.rb0000644000004100000410000000236112133445414020546 0ustar www-datawww-datamodule Hashie module HashExtensions def self.included(base) # Don't tread on existing extensions of Hash by # adding methods that are likely to exist. %w(stringify_keys stringify_keys!).each do |hashie_method| base.send :alias_method, hashie_method, "hashie_#{hashie_method}" unless base.instance_methods.include?(hashie_method) end end # Destructively convert all of the keys of a Hash # to their string representations. def hashie_stringify_keys! self.keys.each do |k| unless String === k self[k.to_s] = self.delete(k) end end self end # Convert all of the keys of a Hash # to their string representations. def hashie_stringify_keys self.dup.stringify_keys! end # Convert this hash into a Mash def to_mash ::Hashie::Mash.new(self) end end module PrettyInspect def self.included(base) base.send :alias_method, :hash_inspect, :inspect base.send :alias_method, :inspect, :hashie_inspect end def hashie_inspect ret = "#<#{self.class.to_s}" stringify_keys.keys.sort.each do |key| ret << " #{key}=#{self[key].inspect}" end ret << ">" ret end end end hashie-2.0.3/lib/hashie/mash.rb0000644000004100000410000001474612133445414016306 0ustar www-datawww-datarequire 'hashie/hash' module Hashie # Mash allows you to create pseudo-objects that have method-like # accessors for hash keys. This is useful for such implementations # as an API-accessing library that wants to fake robust objects # without the overhead of actually doing so. Think of it as OpenStruct # with some additional goodies. # # A Mash will look at the methods you pass it and perform operations # based on the following rules: # # * No punctuation: Returns the value of the hash for that key, or nil if none exists. # * Assignment (=): Sets the attribute of the given method name. # * Existence (?): Returns true or false depending on whether that key has been set. # * Bang (!): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes. # * Under Bang (_): Like Bang, but returns a new Mash rather than creating a key. Used to test existance in deep Mashes. # # == Basic Example # # mash = Mash.new # mash.name? # => false # mash.name = "Bob" # mash.name # => "Bob" # mash.name? # => true # # == Hash Conversion Example # # hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]} # mash = Mash.new(hash) # mash.a.b # => 23 # mash.a.d.e # => "abc" # mash.f.first.g # => 44 # mash.f.last # => 12 # # == Bang Example # # mash = Mash.new # mash.author # => nil # mash.author! # => # # mash = Mash.new # mash.author!.name = "Michael Bleigh" # mash.author # => # # == Under Bang Example # # mash = Mash.new # mash.author # => nil # mash.author_ # => # mash.author_.name # => nil # # mash = Mash.new # mash.author_.name = "Michael Bleigh" (assigned to temp object) # mash.author # => # class Mash < Hash include Hashie::PrettyInspect alias_method :to_s, :inspect # If you pass in an existing hash, it will # convert it to a Mash including recursively # descending into arrays and hashes, converting # them as well. def initialize(source_hash = nil, default = nil, &blk) deep_update(source_hash) if source_hash default ? super(default) : super(&blk) end class << self; alias [] new; end def id #:nodoc: self["id"] end def type #:nodoc: self["type"] end alias_method :regular_reader, :[] alias_method :regular_writer, :[]= # Retrieves an attribute set in the Mash. Will convert # any key passed in to a string before retrieving. def [](key) value = regular_reader(convert_key(key)) yield value if block_given? value end # Sets an attribute in the Mash. Key will be converted to # a string before it is set, and Hashes will be converted # into Mashes for nesting purposes. def []=(key,value) #:nodoc: regular_writer(convert_key(key), convert_value(value)) end # This is the bang method reader, it will return a new Mash # if there isn't a value already assigned to the key requested. def initializing_reader(key) ck = convert_key(key) regular_writer(ck, self.class.new) unless key?(ck) regular_reader(ck) end # This is the under bang method reader, it will return a temporary new Mash # if there isn't a value already assigned to the key requested. def underbang_reader(key) ck = convert_key(key) if key?(ck) regular_reader(ck) else self.class.new end end def fetch(key, default_value = nil) self[key] || block_given? && yield(key) || default_value || super(key) end def delete(key) super(convert_key(key)) end alias_method :regular_dup, :dup # Duplicates the current mash as a new mash. def dup self.class.new(self, self.default) end def key?(key) super(convert_key(key)) end alias_method :has_key?, :key? alias_method :include?, :key? alias_method :member?, :key? # Performs a deep_update on a duplicate of the # current mash. def deep_merge(other_hash, &blk) dup.deep_update(other_hash, &blk) end alias_method :merge, :deep_merge # Recursively merges this mash with the passed # in hash, merging each hash in the hierarchy. def deep_update(other_hash, &blk) other_hash.each_pair do |k,v| key = convert_key(k) if regular_reader(key).is_a?(Mash) and v.is_a?(::Hash) regular_reader(key).deep_update(v, &blk) else value = convert_value(v, true) value = blk.call(key, self[k], value) if blk regular_writer(key, value) end end self end alias_method :deep_merge!, :deep_update alias_method :update, :deep_update alias_method :merge!, :update # Performs a shallow_update on a duplicate of the current mash def shallow_merge(other_hash) dup.shallow_update(other_hash) end # Merges (non-recursively) the hash from the argument, # changing the receiving hash def shallow_update(other_hash) other_hash.each_pair do |k,v| regular_writer(convert_key(k), convert_value(v, true)) end self end def replace(other_hash) (keys - other_hash.keys).each { |key| delete(key) } other_hash.each { |key, value| self[key] = value } self end # Will return true if the Mash has had a key # set in addition to normal respond_to? functionality. def respond_to?(method_name, include_private=false) return true if key?(method_name) || method_name.to_s.slice(/[=?!_]\Z/) super end def method_missing(method_name, *args, &blk) return self.[](method_name, &blk) if key?(method_name) match = method_name.to_s.match(/(.*?)([?=!_]?)$/) case match[2] when "=" self[match[1]] = args.first when "?" !!self[match[1]] when "!" initializing_reader(match[1]) when "_" underbang_reader(match[1]) else default(method_name, *args, &blk) end end protected def convert_key(key) #:nodoc: key.to_s end def convert_value(val, duping=false) #:nodoc: case val when self.class val.dup when Hash duping ? val.dup : val when ::Hash val = val.dup if duping self.class.new(val) when Array val.collect{ |e| convert_value(e) } else val end end end end hashie-2.0.3/lib/hashie/trash.rb0000644000004100000410000000544312133445414016471 0ustar www-datawww-datarequire 'hashie/dash' module Hashie # A Trash is a 'translated' Dash where the keys can be remapped from a source # hash. # # Trashes are useful when you need to read data from another application, # such as a Java api, where the keys are named differently from how we would # in Ruby. class Trash < Dash # Defines a property on the Trash. Options are as follows: # # * :default - Specify a default value for this property, to be # returned before a value is set on the property in a new Dash. # * :from - Specify the original key name that will be write only. # * :with - Specify a lambda to be used to convert value. # * :transform_with - Specify a lambda to be used to convert value # without using the :from option. It transform the property itself. def self.property(property_name, options = {}) super if options[:from] if property_name.to_sym == options[:from].to_sym raise ArgumentError, "Property name (#{property_name}) and :from option must not be the same" end translations << options[:from].to_sym if options[:with].respond_to? :call class_eval do define_method "#{options[:from]}=" do |val| self[property_name.to_sym] = options[:with].call(val) end end else class_eval <<-RUBY def #{options[:from]}=(val) self[:#{property_name}] = val end RUBY end elsif options[:transform_with].respond_to? :call transforms[property_name.to_sym] = options[:transform_with] end end # Set a value on the Dash in a Hash-like way. Only works # on pre-existing properties. def []=(property, value) if self.class.translations.include? property.to_sym send("#{property}=", value) elsif self.class.transforms.key? property.to_sym super property, self.class.transforms[property.to_sym].call(value) elsif property_exists? property super end end private def self.translations @translations ||= [] end def self.transforms @transforms ||= {} end # Raises an NoMethodError if the property doesn't exist # def property_exists?(property) unless self.class.property?(property.to_sym) raise NoMethodError, "The property '#{property}' is not defined for this Trash." end true end private # Deletes any keys that have a translation def initialize_attributes(attributes) return unless attributes attributes_copy = attributes.dup.delete_if do |k,v| if self.class.translations.include?(k.to_sym) self[k] = v true end end super attributes_copy end end end hashie-2.0.3/lib/hashie/extensions/0000755000004100000410000000000012133445414017214 5ustar www-datawww-datahashie-2.0.3/lib/hashie/extensions/deep_merge.rb0000644000004100000410000000127512133445414021642 0ustar www-datawww-datamodule Hashie module Extensions module DeepMerge # Returns a new hash with +self+ and +other_hash+ merged recursively. def deep_merge(other_hash) (class << (h = dup); self; end).send :include, Hashie::Extensions::DeepMerge h.deep_merge!(other_hash) end # Returns a new hash with +self+ and +other_hash+ merged recursively. # Modifies the receiver in place. def deep_merge!(other_hash) other_hash.each do |k,v| (class << (tv = self[k]); self; end).send :include, Hashie::Extensions::DeepMerge self[k] = tv.is_a?(::Hash) && v.is_a?(::Hash) ? tv.deep_merge(v) : v end self end end end end hashie-2.0.3/lib/hashie/extensions/indifferent_access.rb0000644000004100000410000000754112133445414023366 0ustar www-datawww-datamodule 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 def self.included(base) base.class_eval do alias_method :regular_writer, :[]= alias_method :[]=, :indifferent_writer %w(default update replace fetch delete key? values_at).each do |m| alias_method "regular_#{m}", m alias_method m, "indifferent_#{m}" end %w(include? member? has_key?).each do |key_alias| alias_method key_alias, :indifferent_key? 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) (class << hash; self; end).send :include, 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) 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 def indifferent_replace(other_hash) (keys - other_hash.keys).each { |key| delete(key) } other_hash.each { |key, value| self[key] = value } self 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.indifferent_access? end end end end hashie-2.0.3/lib/hashie/extensions/coercion.rb0000644000004100000410000000721212133445414021344 0ustar www-datawww-datamodule Hashie module Extensions module Coercion def self.included(base) base.send :extend, ClassMethods base.send :include, InstanceMethods end module InstanceMethods def []=(key, value) into = self.class.key_coercion(key) || self.class.value_coercion(value) if value && into if into.respond_to?(:coerce) value = into.coerce(value) else value = into.new(value) end end super(key, value) end def replace(other_hash) (keys - other_hash.keys).each { |key| delete(key) } other_hash.each { |key, value| self[key] = value } self end end module ClassMethods # Set up a coercion rule such that any time the specified # key is set it will be coerced into the specified class. # Coercion will occur by first attempting to call Class.coerce # and then by calling Class.new with the value as an argument # in either case. # # @param [Object] key the key or array of keys you would like to be coerced. # @param [Class] into the class into which you want the key(s) coerced. # # @example Coerce a "user" subhash into a User object # class Tweet < Hash # include Hashie::Extensions::Coercion # coerce_key :user, User # end def coerce_key(*attrs) @key_coercions ||= {} into = attrs.pop attrs.each { |key| @key_coercions[key] = into } end alias :coerce_keys :coerce_key # Returns a hash of any existing key coercions. def key_coercions @key_coercions || {} end # Returns the specific key coercion for the specified key, # if one exists. def key_coercion(key) key_coercions[key] end # Set up a coercion rule such that any time a value of the # specified type is set it will be coerced into the specified # class. # # @param [Class] from the type you would like coerced. # @param [Class] into the class into which you would like the value coerced. # @option options [Boolean] :strict (true) whether use exact source class only or include ancestors # # @example Coerce all hashes into this special type of hash # class SpecialHash < Hash # include Hashie::Extensions::Coercion # coerce_value Hash, SpecialHash # # def initialize(hash = {}) # super # hash.each_pair do |k,v| # self[k] = v # end # end # end def coerce_value(from, into, options = {}) options = {:strict => true}.merge(options) if options[:strict] (@strict_value_coercions ||= {})[from] = into else while from.superclass && from.superclass != Object (@lenient_value_coercions ||= {})[from] = into from = from.superclass end end end # Return all value coercions that have the :strict rule as true. def strict_value_coercions; @strict_value_coercions || {} end # Return all value coercions that have the :strict rule as false. def lenient_value_coercions; @value_coercions || {} end # Fetch the value coercion, if any, for the specified object. def value_coercion(value) from = value.class strict_value_coercions[from] || lenient_value_coercions[from] end end end end end hashie-2.0.3/lib/hashie/extensions/key_conversion.rb0000644000004100000410000000423312133445414022600 0ustar www-datawww-datamodule Hashie module Extensions module StringifyKeys # Convert all keys in the hash to strings. # # @example # test = {:abc => 'def'} # test.stringify_keys! # test # => {'abc' => 'def'} def stringify_keys! keys.each do |k| stringify_keys_recursively!(self[k]) self[k.to_s] = self.delete(k) end self end # Return a new hash with all keys converted # to strings. def stringify_keys dup.stringify_keys! end protected # Stringify all keys recursively within nested # hashes and arrays. def stringify_keys_recursively!(object) if self.class === object object.stringify_keys! elsif ::Array === object object.each do |i| stringify_keys_recursively!(i) end object elsif object.respond_to?(:stringify_keys!) object.stringify_keys! else object end end end module SymbolizeKeys # Convert all keys in the hash to symbols. # # @example # test = {'abc' => 'def'} # test.symbolize_keys! # test # => {:abc => 'def'} def symbolize_keys! keys.each do |k| symbolize_keys_recursively!(self[k]) self[k.to_sym] = self.delete(k) end self end # Return a new hash with all keys converted # to symbols. def symbolize_keys dup.symbolize_keys! end protected # Symbolize all keys recursively within nested # hashes and arrays. def symbolize_keys_recursively!(object) if self.class === object object.symbolize_keys! elsif ::Array === object object.each do |i| symbolize_keys_recursively!(i) end object elsif object.respond_to?(:symbolize_keys!) object.symbolize_keys! else object end end end module KeyConversion def self.included(base) base.send :include, SymbolizeKeys base.send :include, StringifyKeys end end end end hashie-2.0.3/lib/hashie/extensions/merge_initializer.rb0000644000004100000410000000135112133445414023243 0ustar www-datawww-datamodule Hashie module Extensions # The MergeInitializer is a super-simple mixin that allows # you to initialize a subclass of Hash with another Hash # to give you faster startup time for Hash subclasses. Note # that you can still provide a default value as a second # argument to the initializer. # # @example # class MyHash < Hash # include Hashie::Extensions::MergeInitializer # end # # h = MyHash.new(:abc => 'def') # h[:abc] # => 'def' # module MergeInitializer def initialize(hash = {}, default = nil, &block) default ? super(default) : super(&block) hash.each do |key, value| self[key] = value end end end end end hashie-2.0.3/lib/hashie/extensions/structure.rb0000644000004100000410000000232612133445414021604 0ustar www-datawww-datamodule Hashie module Extensions # The Structure extension provides facilities for declaring # properties that a Hash can have. This provides for the # creation of structures that still behave like hashes but # do not allow setting non-allowed keys. # # @example # class RestrictedHash < Hash # include Hashie::Extensions::MergeInitializer # include Hashie::Extensions::Structure # # key :first # key :second, :default => 'foo' # end # # h = RestrictedHash.new(:first => 1) # h[:first] # => 1 # h[:second] # => 'foo' # h[:third] # => ArgumentError # module Structure def self.included(base) base.extend ClassMethods base.class_eval do @permitted_keys = superclass.permitted_keys if superclass.respond_to?(:permitted_keys) end end module ClassMethods def key(key, options = {}) (@permitted_keys ||= []) << key if options[:default] (@default_values ||= {})[key] = options.delete(:default) end permitted_keys end def permitted_keys @permitted_keys end end end end end hashie-2.0.3/lib/hashie/extensions/method_access.rb0000644000004100000410000000710412133445414022344 0ustar www-datawww-datamodule Hashie module Extensions # MethodReader allows you to access keys of the hash # via method calls. This gives you an OStruct like way # to access your hash's keys. It will recognize keys # either as strings or symbols. # # Note that while nil keys will be returned as nil, # undefined keys will raise NoMethodErrors. Also note that # #respond_to? has been patched to appropriately recognize # key methods. # # @example # class User < Hash # include Hashie::Extensions::MethodReader # end # # user = User.new # user['first_name'] = 'Michael' # user.first_name # => 'Michael' # # user[:last_name] = 'Bleigh' # user.last_name # => 'Bleigh' # # user[:birthday] = nil # user.birthday # => nil # # user.not_declared # => NoMethodError module MethodReader def respond_to?(name, include_private = false) return true if key?(name.to_s) || key?(name.to_sym) super end def method_missing(name, *args) return self[name.to_s] if key?(name.to_s) return self[name.to_sym] if key?(name.to_sym) super end end # MethodWriter gives you #key_name= shortcuts for # writing to your hash. Keys are written as strings, # override #convert_key if you would like to have symbols # or something else. # # Note that MethodWriter also overrides #respond_to such # that any #method_name= will respond appropriately as true. # # @example # class MyHash < Hash # include Hashie::Extensions::MethodWriter # end # # h = MyHash.new # h.awesome = 'sauce' # h['awesome'] # => 'sauce' # module MethodWriter def respond_to?(name, include_private = false) return true if name.to_s =~ /=$/ super end def method_missing(name, *args) if args.size == 1 && name.to_s =~ /(.*)=$/ return self[convert_key($1)] = args.first end super end def convert_key(key) key.to_s end end # MethodQuery gives you the ability to check for the truthiness # of a key via method calls. Note that it will return false if # the key is set to a non-truthful value, not if the key isn't # set at all. Use #key? for checking if a key has been set. # # MethodQuery will check against both string and symbol names # of the method for existing keys. It also patches #respond_to # to appropriately detect the query methods. # # @example # class MyHash < Hash # include Hashie::Extensions::MethodQuery # end # # h = MyHash.new # h['abc'] = 123 # h.abc? # => true # h['def'] = nil # h.def? # => false # h.hji? # => NoMethodError module MethodQuery def respond_to?(name, include_private = false) return true if name.to_s =~ /(.*)\?$/ && (key?($1) || key?($1.to_sym)) super end def method_missing(name, *args) if args.empty? && name.to_s =~ /(.*)\?$/ && (key?($1) || key?($1.to_sym)) return self[$1] || self[$1.to_sym] end super end end # A macro module that will automatically include MethodReader, # MethodWriter, and MethodQuery, giving you the ability to read, # write, and query keys in a hash using method call shortcuts. module MethodAccess def self.included(base) [MethodReader, MethodWriter, MethodQuery].each do |mod| base.send :include, mod end end end end end hashie-2.0.3/lib/hashie.rb0000644000004100000410000000212012133445414015335 0ustar www-datawww-datamodule Hashie autoload :Clash, 'hashie/clash' autoload :Dash, 'hashie/dash' autoload :Hash, 'hashie/hash' autoload :HashExtensions, 'hashie/hash_extensions' autoload :Mash, 'hashie/mash' autoload :PrettyInspect, 'hashie/hash_extensions' autoload :Trash, 'hashie/trash' module Extensions autoload :Coercion, 'hashie/extensions/coercion' autoload :DeepMerge, 'hashie/extensions/deep_merge' autoload :KeyConversion, 'hashie/extensions/key_conversion' autoload :IndifferentAccess, 'hashie/extensions/indifferent_access' autoload :MergeInitializer, 'hashie/extensions/merge_initializer' autoload :MethodAccess, 'hashie/extensions/method_access' autoload :MethodQuery, 'hashie/extensions/method_access' autoload :MethodReader, 'hashie/extensions/method_access' autoload :MethodWriter, 'hashie/extensions/method_access' autoload :StringifyKeys, 'hashie/extensions/key_conversion' autoload :SymbolizeKeys, 'hashie/extensions/key_conversion' end end