hashshashin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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