summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Mitchell <dave@connectedbits.com>2012-04-15 23:23:59 -0400
committerDave Mitchell <dave@connectedbits.com>2012-04-15 23:23:59 -0400
commit4c9bee6dcd3f0f89786f1e54628b705fe88b9e47 (patch)
treeb1fa6ee87b8262ae260d9334fe9b29c021403781
parentf7cb5e2b3c3ea3edd66ed2f9865eac7d7db80359 (diff)
downloadhashie-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.markdown5
-rw-r--r--lib/hashie/mash.rb27
-rw-r--r--spec/hashie/mash_spec.rb28
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"})