aha 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb3f97c109690791bca8f2838ac07cc21aa40e0a
4
+ data.tar.gz: 14ade28b15cff210f3b416db650ec9128d9e381e
5
+ SHA512:
6
+ metadata.gz: 32234582b85e179aba8baa33540a57e66fe8f101bad71eebacb352319db29b0b59a28fe6b426755b52557426ed96cd4d11d052bd9eac1e08e80e22ef0634478e
7
+ data.tar.gz: 513d4cb0a36dc4d93e04a985b093ef02cbcd85365aee380923cfb9c476f3107f6cee89d76fe7358bae43f5ac67f66d1e239055f1d08f449bcddd4314d03fbd2a
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Mike Jackson
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,31 @@
1
+ # Aha
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'aha'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install aha
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/aha/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
@@ -0,0 +1,186 @@
1
+ require 'aha/helper'
2
+
3
+ module Aha
4
+ # Public: Provides functionality for dealing with nested hashes.
5
+ #
6
+ # Nested keys are represented by an array where the first element represents the
7
+ # the key to access the sub hash and the rest represent keys for that hash.
8
+ #
9
+ # These arrays can be nested allowing us to represent keys for an arbitrarily
10
+ # nested hash. This way of representing keys is used throughout Aha and is
11
+ # heavily inspired by the Prismatic's Clojure fnk style destructuring as seen
12
+ # here: https://github.com/Prismatic/plumbing/tree/master/src/plumbing/fnk
13
+ #
14
+ # For example:
15
+ #
16
+ # Given the hash {a: 'hello', b: {c: 'augmented', d: {e: 'hash'}}}
17
+ # the keys :a, [:b, :c, [:d, :e]] would correspond to the values
18
+ # 'hello', 'augmented', 'hash'
19
+ module AugmentedHash
20
+ extend self
21
+
22
+ # Public: Similar to Hash#values_at except that it supports nested key
23
+ # syntax and is primarily intented to be used for destructuring assignment.
24
+ #
25
+ # hash - The nested hash to extract values from.
26
+ # keys - They keys corresponding to the values to be extracted. Supports
27
+ # nested key syntax.
28
+ #
29
+ # Examples:
30
+ #
31
+ # h = {'a' => 3, :b => {:c => 2, 'd' => {:e => 1}}}
32
+ # value_1, value_2, value_3 = vals_at h, 'a', [:b, :c, ['d', :e]]
33
+ #
34
+ # value_1
35
+ # # => 3
36
+ # value_2
37
+ # # => 2
38
+ # value_3
39
+ # # => 1
40
+ #
41
+ # Returns an array of values corresponding to the given keys.
42
+ def vals_at(hash, *keys)
43
+ result = Aha::Helper::extract_option(:acc, keys) || []
44
+
45
+ result.tap do |acc|
46
+ keys.each do |k|
47
+ if k.is_a? Array
48
+ subkey, *keys = k
49
+ vals_at(hash[subkey], *keys, {acc: acc})
50
+ else
51
+ acc << hash[k]
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # Public: Retrieves a value from a nested hash. Equivalent to
58
+ # hash[k1][..][kn]
59
+ #
60
+ # hash - The hash to retrieve the value from.
61
+ # keys - The keys to the value in order of depth.
62
+ #
63
+ # Examples:
64
+ #
65
+ # h = {:a => {'b' => {:c => 'value'}}}
66
+ # get_in h, :a, 'b', :c
67
+ # # => 'value'
68
+ #
69
+ # Returns the value if it exists and nil otherwise
70
+ def get_in(hash, *keys)
71
+ keys.reduce(hash) { |acc, k| acc[k] }
72
+ end
73
+
74
+ # Public: Sets a value within a nested hash. Equivalent to
75
+ # hash[k1][..][kn] = val
76
+ #
77
+ # hash - The nested hash to set the value in.
78
+ # keys - The keys to the value to set in order of depth.
79
+ # val - The new value to set.
80
+ #
81
+ # Examples:
82
+ #
83
+ # h = {:a => {'b' => {:c => 'value'}}}
84
+ # put_in! h, :a, 'b', :c, 'new_value'
85
+ # h
86
+ # # => {:a => {'b' => {:c => 'new_value'}}}
87
+ #
88
+ # Returns nothing.
89
+ def put_in!(hash, *keys, val)
90
+ last_key = keys.pop
91
+ get_in(hash, *keys)[last_key] = val
92
+ end
93
+
94
+ # Public: Updates a value within a nested hash.
95
+ #
96
+ # hash - The nested hash to update the value in.
97
+ # keys - The keys to the current value in order of depth
98
+ # block - A required block that is given the current value and should return
99
+ # the updated value to be set.
100
+ #
101
+ # Examples:
102
+ #
103
+ # h = {:a => {'b' => {:c => 33}}}
104
+ # update_in!(h, :a, 'b', :c) { |v| v + 9}
105
+ # h
106
+ # # => {:a => {'b' => {:c => 42}}}
107
+ #
108
+ # Returns nothing.
109
+ def update_in!(hash, *keys)
110
+ put_in!(hash, *keys, yield(get_in(hash, *keys))) if block_given?
111
+ end
112
+
113
+ # Public: Deletes a value within a nested hash. Equivalent to
114
+ # hash[k1][..][kn-1].delete(kn)
115
+ #
116
+ # hash - The nested hash to delete the value in.
117
+ # keys - They keys to the value to delete in order of depth.
118
+ #
119
+ # Examples:
120
+ #
121
+ # h = {:a => {'b' => {:c => 'hello', :d => 'world'}}}
122
+ # delete_in! h, :a, 'b', :c
123
+ # h
124
+ # # => {:a => {'b' => {:d => 'world'}}}
125
+ #
126
+ # Returns nothing.
127
+ def delete_in!(hash, *keys)
128
+ last_key = keys.pop
129
+ get_in(hash, *keys).delete last_key
130
+ end
131
+
132
+ # Public: Removes the excluded keys from the hash.
133
+ #
134
+ # hash - The hash to exclude keys from.
135
+ # keys - The keys to be excluded from the hash. Supports nested key syntax.
136
+ #
137
+ # Examples:
138
+ #
139
+ # h = {:a => {'b' => {:c => 'hello', :d => 'augmented'}}, :e => 'hash'}
140
+ # exclude! h, [:a, ['b', :d]], :e
141
+ # h
142
+ # # => {:a => {'b' => {:c => 'hello'}}}
143
+ #
144
+ # Returns nothing.
145
+ def exclude!(hash, *keys)
146
+ keys.each do |k|
147
+ if k.is_a? Array
148
+ subkey, *keys = k
149
+ exclude!(hash[subkey], *keys)
150
+ else
151
+ hash.delete k
152
+ end
153
+ end
154
+ end
155
+
156
+ # Public: Creates a new hash consisting only keys given and their
157
+ # corresponding values.
158
+ #
159
+ # hash - The hash to base the result on.
160
+ # keys - The keys that the new hash will contain. Supports nested key
161
+ # syntax.
162
+ #
163
+ # Examples:
164
+ #
165
+ # h = {:a => {'b' => {:c => 'hello', :d => 'augmented'}}, :e => 'hash'}
166
+ # only h, [:a, ['b', :d]]
167
+ # # => {:a => {'b' => {:d => 'augmented'}}}
168
+ #
169
+ # Returns the new, smaller hash.
170
+ def only(hash, *keys)
171
+ result = {}
172
+
173
+ result.tap do |r|
174
+ keys.each do |k|
175
+ if k.is_a? Array
176
+ subkey, *keys = k
177
+ r[subkey] = only(hash[subkey], *keys)
178
+ else
179
+ r[k] = hash[k]
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ end
186
+ end
data/lib/aha/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Aha
2
+ # Internal: Provides helper methods for the rest of the Aha library.
3
+ module Helper
4
+ extend self
5
+
6
+ # Internal: Extracts an option from the options hash if it exists in the method args.
7
+ #
8
+ # option_key - A symbol corresponding to a key in the options hash.
9
+ # args - An array containing a number of args followed by an optional options hash.
10
+ #
11
+ # Examples:
12
+ #
13
+ # args_with_options = [:a, :b, :c, :d, {:key => :option}]
14
+ # args_without_options = [:a, :b, :c, :d]
15
+ #
16
+ # extract_option :key, args_with_options
17
+ # # => :option
18
+ #
19
+ # extract_option :key, args_without_options
20
+ # # => nil
21
+ #
22
+ # Returns the option value if it exists and nil otherwise.
23
+ def extract_option(option_key, args)
24
+ options = args.last.is_a?(Hash) ? args.pop : {}
25
+ options[option_key]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Aha
2
+ VERSION = "1.0.0"
3
+ end
data/lib/aha.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'aha/version'
2
+ require 'aha/augmented_hash'
3
+
4
+ # Extends Hash with functionality from Aha::AugmentedHash.
5
+ #
6
+ # Methods are implemented by delegating to AugmentedHash on an individual method
7
+ # basis. Using module methods as opposed to a mixing in a module is preferrable
8
+ # as it allows us to include functionality in a piecemeal manner if necessary
9
+ # and supports minimal changes to the core Hash class.
10
+ #
11
+ # For more information see Aha::AugmentedHash.
12
+ class Hash
13
+ def vals_at(*keys)
14
+ Aha::AugmentedHash::vals_at(self, *keys)
15
+ end
16
+
17
+ def get_in(*keys)
18
+ Aha::AugmentedHash::get_in(self, *keys)
19
+ end
20
+
21
+ def put_in!(*args)
22
+ Aha::AugmentedHash::put_in!(self, *args)
23
+ end
24
+
25
+ def update_in!(*keys, &block)
26
+ Aha::AugmentedHash::update_in!(self, *keys, &block)
27
+ end
28
+
29
+ def delete_in!(*keys)
30
+ Aha::AugmentedHash::delete_in!(self, *keys)
31
+ end
32
+
33
+ def exclude!(*keys)
34
+ Aha::AugmentedHash::exclude!(self, *keys)
35
+ end
36
+
37
+ def only(*keys)
38
+ Aha::AugmentedHash::only(self, *keys)
39
+ end
40
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'aha'
3
+
4
+ describe Hash do
5
+ before(:each) do
6
+ @data = {:name => 'Oculus VR',
7
+ :founders => ['Palmer Luckey', 'Brendan Iribe', 'Michael Antonov', 'Nate Mitchell', 'Jack McCauley'],
8
+ :ceo => {'name' => 'Brendan Iribe'},
9
+ 'cto' => {:name => 'John Carmack',
10
+ 'age' => 44,
11
+ :spouse => {:name => 'Katherine Anna Kang'}}}
12
+ end
13
+
14
+ it 'should destructure nested hashes' do
15
+ company_name, ceo_name, cto_name, cto_age, cto_spouse_name =
16
+ @data.vals_at(:name, [:ceo, 'name'], ['cto', :name, 'age', [:spouse, :name]])
17
+
18
+ company_name.should == 'Oculus VR'
19
+ ceo_name.should == 'Brendan Iribe'
20
+ cto_name.should == 'John Carmack'
21
+ cto_age.should == 44
22
+ cto_spouse_name.should == 'Katherine Anna Kang'
23
+ end
24
+
25
+ it 'should retrieve nested values' do
26
+ @data.get_in('cto', :spouse, :name).should == 'Katherine Anna Kang'
27
+ end
28
+
29
+ it 'should set nested values' do
30
+ @data.put_in! 'cto', :name, 'Alan Kay'
31
+ @data['cto'][:name].should == 'Alan Kay'
32
+ end
33
+
34
+ it 'should updated nested values' do
35
+ @data.update_in!('cto', 'age') { |age| age + 5 }
36
+ @data['cto']['age'].should == 49
37
+ end
38
+
39
+ it 'should delete nested values' do
40
+ @data.delete_in! 'cto', :spouse
41
+ @data['cto'][:spouse].should be_nil
42
+ end
43
+
44
+ it 'should exclude keys in nested hashes' do
45
+ @data.exclude! :founders, :ceo, ['cto', 'age', :spouse]
46
+ @data.should == {:name => 'Oculus VR', 'cto' => {:name => 'John Carmack'}}
47
+ end
48
+
49
+ it 'should return a new hash from given keys' do
50
+ @data.only(:name, ['cto', [:spouse, :name]]). should ==
51
+ {:name => 'Oculus VR',
52
+ 'cto' => {:spouse => {:name => 'Katherine Anna Kang'}}}
53
+ end
54
+ end
@@ -0,0 +1,10 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |expectations|
3
+ expectations.syntax = [:should, :expect]
4
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
5
+ end
6
+
7
+ config.mock_with :rspec do |mocks|
8
+ mocks.verify_partial_doubles = true
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aha
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Jackson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email: occamin@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - MIT-LICENSE
90
+ - README.md
91
+ - lib/aha.rb
92
+ - lib/aha/augmented_hash.rb
93
+ - lib/aha/helper.rb
94
+ - lib/aha/version.rb
95
+ - spec/lib/aha_spec.rb
96
+ - spec/spec_helper.rb
97
+ homepage: https://rubygems.org/gems/aha
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 1.9.3
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.2.2
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: A library of Hash extensions to simplify working with nested data.
121
+ test_files: []