aha 1.0.0

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