mbleigh-mash 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,22 @@
1
+ === 0.0.4 / 2008-04-25
2
+
3
+ * Setting up for GitHub gem hosting instead of Rubyforge.
4
+
5
+ === 0.0.3 / 2008-04-19
6
+
7
+ * [] no longer defaults to a new Mash, will return nil if
8
+ * Attribute-esque method names will yield the default value if not set
9
+ * Hash extended with #to_mash and #stringify_keys
10
+ * Added #dup and #deep_merge
11
+ * Aliased the default Hash methods so they are still accessible
12
+ * Cleaned up the recursive conversion process
13
+
14
+ === 0.0.2 / 2008-04-12
15
+
16
+ * Added bang(!) method support
17
+ * No longer automatically multi-level assigning
18
+ * Hash conversion now calls methods instead of []= to allow for overrides
19
+
20
+ === 0.0.1 / 2008-04-12
21
+
22
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/mash.rb
6
+ spec/mash_spec.rb
7
+ spec/spec_helper.rb
data/README.txt ADDED
@@ -0,0 +1,68 @@
1
+ = Mash (Mocking Hash)
2
+
3
+ http://github.com/mbleigh/mash
4
+
5
+ == DESCRIPTION:
6
+
7
+ Mash is an extended Hash that gives simple pseudo-object functionality
8
+ that can be built from hashes and easily extended. It is designed to
9
+ be used in RESTful API libraries to provide easy object-like access
10
+ to JSON and XML parsed hashes.
11
+
12
+ == SYNOPSIS:
13
+
14
+ mash = Mash.new
15
+ mash.name? # => false
16
+ mash.name # => nil
17
+ mash.name = "My Mash"
18
+ mash.name # => "My Mash"
19
+ mash.name? # => true
20
+ mash.inspect # => <Mash name="My Mash">
21
+
22
+ mash = Mash.new
23
+ # use bang methods for multi-level assignment
24
+ mash.author!.name = "Michael Bleigh"
25
+ mash.author # => <Mash name="Michael Bleigh">
26
+
27
+ == INSTALL:
28
+
29
+ Gem:
30
+
31
+ Mash is hosted on the GitHub gem repository, so if you haven't already:
32
+
33
+ gem sources -a http://gems.github.com/
34
+ sudo gem install mbleigh-mash
35
+
36
+ Git:
37
+
38
+ git clone git://github.com/mbleigh/mash.git
39
+
40
+ == RESOURCES
41
+
42
+ If you encounter any problems or have ideas for new features
43
+ please report them at the Lighthouse project for Mash:
44
+
45
+ http://mbleigh.lighthouseapp.com/projects/10112-mash
46
+
47
+ == LICENSE:
48
+
49
+ Copyright (c) 2008 Michael Bleigh
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining
52
+ a copy of this software and associated documentation files (the
53
+ 'Software'), to deal in the Software without restriction, including
54
+ without limitation the rights to use, copy, modify, merge, publish,
55
+ distribute, sublicense, and/or sell copies of the Software, and to
56
+ permit persons to whom the Software is furnished to do so, subject to
57
+ the following conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require './lib/mash.rb'
3
+ require 'spec/rake/spectask'
4
+
5
+ desc "Run specs."
6
+ Spec::Rake::SpecTask.new("spec") do |t|
7
+ t.spec_files = "spec/*_spec.rb"
8
+ end
data/lib/mash.rb ADDED
@@ -0,0 +1,226 @@
1
+ # Mash allows you to create pseudo-objects that have method-like
2
+ # accessors for hash keys. This is useful for such implementations
3
+ # as an API-accessing library that wants to fake robust objects
4
+ # without the overhead of actually doing so. Think of it as OpenStruct
5
+ # with some additional goodies.
6
+ #
7
+ # A Mash will look at the methods you pass it and perform operations
8
+ # based on the following rules:
9
+ #
10
+ # * No punctuation: Returns the value of the hash for that key, or nil if none exists.
11
+ # * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
12
+ # * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
13
+ # * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
14
+ #
15
+ # == Basic Example
16
+ #
17
+ # mash = Mash.new
18
+ # mash.name? # => false
19
+ # mash.name = "Bob"
20
+ # mash.name # => "Bob"
21
+ # mash.name? # => true
22
+ #
23
+ # == Hash Conversion Example
24
+ #
25
+ # hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
26
+ # mash = Mash.new(hash)
27
+ # mash.a.b # => 23
28
+ # mash.a.d.e # => "abc"
29
+ # mash.f.first.g # => 44
30
+ # mash.f.last # => 12
31
+ #
32
+ # == Bang Example
33
+ #
34
+ # mash = Mash.new
35
+ # mash.author # => nil
36
+ # mash.author! # => <Mash>
37
+ #
38
+ # mash = Mash.new
39
+ # mash.author!.name = "Michael Bleigh"
40
+ # mash.author # => <Mash name="Michael Bleigh">
41
+ #
42
+ class Mash < Hash
43
+ VERSION = '0.0.3'
44
+
45
+ # If you pass in an existing hash, it will
46
+ # convert it to a Mash including recursively
47
+ # descending into arrays and hashes, converting
48
+ # them as well.
49
+ def initialize(source_hash = nil)
50
+ deep_update(source_hash) if source_hash
51
+ super(nil)
52
+ end
53
+
54
+ def id #:nodoc:
55
+ self["id"] ? self["id"] : super
56
+ end
57
+
58
+ # Borrowed from Merb's Mash object.
59
+ #
60
+ # ==== Parameters
61
+ # key<Object>:: The default value for the mash. Defaults to nil.
62
+ #
63
+ # ==== Alternatives
64
+ # If key is a Symbol and it is a key in the mash, then the default value will
65
+ # be set to the value matching the key.
66
+ def default(key = nil)
67
+ if key.is_a?(Symbol) && key?(key)
68
+ self[key]
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ alias_method :regular_reader, :[]
75
+ alias_method :regular_writer, :[]=
76
+
77
+ # Retrieves an attribute set in the Mash. Will convert
78
+ # any key passed in to a string before retrieving.
79
+ def [](key)
80
+ key = convert_key(key)
81
+ regular_reader(key)
82
+ end
83
+
84
+ # Sets an attribute in the Mash. Key will be converted to
85
+ # a string before it is set.
86
+ def []=(key,value) #:nodoc:
87
+ key = convert_key(key)
88
+ regular_writer(key,convert_value(value))
89
+ end
90
+
91
+ # This is the bang method reader, it will return a new Mash
92
+ # if there isn't a value already assigned to the key requested.
93
+ def initializing_reader(key)
94
+ return self[key] if key?(key)
95
+ self[key] = Mash.new
96
+ end
97
+
98
+ # Duplicates the current mash as a new mash.
99
+ def dup
100
+ Mash.new(self)
101
+ end
102
+
103
+ alias_method :regular_inspect, :inspect
104
+
105
+ alias_method :picky_key?, :key?
106
+ def key?(key)
107
+ picky_key?(convert_key(key))
108
+ end
109
+
110
+ # Prints out a pretty object-like string of the
111
+ # defined attributes.
112
+ def inspect
113
+ ret = "<#{self.class.to_s}"
114
+ keys.sort.each do |key|
115
+ ret << " #{key}=#{self[key].inspect}"
116
+ end
117
+ ret << ">"
118
+ ret
119
+ end
120
+ alias_method :to_s, :inspect
121
+
122
+ # Performs a deep_update on a duplicate of the
123
+ # current mash.
124
+ def deep_merge(other_hash)
125
+ dup.deep_merge!(other_hash)
126
+ end
127
+
128
+ # Recursively merges this mash with the passed
129
+ # in hash, merging each hash in the hierarchy.
130
+ def deep_update(other_hash)
131
+ stringified_hash = other_hash.stringify_keys
132
+ stringified_hash.each_pair do |k,v|
133
+ k = convert_key(k)
134
+ self[k] = self[k].to_mash if self[k].is_a?(Hash) unless self[k].is_a?(Mash)
135
+ if self[k].is_a?(Hash) && stringified_hash[k].is_a?(Hash)
136
+ self[k].deep_merge!(stringified_hash[k])
137
+ else
138
+ self.send(k + "=", convert_value(stringified_hash[k]))
139
+ end
140
+ end
141
+ end
142
+ alias_method :deep_merge!, :deep_update
143
+
144
+ # ==== Parameters
145
+ # other_hash<Hash>::
146
+ # A hash to update values in the mash with. Keys will be
147
+ # stringified and Hashes will be converted to Mashes.
148
+ #
149
+ # ==== Returns
150
+ # Mash:: The updated mash.
151
+ def update(other_hash)
152
+ other_hash.each_pair do |key, value|
153
+ if respond_to?(convert_key(key) + "=")
154
+ self.send(convert_key(key) + "=", convert_value(value))
155
+ else
156
+ regular_writer(convert_key(key), convert_value(value))
157
+ end
158
+ end
159
+ self
160
+ end
161
+ alias_method :merge!, :update
162
+
163
+ # Converts a mash back to a hash (with stringified keys)
164
+ def to_hash
165
+ Hash.new(default).merge(self)
166
+ end
167
+
168
+ def method_missing(method_name, *args) #:nodoc:
169
+ if (match = method_name.to_s.match(/(.*)=$/)) && args.size == 1
170
+ self[match[1]] = args.first
171
+ elsif (match = method_name.to_s.match(/(.*)\?$/)) && args.size == 0
172
+ key?(match[1])
173
+ elsif (match = method_name.to_s.match(/(.*)!$/)) && args.size == 0
174
+ initializing_reader(match[1])
175
+ elsif key?(method_name)
176
+ self[method_name]
177
+ elsif match = method_name.to_s.match(/^([a-z][a-z0-9A-Z_]+)$/)
178
+ default
179
+ else
180
+ super
181
+ end
182
+ end
183
+
184
+ protected
185
+
186
+ def convert_key(key) #:nodoc:
187
+ key.to_s
188
+ end
189
+
190
+ def convert_value(value) #:nodoc:
191
+ case value
192
+ when Hash
193
+ value.is_a?(Mash) ? value : value.to_mash
194
+ when Array
195
+ value.collect{ |e| convert_value(e) }
196
+ else
197
+ value
198
+ end
199
+ end
200
+ end
201
+
202
+ class Hash
203
+ # Returns a new Mash initialized from this Hash.
204
+ def to_mash
205
+ mash = Mash.new(self)
206
+ mash.default = default
207
+ mash
208
+ end
209
+
210
+ # Returns a duplicate of the current hash with
211
+ # all of the keys converted to strings.
212
+ def stringify_keys
213
+ dup.stringify_keys!
214
+ end
215
+
216
+ # Converts all of the keys to strings
217
+ def stringify_keys!
218
+ keys.each{|k|
219
+ v = delete(k)
220
+ self[k.to_s] = v
221
+ v.stringify_keys! if v.is_a?(Hash)
222
+ v.each{|p| p.stringify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
223
+ }
224
+ self
225
+ end
226
+ end
data/mash.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "mash"
3
+ s.version = "0.0.4"
4
+ s.date = "2008-04-26"
5
+ s.summary = "An extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended"
6
+ s.email = "michael@intridea.com"
7
+ s.homepage = "http://github.com/mbleigh/mash"
8
+ s.summary = "Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended"
9
+ s.has_rdoc = true
10
+ s.authors = ["Michael Bleigh"]
11
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "mash.gemspec", "lib/mash.rb", "spec/mash_spec.rb","spec/spec_helper.rb"]
12
+ s.rdoc_options = ["--main", "README.txt"]
13
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
14
+ end
data/spec/mash_spec.rb ADDED
@@ -0,0 +1,114 @@
1
+ require File.join(File.dirname(__FILE__),"..","lib","mash")
2
+ require File.join(File.dirname(__FILE__),"spec_helper")
3
+
4
+ describe Mash do
5
+ before(:each) do
6
+ @mash = Mash.new
7
+ end
8
+
9
+ it "should inherit from hash" do
10
+ @mash.is_a?(Hash).should be_true
11
+ end
12
+
13
+ it "should be able to set hash values through method= calls" do
14
+ @mash.test = "abc"
15
+ @mash["test"].should == "abc"
16
+ end
17
+
18
+ it "should be able to retrieve set values through method calls" do
19
+ @mash["test"] = "abc"
20
+ @mash.test.should == "abc"
21
+ end
22
+
23
+ it "should test for already set values when passed a ? method" do
24
+ @mash.test?.should be_false
25
+ @mash.test = "abc"
26
+ @mash.test?.should be_true
27
+ end
28
+
29
+ it "should make all [] and []= into strings for consistency" do
30
+ @mash["abc"] = 123
31
+ @mash.key?('abc').should be_true
32
+ @mash["abc"].should == 123
33
+ end
34
+
35
+ it "should have a to_s that is identical to its inspect" do
36
+ @mash.abc = 123
37
+ @mash.to_s.should == @mash.inspect
38
+ end
39
+
40
+ it "should return nil instead of raising an error for attribute-esque method calls" do
41
+ @mash.abc.should be_nil
42
+ end
43
+
44
+ it "should return a Mash when passed a bang method to a non-existenct key" do
45
+ @mash.abc!.is_a?(Mash).should be_true
46
+ end
47
+
48
+ it "should return the existing value when passed a bang method for an existing key" do
49
+ @mash.name = "Bob"
50
+ @mash.name!.should == "Bob"
51
+ end
52
+
53
+ it "#initializing_reader should return a Mash when passed a non-existent key" do
54
+ @mash.initializing_reader(:abc).is_a?(Mash).should be_true
55
+ end
56
+
57
+ it "should allow for multi-level assignment through bang methods" do
58
+ @mash.author!.name = "Michael Bleigh"
59
+ @mash.author.should == Mash.new(:name => "Michael Bleigh")
60
+ @mash.author!.website!.url = "http://www.mbleigh.com/"
61
+ @mash.author.website.should == Mash.new(:url => "http://www.mbleigh.com/")
62
+ end
63
+
64
+ it "#deep_update should recursively mash mashes and hashes together" do
65
+ @mash.first_name = "Michael"
66
+ @mash.last_name = "Bleigh"
67
+ @mash.details = {:email => "michael@asf.com"}.to_mash
68
+ @mash.deep_update({:details => {:email => "michael@intridea.com"}})
69
+ @mash.details.email.should == "michael@intridea.com"
70
+ end
71
+
72
+ context "#initialize" do
73
+ it "should convert an existing hash to a Mash" do
74
+ converted = Mash.new({:abc => 123, :name => "Bob"})
75
+ converted.abc.should == 123
76
+ converted.name.should == "Bob"
77
+ end
78
+
79
+ it "should convert hashes recursively into mashes" do
80
+ converted = Mash.new({:a => {:b => 1, :c => {:d => 23}}})
81
+ converted.a.is_a?(Mash).should be_true
82
+ converted.a.b.should == 1
83
+ converted.a.c.d.should == 23
84
+ end
85
+
86
+ it "should convert hashes in arrays into mashes" do
87
+ converted = Mash.new({:a => [{:b => 12}, 23]})
88
+ converted.a.first.b.should == 12
89
+ converted.a.last.should == 23
90
+ end
91
+ end
92
+ end
93
+
94
+ describe Hash do
95
+ it "should be convertible to a Mash" do
96
+ mash = {:some => "hash"}.to_mash
97
+ mash.is_a?(Mash).should be_true
98
+ mash.some.should == "hash"
99
+ end
100
+
101
+ it "#stringify_keys! should turn all keys into strings" do
102
+ hash = {:a => "hey", 123 => "bob"}
103
+ hash.stringify_keys!
104
+ hash.should == {"a" => "hey", "123" => "bob"}
105
+ end
106
+
107
+ it "#stringify_keys should return a hash with stringified keys" do
108
+ hash = {:a => "hey", 123 => "bob"}
109
+ stringified_hash = hash.stringify_keys
110
+ hash.should == {:a => "hey", 123 => "bob"}
111
+ stringified_hash.should == {"a" => "hey", "123" => "bob"}
112
+ end
113
+
114
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__),"..","lib","mash")
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mbleigh-mash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Michael Bleigh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-04-26 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: michael@intridea.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - History.txt
24
+ - Manifest.txt
25
+ - README.txt
26
+ files:
27
+ - History.txt
28
+ - Manifest.txt
29
+ - README.txt
30
+ - Rakefile
31
+ - mash.gemspec
32
+ - lib/mash.rb
33
+ - spec/mash_spec.rb
34
+ - spec/spec_helper.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/mbleigh/mash
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --main
40
+ - README.txt
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.0.1
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended
62
+ test_files: []
63
+