diff options
author | Dave Mitchell <dave@connectedbits.com> | 2012-04-15 23:23:59 -0400 |
---|---|---|
committer | Dave Mitchell <dave@connectedbits.com> | 2012-04-15 23:23:59 -0400 |
commit | 4c9bee6dcd3f0f89786f1e54628b705fe88b9e47 (patch) | |
tree | b1fa6ee87b8262ae260d9334fe9b29c021403781 | |
parent | f7cb5e2b3c3ea3edd66ed2f9865eac7d7db80359 (diff) | |
download | hashie-4c9bee6dcd3f0f89786f1e54628b705fe88b9e47.tar.gz |
Add under-bang extension to Hashie::Mash
Under-bang is similar to Mash's bang (!) operation, except that it doesn't create new keys. Instead, it just returns an unassociated new Mash instance when it encounters a missing key.
Example:
mash = Hashie::Mash.new
mash.author_.name? # => false
mash.author_.website_.url # => nil
mash.author_.age = 100 # => 100 (assigned to temp on stack)
mash.inspect # => <Hashie::Mash>
-rw-r--r-- | README.markdown | 5 | ||||
-rw-r--r-- | lib/hashie/mash.rb | 27 | ||||
-rw-r--r-- | spec/hashie/mash_spec.rb | 28 |
3 files changed, 59 insertions, 1 deletions
diff --git a/README.markdown b/README.markdown index 30504c7..60ac086 100644 --- a/README.markdown +++ b/README.markdown @@ -124,6 +124,11 @@ to JSON and XML parsed hashes. mash.author!.name = "Michael Bleigh" mash.author # => <Hashie::Mash name="Michael Bleigh"> + mash = Mash.new + # use under-bang methods for multi-level testing + mash.author_.name? # => false + mash.inspect # => <Hashie::Mash> + **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. diff --git a/lib/hashie/mash.rb b/lib/hashie/mash.rb index 673e210..928f726 100644 --- a/lib/hashie/mash.rb +++ b/lib/hashie/mash.rb @@ -14,6 +14,7 @@ module Hashie # * Assignment (<tt>=</tt>): Sets the attribute of the given method name. # * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set. # * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes. + # * Under Bang (<tt>_</tt>): Like Bang, but returns a new Mash rather than creating a key. Used to test existance in deep Mashes. # # == Basic Example # @@ -42,6 +43,17 @@ module Hashie # mash.author!.name = "Michael Bleigh" # mash.author # => <Mash name="Michael Bleigh"> # + # == Under Bang Example + # + # mash = Mash.new + # mash.author # => nil + # mash.author_ # => <Mash> + # mash.author_.name # => nil + # + # mash = Mash.new + # mash.author_.name = "Michael Bleigh" (assigned to temp object) + # mash.author # => <Mash> + # class Mash < Hashie::Hash include Hashie::PrettyInspect alias_method :to_s, :inspect @@ -91,6 +103,17 @@ module Hashie 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 delete(key) super(convert_key(key)) end @@ -155,7 +178,7 @@ module Hashie def method_missing(method_name, *args, &blk) return self.[](method_name, &blk) if key?(method_name) - match = method_name.to_s.match(/(.*?)([?=!]?)$/) + match = method_name.to_s.match(/(.*?)([?=!_]?)$/) case match[2] when "=" self[match[1]] = args.first @@ -163,6 +186,8 @@ module Hashie !!self[match[1]] when "!" initializing_reader(match[1]) + when "_" + underbang_reader(match[1]) else default(method_name, *args, &blk) end diff --git a/spec/hashie/mash_spec.rb b/spec/hashie/mash_spec.rb index f5c559f..6d33a25 100644 --- a/spec/hashie/mash_spec.rb +++ b/spec/hashie/mash_spec.rb @@ -71,6 +71,15 @@ describe Hashie::Mash do @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 @@ -82,6 +91,13 @@ describe Hashie::Mash do @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 call super if type is not a key" do # @mash.type.should == Hashie::Mash # end @@ -204,6 +220,18 @@ describe Hashie::Mash do 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"}) |