hashshashin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
+ environment_id="ruby-1.9.3-p194@hashshashin"
8
+
9
+ #
10
+ # Uncomment the following lines if you want to verify rvm version per project
11
+ #
12
+ # rvmrc_rvm_version="1.10.2" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+ #
18
+
19
+ #
20
+ # Uncomment following line if you want options to be set only for given project.
21
+ #
22
+ # PROJECT_JRUBY_OPTS=( --1.9 )
23
+ #
24
+ # The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
25
+ #
26
+ # chmod +x ${rvm_path}/hooks/after_use_jruby_opts
27
+ #
28
+
29
+ #
30
+ # First we attempt to load the desired environment directly from the environment
31
+ # file. This is very fast and efficient compared to running through the entire
32
+ # CLI and selector. If you want feedback on which environment was used then
33
+ # insert the word 'use' after --create as this triggers verbose mode.
34
+ #
35
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
36
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
37
+ then
38
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
39
+
40
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
41
+ then
42
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
43
+ fi
44
+ else
45
+ # If the environment file has not yet been created, use the RVM CLI to select.
46
+ if ! rvm --create "$environment_id"
47
+ then
48
+ echo "Failed to create RVM environment '${environment_id}'."
49
+ return 1
50
+ fi
51
+ fi
52
+
53
+ #
54
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
55
+ # it be automatically loaded. Uncomment the following and adjust the filename if
56
+ # necessary.
57
+ #
58
+ # filename=".gems"
59
+ # if [[ -s "$filename" ]]
60
+ # then
61
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
62
+ # fi
63
+
64
+ # If you use bundler, this might be useful to you:
65
+ # if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
66
+ # then
67
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
68
+ # gem install bundler
69
+ # fi
70
+ # if [[ -s Gemfile ]] && command -v bundle
71
+ # then
72
+ # bundle install
73
+ # fi
74
+
75
+ if [[ $- == *i* ]] # check for interactive shells
76
+ then
77
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
78
+ else
79
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
80
+ fi
81
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Aaron Oman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Hashshashin
2
+
3
+ What is Hashshashin? [Hashish consuming, intoxicated assassins](https://en.wikipedia.org/wiki/Assassins).
4
+
5
+ This is a SimpleDelegator that wraps Ruby's built-in Hash object and adds useful
6
+ functionality. For example, there's a recursive method to convert all keys to symbols.
7
+
8
+ ##Installation
9
+ Add this line to your project's Gemfile:
10
+
11
+ ```
12
+ gem 'hashshashin'
13
+ ```
14
+
15
+ Then execute:
16
+
17
+ ```
18
+ bundle install
19
+ ```
20
+
21
+ Or, install it yourself with:
22
+
23
+ ```
24
+ $ gem install hashshashin
25
+ ```
26
+
27
+ ##Usage
28
+ Create a new instance:
29
+
30
+ ```
31
+ regular_hash = { 'a' => 1, 'b' => 2, 'c' => 3 }
32
+ fancy_hash = Hashshashin.new(regular_hash)
33
+
34
+ fancy_hash.class # => Hashshashin
35
+ ```
36
+
37
+ Invoking a Hashashin instance method:
38
+
39
+ ```
40
+ fancy_hash.recursive_symbolize_keys # => { :a => 1, :b => 2, :c => 3 }
41
+ ```
42
+
43
+ ##Contributing
44
+ 1. Fork it
45
+ 2. Create your feature branch (git checkout -b my-new-feature)
46
+ 3. Commit your changes (git commit -am 'Add some feature')
47
+ 4. Push to the branch (git push origin my-new-feature)
48
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << 'lib/hashshashin'
8
+ t.test_files = FileList['test/lib/hashshashin/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'hashshashin/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = 'hashshashin'
9
+ gem.version = HashshashinUtils::VERSION
10
+ gem.authors = ["Aaron Oman"]
11
+ gem.email = ["aaron@unbounce.com"]
12
+ gem.description = %q{Convenient recursive methods to extend Ruby's built-in Hash.}
13
+ gem.summary = %q{Destructive and non-destructive recursive methods to augment the build in Hash.}
14
+ gem.homepage = ""
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency 'rake'
22
+ gem.add_development_dependency 'shoulda'
23
+ gem.add_development_dependency 'rspec'
24
+ gem.add_dependency 'timecop'
25
+ end
@@ -0,0 +1,3 @@
1
+ module HashshashinUtils
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,241 @@
1
+ require 'delegate'
2
+
3
+ class Hashshashin < SimpleDelegator
4
+ # Adds the ability to cherrypick specific key/value pairs from a hash
5
+ #
6
+ # Example:
7
+ #
8
+ # x = { a: 1, b: 2, c: 3 }
9
+ # y = Hashshashin.new(x)
10
+ #
11
+ # y.pick
12
+ # => {}
13
+ #
14
+ # y.pick(:a, :c)
15
+ # => { a: 1, c: 3 }
16
+ #
17
+ # y.pick(:a, :c, :d)
18
+ # => { a: 1, c: 3, d: nil }
19
+ #
20
+ def pick(*keys)
21
+ values = values_at(*keys)
22
+ Hash[keys.zip(values)]
23
+ end
24
+
25
+ # See:
26
+ # http://stackoverflow.com/a/12360142/321897
27
+ #
28
+ # By default, remove any key/value pairs recursively from the hash if the value is
29
+ # nil, 0, false, empty or a blank string.
30
+ # Alternatively, a block can be passed in which specifies the condition for whether the
31
+ # key/value pair should be excised.
32
+ #
33
+ # Example:
34
+ #
35
+ # {
36
+ # hash: {
37
+ # false: false, nil: nil, zero: 0, empty_string: '', empty_list: [], empty_hash: {}, one: 1
38
+ # },
39
+ # false: false,
40
+ # nil: nil,
41
+ # zero: 0,
42
+ # empty_string: '',
43
+ # empty_list: [],
44
+ # empty_hash: {},
45
+ # one: 1
46
+ # }
47
+ #
48
+ # Becomes:
49
+ #
50
+ # { hash: { one: 1 }, one: 1 }
51
+ #
52
+ def recursive_compact!
53
+ self.delete_if do |key, value|
54
+ if block_given?
55
+ yield(key, value)
56
+ else
57
+ value.nil? || value === 0 || value === false || empty?(value)
58
+ end
59
+ end
60
+
61
+ self.each do |key, value|
62
+ value = Hashshashin.new(value) if value.is_a?(Hash)
63
+
64
+ if value.is_a?(Hashshashin)
65
+ if block_given?
66
+ self[key] = value.recursive_compact!(&Proc.new)
67
+ else
68
+ self[key] = value.recursive_compact!
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ return self
75
+ end
76
+
77
+ # The same as #recursive_compact! above, except this doesn't modify self; instead,
78
+ # this returns a new hash with the recursive compaction applied.
79
+ #
80
+ def recursive_compact(&block)
81
+ Marshal.load(Marshal.dump(self)).recursive_compact!(&block)
82
+ end
83
+
84
+ # By default, call #to_datetime, then #rfc3339 on the values for every key/value pair
85
+ # in the hash where the value is a Time or DateTime object, or responds to
86
+ # #to_datetime.
87
+ # Alternatively, a block can be passed in that specifies the acceptance condition for
88
+ # key/value pairs in the hash.
89
+ #
90
+ # Note: Regardless of the acceptance block, we will call #to_datetime, then #rfc3339
91
+ # on the value.
92
+ #
93
+ # Example:
94
+ #
95
+ # {
96
+ # hash: {
97
+ # time: '2012-12-05 12:26:54 -0800', # Time.now (object) shown as a string.
98
+ # datetime: 'Wed, 05 Dec 2012 12:26:56 -0800' # DateTime.now (object) shown as a string.
99
+ # },
100
+ # time: '2012-12-05 12:26:54 -0800', # Time.now (object) shown as a string.
101
+ # datetime: 'Wed, 05 Dec 2012 12:26:56 -0800' # DateTime.now (object) shown as a string.
102
+ # }
103
+ #
104
+ # Becomes:
105
+ # {
106
+ # hash: {
107
+ # time: '2012-01-01T12:26:54-08:00', # String
108
+ # datetime: '2012-01-01T12:26:56-08:00' # String
109
+ # },
110
+ # time: '2012-01-01T12:26:54-08:00', # String
111
+ # datetime: '2012-01-01T12:26:56-08:00' # String
112
+ # }
113
+ #
114
+ def recursive_rfc3339!
115
+ hash = self.dup
116
+
117
+ hash.keep_if do |key, value|
118
+ if block_given?
119
+ yield(key, value)
120
+ else
121
+ time?(value) || datetime?(value) || rfc3339able_and_not_string?(value)
122
+ end
123
+ end
124
+
125
+ hash.each do |key, value|
126
+ datetime = value.to_datetime
127
+ hash[key] = datetime.rfc3339 if datetime.respond_to?(:rfc3339)
128
+ end
129
+
130
+ self.merge!(hash)
131
+
132
+ self.each do |key, value|
133
+ value = Hashshashin.new(value) if value.is_a?(Hash)
134
+
135
+ if value.is_a?(Hashshashin)
136
+ if block_given?
137
+ self[key] = value.recursive_rfc3339!(&Proc.new)
138
+ else
139
+ self[key] = value.recursive_rfc3339!
140
+ end
141
+ end
142
+
143
+ end
144
+
145
+ return self
146
+ end
147
+
148
+ # The same as #recursive_rfc3339! above, except this doesn't modify self; instead,
149
+ # this returns a new hash with the recursive time formatting applied.
150
+ #
151
+ def recursive_rfc3339(&block)
152
+ Marshal.load(Marshal.dump(self)).recursive_rfc3339!(&block)
153
+ end
154
+
155
+ # Replace all string keys with symbols.
156
+ # ie.
157
+ #
158
+ # Hashshashin.new({ 'a' => 1, 'b' => 2 }).recursive_symbolize_keys!
159
+ #
160
+ # => { a: 1, b: 2 }
161
+ #
162
+ def recursive_symbolize_keys!
163
+ string_pairs = self.select { |key, value| key.kind_of?(String) }
164
+
165
+ string_pairs.each do |key, value|
166
+ self[key.to_sym] = value
167
+ self.delete(key)
168
+ end
169
+
170
+ self.each do |key, value|
171
+ value = Hashshashin.new(value) if value.is_a?(Hash)
172
+
173
+ self[key] = value.recursive_symbolize_keys! if value.is_a?(Hashshashin)
174
+ end
175
+
176
+ self
177
+ end
178
+
179
+ # The same as #recursive_symbolize_keys! above, except this doesn't modify self;
180
+ # instead, this returns a new hash with the recursively defined symbolized keys.
181
+ #
182
+ def recursive_symbolize_keys
183
+ Marshal.load(Marshal.dump(self)).recursive_symbolize_keys!
184
+ end
185
+
186
+ # This method takes a block that returns an array of [key, value] pairs.
187
+ # It then returns a new hash where each [key, value] pair in the original hash has been
188
+ # replaced with those returned by the block.
189
+ #
190
+ # For example:
191
+ #
192
+ # h = { a: 1, b: 2, c: 3, d: 4 }
193
+ #
194
+ # Hashshashin.new(h).recursively_apply do |key, value|
195
+ # if v < 3
196
+ # [key, value + 10]
197
+ # else
198
+ # [key, value]
199
+ # end
200
+ # end
201
+ #
202
+ # # => { a: 11, b: 12, c: 3, d: 4 }
203
+ #
204
+ def recursively_apply
205
+ return self unless block_given?
206
+
207
+ new_hash = Hash[ self.collect { |key, value| yield(key, value) } ]
208
+
209
+ new_hash.each do |key, value|
210
+ value = Hashshashin.new(value) if value.is_a?(Hash)
211
+
212
+ new_hash[key] = value.recursively_apply(&Proc.new) if value.is_a?(Hashshashin)
213
+ end
214
+
215
+ new_hash
216
+ end
217
+
218
+ private
219
+
220
+ def time?(value)
221
+ value.kind_of?(Time)
222
+ end
223
+
224
+ def datetime?(value)
225
+ value.kind_of?(DateTime)
226
+ end
227
+
228
+ def rfc3339able_and_not_string?(value)
229
+ value.respond_to?('to_datetime') && !value.kind_of?(String)
230
+ end
231
+
232
+ def empty?(value)
233
+ empty = value.empty? if value.respond_to?('empty?')
234
+
235
+ strip_empty = if value.respond_to?('strip') && value.respond_to?('empty')
236
+ value.strip.empty?
237
+ end
238
+
239
+ empty || strip_empty
240
+ end
241
+ end
@@ -0,0 +1,361 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashshashin do
4
+ let(:populated_hash) { { a: 1, b: 2, c: 3 } }
5
+
6
+ describe '#pick' do
7
+ context 'for no keys' do
8
+ it('returns an empty hash') { Hashshashin.new(populated_hash).pick.should == {} }
9
+ it('returns nils') { Hashshashin.new({}).pick(:a, :b).should == { a: nil, b: nil } }
10
+ end
11
+
12
+ context 'for a single key' do
13
+ it('returns the hash') { Hashshashin.new(populated_hash).pick(:a).should == { a: 1 } }
14
+ it('returns the hash') { Hashshashin.new({ a: 1 }).pick(:a, :b).should == { a: 1, b: nil } }
15
+ end
16
+
17
+ context 'for multiple keys' do
18
+ it('returns a hash') { Hashshashin.new(populated_hash).pick(:a, :c).should == { a: 1, c: 3 } }
19
+ end
20
+ end
21
+
22
+ describe '#recursive_compact' do
23
+
24
+ it 'does not alter self' do
25
+ Timecop.freeze(DateTime.now) do
26
+ decorator = Hashshashin.new(sample_hash)
27
+ decorator.recursive_compact
28
+ decorator.should == Hashshashin.new(sample_hash)
29
+ end
30
+ end
31
+
32
+ it 'returns a Hashshashin instance' do
33
+ Hashshashin.new(sample_hash).recursive_compact.kind_of?(Hashshashin).should be_true
34
+ end
35
+
36
+ it 'removes nil' do
37
+ result = Hashshashin.new(sample_hash).recursive_compact
38
+ result.has_key?(:nil).should be_false
39
+ end
40
+
41
+ it 'recursively removes nil' do
42
+ result = Hashshashin.new(sample_hash).recursive_compact
43
+ result[:hash].has_key?(:nil).should be_false
44
+ end
45
+
46
+ it 'removes 0' do
47
+ result = Hashshashin.new(sample_hash).recursive_compact
48
+ result.has_key?(:zero).should be_false
49
+ end
50
+
51
+ it 'recursively removes 0' do
52
+ result = Hashshashin.new(sample_hash).recursive_compact
53
+ result[:hash].has_key?(:zero).should be_false
54
+ end
55
+
56
+ it 'removes false' do
57
+ result = Hashshashin.new(sample_hash).recursive_compact
58
+ result.has_key?(:false).should be_false
59
+ end
60
+
61
+ it 'recursively removes false' do
62
+ result = Hashshashin.new(sample_hash).recursive_compact
63
+ result[:hash].has_key?(:false).should be_false
64
+ end
65
+
66
+ it 'removes empty' do
67
+ result = Hashshashin.new(sample_hash).recursive_compact
68
+ result.has_key?(:empty_list).should be_false
69
+ end
70
+
71
+ it 'recursively removes empty' do
72
+ result = Hashshashin.new(sample_hash).recursive_compact
73
+ result[:hash].has_key?(:empty_list).should be_false
74
+ end
75
+
76
+ it 'removes :one' do
77
+ result = Hashshashin.new(sample_hash).recursive_compact { |k,v| k == :one }
78
+ result.has_key?(:one).should be_false
79
+ end
80
+
81
+ it 'recursively removes :one' do
82
+ result = Hashshashin.new(sample_hash).recursive_compact { |k,v| k == :one }
83
+ result[:hash].has_key?(:one).should be_false
84
+ end
85
+
86
+ end
87
+
88
+ describe '#recursive_compact!' do
89
+
90
+ it 'alters self' do
91
+ decorator = Hashshashin.new(sample_hash)
92
+ decorator.recursive_compact!
93
+ decorator.should_not == Hashshashin.new(sample_hash)
94
+ end
95
+
96
+ it 'returns a Hashshashin instance' do
97
+ Hashshashin.new(sample_hash).recursive_compact!.kind_of?(Hashshashin).should be_true
98
+ end
99
+
100
+ it 'removes nil' do
101
+ result = Hashshashin.new(sample_hash).recursive_compact!
102
+ result.has_key?(:nil).should be_false
103
+ end
104
+
105
+ it 'recursively removes nil' do
106
+ result = Hashshashin.new(sample_hash).recursive_compact!
107
+ result[:hash].has_key?(:nil).should be_false
108
+ end
109
+
110
+ it 'removes 0' do
111
+ result = Hashshashin.new(sample_hash).recursive_compact!
112
+ result.has_key?(:zero).should be_false
113
+ end
114
+
115
+ it 'recursively removes 0' do
116
+ result = Hashshashin.new(sample_hash).recursive_compact!
117
+ result[:hash].has_key?(:zero).should be_false
118
+ end
119
+
120
+ it 'removes false' do
121
+ result = Hashshashin.new(sample_hash).recursive_compact!
122
+ result.has_key?(:false).should be_false
123
+ end
124
+
125
+ it 'recursively removes false' do
126
+ result = Hashshashin.new(sample_hash).recursive_compact!
127
+ result[:hash].has_key?(:false).should be_false
128
+ end
129
+
130
+ it 'removes empty' do
131
+ result = Hashshashin.new(sample_hash).recursive_compact!
132
+ result.has_key?(:empty_list).should be_false
133
+ end
134
+
135
+ it 'recursively removes empty' do
136
+ result = Hashshashin.new(sample_hash).recursive_compact!
137
+ result[:hash].has_key?(:empty_list).should be_false
138
+ end
139
+
140
+ it 'removes :one' do
141
+ result = Hashshashin.new(sample_hash).recursive_compact! { |k,v| k == :one }
142
+ result.has_key?(:one).should be_false
143
+ end
144
+
145
+ it 'recursively removes :one' do
146
+ result = Hashshashin.new(sample_hash).recursive_compact! { |k,v| k == :one }
147
+ result[:hash].has_key?(:one).should be_false
148
+ end
149
+
150
+ end
151
+
152
+ describe '#recursive_rfc3339' do
153
+
154
+ it 'does not alter self' do
155
+ Timecop.freeze(DateTime.now) do
156
+ decorator = Hashshashin.new(sample_hash)
157
+ decorator.recursive_rfc3339
158
+ decorator.should == Hashshashin.new(sample_hash)
159
+ end
160
+ end
161
+
162
+ it 'returns a Hashshashin instance' do
163
+ Hashshashin.new(sample_hash).recursive_rfc3339.kind_of?(Hashshashin).should be_true
164
+ end
165
+
166
+ it 'replaces Time values' do
167
+ result = Hashshashin.new(sample_hash).recursive_rfc3339
168
+ result[:Time].should be_kind_of(String)
169
+ result[:Time].should == sample_hash[:Time].to_datetime.rfc3339
170
+ end
171
+
172
+ it 'recursively replaces Time values' do
173
+ result = Hashshashin.new(sample_hash).recursive_rfc3339
174
+ result[:hash][:Time].should be_kind_of(String)
175
+ result[:hash][:Time].should == sample_hash[:Time].to_datetime.rfc3339
176
+ end
177
+
178
+ it 'replaces DateTime values' do
179
+ result = Hashshashin.new(sample_hash).recursive_rfc3339
180
+ result[:DateTime].should be_kind_of(String)
181
+ result[:DateTime].should == sample_hash[:DateTime].to_datetime.rfc3339
182
+ end
183
+
184
+ it 'recursively replaces DateTime values' do
185
+ result = Hashshashin.new(sample_hash).recursive_rfc3339
186
+ result[:hash][:DateTime].should be_kind_of(String)
187
+ result[:hash][:DateTime].should == sample_hash[:DateTime].to_datetime.rfc3339
188
+ end
189
+
190
+ end
191
+
192
+ describe '#recursive_rfc3339!' do
193
+
194
+ it 'alters self' do
195
+ Timecop.freeze(DateTime.now) do
196
+ decorator = Hashshashin.new(sample_hash)
197
+ decorator.recursive_rfc3339!
198
+ decorator.should_not == Hashshashin.new(sample_hash)
199
+ end
200
+ end
201
+
202
+ it 'returns a Hashshashin instance' do
203
+ Hashshashin.new(sample_hash).recursive_rfc3339!.kind_of?(Hashshashin).should be_true
204
+ end
205
+
206
+ it 'replaces Time values' do
207
+ result = Hashshashin.new(sample_hash).recursive_rfc3339!
208
+ result[:Time].should be_kind_of(String)
209
+ result[:Time].should == sample_hash[:Time].to_datetime.rfc3339
210
+ end
211
+
212
+ it 'recursively replaces Time values' do
213
+ result = Hashshashin.new(sample_hash).recursive_rfc3339!
214
+ result[:hash][:Time].should be_kind_of(String)
215
+ result[:hash][:Time].should == sample_hash[:Time].to_datetime.rfc3339
216
+ end
217
+
218
+ it 'replaces DateTime values' do
219
+ Timecop.freeze(DateTime.now) do
220
+ result = Hashshashin.new(sample_hash).recursive_rfc3339!
221
+ result[:DateTime].should be_kind_of(String)
222
+ result[:DateTime].should == sample_hash[:DateTime].to_datetime.rfc3339
223
+ end
224
+ end
225
+
226
+ it 'recursively replaces DateTime values' do
227
+ result = Hashshashin.new(sample_hash).recursive_rfc3339!
228
+ result[:hash][:DateTime].should be_kind_of(String)
229
+ result[:hash][:DateTime].should == sample_hash[:DateTime].to_datetime.rfc3339
230
+ end
231
+
232
+ end
233
+
234
+ describe '#recursive_symbolize_keys' do
235
+
236
+ it 'does not alter self' do
237
+ Timecop.freeze(DateTime.now) do
238
+ decorator = Hashshashin.new(sample_hash)
239
+ decorator.recursive_symbolize_keys
240
+ decorator.should == Hashshashin.new(sample_hash)
241
+ end
242
+ end
243
+
244
+ it 'returns a Hashshashin instance' do
245
+ Hashshashin.new(sample_hash).recursive_symbolize_keys.kind_of?(Hashshashin).should be_true
246
+ end
247
+
248
+ it 'replaces String keys' do
249
+ result = Hashshashin.new(sample_hash).recursive_symbolize_keys
250
+ result.keys.any? { |key| key.kind_of?(String) }.should be_false
251
+
252
+ result[:string_key].should be
253
+ end
254
+
255
+ it 'recursively replaces String keys' do
256
+ result = Hashshashin.new(sample_hash).recursive_symbolize_keys
257
+ result[:hash].keys.any? { |key| key.kind_of?(String) }.should be_false
258
+
259
+ result[:hash][:string_key].should be
260
+ end
261
+
262
+ end
263
+
264
+ describe '#recursive_symbolize_keys!' do
265
+
266
+ it 'alters self' do
267
+ Timecop.freeze(DateTime.now) do
268
+ decorator = Hashshashin.new(sample_hash)
269
+ decorator.recursive_symbolize_keys!
270
+ decorator.should_not == Hashshashin.new(sample_hash)
271
+ end
272
+ end
273
+
274
+ it 'returns a Hashshashin instance' do
275
+ Hashshashin.new(sample_hash).recursive_symbolize_keys!.kind_of?(Hashshashin).should be_true
276
+ end
277
+
278
+ it 'replaces String keys' do
279
+ result = Hashshashin.new(sample_hash).recursive_symbolize_keys!
280
+ result.keys.any? { |key| key.kind_of?(String) }.should be_false
281
+
282
+ result[:string_key].should be
283
+ end
284
+
285
+ it 'recursively replaces String keys' do
286
+ result = Hashshashin.new(sample_hash).recursive_symbolize_keys!
287
+ result[:hash].keys.any? { |key| key.kind_of?(String) }.should be_false
288
+
289
+ result[:hash][:string_key].should be
290
+ end
291
+
292
+ end
293
+
294
+ describe '#recursively_apply' do
295
+ subject do
296
+ Hashshashin.new(sample_hash).recursively_apply do |key, value|
297
+ ["#{key.to_s}_key", value]
298
+ end
299
+ end
300
+
301
+ specify { subject['false_key'].should == false }
302
+ specify { subject['nil_key'].should == nil }
303
+ specify { subject['zero_key'].should == 0 }
304
+ specify { subject['empty_string_key'].should == '' }
305
+ specify { subject['empty_list_key'].should == [] }
306
+ specify { subject['empty_hash_key'].should == {} }
307
+ specify { subject['one_key'].should == 1 }
308
+ specify { subject['a_key'].should == 'a' }
309
+ specify { subject['Time_key'].should be }
310
+ specify { subject['DateTime_key'].should be }
311
+ specify { subject['string_key_key'].should == 'value' }
312
+ specify { subject['camelize_me_key'].should == :camelize_me }
313
+
314
+ specify { subject['hash_key']['false_key'].should == false }
315
+ specify { subject['hash_key']['nil_key'].should == nil }
316
+ specify { subject['hash_key']['zero_key'].should == 0 }
317
+ specify { subject['hash_key']['empty_string_key'].should == '' }
318
+ specify { subject['hash_key']['empty_list_key'].should == [] }
319
+ specify { subject['hash_key']['empty_hash_key'].should == {} }
320
+ specify { subject['hash_key']['one_key'].should == 1 }
321
+ specify { subject['hash_key']['a_key'].should == 'a' }
322
+ specify { subject['hash_key']['Time_key'].should be }
323
+ specify { subject['hash_key']['DateTime_key'].should be }
324
+ specify { subject['hash_key']['string_key_key'].should == 'value' }
325
+ specify { subject['hash_key']['camelize_me_key'].should == :camelize_me }
326
+ end
327
+
328
+ def sample_hash
329
+ {
330
+ hash: {
331
+ false: false,
332
+ nil: nil,
333
+ zero: 0,
334
+ empty_string: '',
335
+ empty_list: [],
336
+ empty_hash: {},
337
+ one: 1,
338
+ a: 'a',
339
+ Time: Time.now,
340
+ DateTime: DateTime.now,
341
+ 'string_key' => 'value',
342
+ 'camelize_me' => 'camelize_me',
343
+ :camelize_me => :camelize_me
344
+ },
345
+ false: false,
346
+ nil: nil,
347
+ zero: 0,
348
+ empty_string: '',
349
+ empty_list: [],
350
+ empty_hash: {},
351
+ one: 1,
352
+ a: 'a',
353
+ Time: Time.now,
354
+ DateTime: DateTime.now,
355
+ 'string_key' => 'value',
356
+ 'camelize_me' => 'camelize_me',
357
+ :camelize_me => :camelize_me
358
+ }
359
+ end
360
+
361
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'timecop'
3
+ require File.expand_path('../../lib/hashshashin', __FILE__)
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashshashin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Aaron Oman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70164491435080 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70164491435080
25
+ - !ruby/object:Gem::Dependency
26
+ name: shoulda
27
+ requirement: &70164491434580 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70164491434580
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70164491434040 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70164491434040
47
+ - !ruby/object:Gem::Dependency
48
+ name: timecop
49
+ requirement: &70164491433360 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70164491433360
58
+ description: Convenient recursive methods to extend Ruby's built-in Hash.
59
+ email:
60
+ - aaron@unbounce.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rvmrc
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - hashshashin.gemspec
72
+ - lib/hashshashin.rb
73
+ - lib/hashshashin/version.rb
74
+ - spec/lib/hashshashin_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: ''
77
+ licenses: []
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ segments:
89
+ - 0
90
+ hash: -850569894321753333
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ segments:
98
+ - 0
99
+ hash: -850569894321753333
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.15
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Destructive and non-destructive recursive methods to augment the build in
106
+ Hash.
107
+ test_files:
108
+ - spec/lib/hashshashin_spec.rb
109
+ - spec/spec_helper.rb