choc_mool 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b51016959207b8119d08dd726ff8286f519b8e1
4
+ data.tar.gz: 803774c6a55941e08260af5a2708182a0176a5bb
5
+ SHA512:
6
+ metadata.gz: 2b249fdc25bacde3927a17718aa720710287c788768636e168511ce0e41d4d4d533830a7344a6592453e19e31f24bfedad20c43467d6f6b6e43bfc5fd1acd9f2
7
+ data.tar.gz: 7ac0db273e306aa1b10af80fa8504d40f2c70280a4cb9040175043e227ca0abcde332a5f4d8d70974e952803212b3d77f07473a4ecdcc92de56adc789f04e989
@@ -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/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - 1.9.2
6
+ - rbx-19mode
7
+ script: 'rake'
8
+ branches:
9
+ only:
10
+ - master
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in choc_mool.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mark G
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.
@@ -0,0 +1,91 @@
1
+ # ChocMool
2
+
3
+ Wraps a hash, allowing easy access to deeply nested values.
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem 'choc_mool'
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ hash = {one: {two: {three: :four}}}
15
+ data = ChocMool.new(hash)
16
+
17
+ data.fetch(:one, :two, :three)
18
+ # => :four
19
+ ```
20
+
21
+ #### using a regex
22
+
23
+ ```ruby
24
+ hash = {one: 'two, three'}
25
+ data = ChocMool.new(hash)
26
+
27
+ data.using(/^(.*),/).fetch(:one)
28
+ # => 'two'
29
+ ```
30
+
31
+ #### using dot notation
32
+
33
+ ```ruby
34
+ hash = {one: {two: {three: :four}}}
35
+ data = ChocMool.new(hash)
36
+
37
+ data.fetch('one.two.three')
38
+ # => :four
39
+
40
+ data.fetch(:one, 'two.three')
41
+ # => :four
42
+ ```
43
+
44
+ #### accessing attributes
45
+
46
+ A common use for ChocMool is to easily access deeply nested data returned from APIs.
47
+ The API data is converted to hash from XML or JSON then wrapped by ChocMool.
48
+
49
+ XML cannot be represented directly by a hash, thus libraries that convert
50
+ the XML -> hash can expose this data.
51
+
52
+ ChocMool supports accessing these values when they are accessed via
53
+ `#attributes` on the corresponding key (as provided by [Nori](https://github.com/savonrb/nori)).
54
+
55
+ ```ruby
56
+ xml = "<one two='three'>four</one>"
57
+ hash = Nori.new.parse(xml)
58
+ data = ChocMool.new(hash)
59
+
60
+ data.fetch(:one, '@two')
61
+ # => 'three'
62
+
63
+ data.fetch(:one)
64
+ # => 'four'
65
+ ```
66
+
67
+ #### accessing arrays
68
+
69
+ ```ruby
70
+ hash = {one: [:two, :three]}
71
+ data = ChocMool.new(hash)
72
+
73
+ data.fetch(:one, 1)
74
+ # => :three
75
+
76
+ data.fetch_each(:one) { |value| puts value }
77
+ # => :two
78
+ # => :three
79
+
80
+ data.fetch_each_with_index(:one) { |value, index| puts "#{index}: #{value}" }
81
+ # => 0: :two
82
+ # => 1: :three
83
+ ```
84
+
85
+ ## Contributing
86
+
87
+ 1. Fork it
88
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
89
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
90
+ 4. Push to the branch (`git push origin my-new-feature`)
91
+ 5. Create new Pull Request
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.pattern = 'spec/**/*_spec.rb'
9
+ spec.rspec_opts = ["--color"]
10
+ end
11
+
12
+ task default: :spec
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'choc_mool/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'choc_mool'
7
+ spec.version = ChocMool::VERSION
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.authors = ['Mark G']
10
+ spec.email = ['rtec88@gmail.com']
11
+ spec.description = 'Deep dive hash accessor'
12
+ spec.summary = spec.description
13
+ spec.homepage = 'http://github.com/attack/choc_mool'
14
+ spec.license = 'MIT'
15
+
16
+ spec.required_ruby_version = '>= 1.9.2'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.test_files = spec.files.grep(%r{^spec/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.3'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec', '>= 2.11'
25
+ spec.add_development_dependency 'pry'
26
+ end
@@ -0,0 +1,108 @@
1
+ require_relative 'choc_mool/version'
2
+
3
+ class ChocMool
4
+ attr_reader :hash, :regex
5
+
6
+ def initialize(hash)
7
+ @hash = hash
8
+ end
9
+
10
+ def using(regex)
11
+ @regex = regex
12
+ self
13
+ end
14
+
15
+ def fetch(*paths)
16
+ paths = _expand(paths)
17
+
18
+ if hash
19
+ result = fetch_value_or_attribute(paths)
20
+ else
21
+ result = nil
22
+ end
23
+
24
+ result = _apply_regex(result)
25
+ result = _cleanup(result)
26
+
27
+ result
28
+ end
29
+
30
+ def each(*paths, &block)
31
+ path = fetch(*paths)
32
+ if path
33
+ path.each do |result|
34
+ result_payload = ChocMool.new(result)
35
+ block.call(result_payload)
36
+ end
37
+ end
38
+ end
39
+
40
+ def each_with_index(*paths, &block)
41
+ fetch(*paths).each_with_index do |result, index|
42
+ result_payload = ChocMool.new(result)
43
+ block.call(result_payload, index)
44
+ end
45
+ end
46
+
47
+ def fetch_each(*paths, &block)
48
+ each(*paths, &block)
49
+ end
50
+
51
+ def fetch_each_with_index(*paths, &block)
52
+ each_with_index(*paths, &block)
53
+ end
54
+
55
+ private
56
+
57
+ def _expand(paths)
58
+ paths.map do |path|
59
+ if path.respond_to?(:split)
60
+ path.split('.')
61
+ else
62
+ path
63
+ end
64
+ end.flatten
65
+ end
66
+
67
+ def fetch_value_or_attribute(paths)
68
+ result = paths.inject(hash) do |result, path|
69
+ fetch_value(result, path) || fetch_attribute(result, path) || break
70
+ end
71
+ end
72
+
73
+ def fetch_value(result, path)
74
+ if result.respond_to? :fetch
75
+ fetch_native(result, path) || fetch_symbol(result, path) || fetch_string(result, path)
76
+ end
77
+ end
78
+
79
+ def fetch_native(result, path)
80
+ result.fetch(path, nil)
81
+ end
82
+
83
+ def fetch_symbol(result, path)
84
+ result.fetch(path.to_sym, nil) if path.respond_to?(:to_sym)
85
+ end
86
+
87
+ def fetch_string(result, path)
88
+ result.fetch(path.to_s, nil) if path.respond_to?(:to_s)
89
+ end
90
+
91
+ def fetch_attribute(result, path)
92
+ if path.to_s.start_with?('@') && result.respond_to?(:attributes)
93
+ result.attributes.fetch(path.slice(1..-1))
94
+ end
95
+ end
96
+
97
+ def _apply_regex(result)
98
+ if @regex && @regex.is_a?(Regexp) && matched = result.to_s.match(@regex)
99
+ result = matched[1] if matched[1]
100
+ end
101
+ @regex = nil
102
+ result
103
+ end
104
+
105
+ def _cleanup(result)
106
+ result.respond_to?(:strip) ? result.strip : result
107
+ end
108
+ end
@@ -0,0 +1,3 @@
1
+ class ChocMool
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ describe ChocMool do
4
+ describe '#fetch' do
5
+ it 'returns the value for the key provided' do
6
+ hash = {one: 1}
7
+ choc_mool = ChocMool.new(hash)
8
+ expect( choc_mool.fetch(:one) ).to eq 1
9
+ end
10
+
11
+ it 'returns nil when the key cannot be found' do
12
+ hash = {}
13
+ choc_mool = ChocMool.new(hash)
14
+ expect( choc_mool.fetch(:one) ).to be_nil
15
+ end
16
+
17
+ it 'traverses multiple levels to get the value' do
18
+ hash = {one: {two: {three: 3}}}
19
+ choc_mool = ChocMool.new(hash)
20
+ expect( choc_mool.fetch(:one, :two, :three) ).to eq 3
21
+ end
22
+
23
+ it 'returns nil when any level cannot be found' do
24
+ hash = {one: {two: {three: {four: 4}}}}
25
+ choc_mool = ChocMool.new(hash)
26
+ expect( choc_mool.fetch(:one, :too, :three) ).to be_nil
27
+ end
28
+
29
+ it 'returns nil when the starting value is nil' do
30
+ hash = nil
31
+ choc_mool = ChocMool.new(hash)
32
+ expect( choc_mool.fetch(:one) ).to be_nil
33
+ end
34
+
35
+ it 'returns a stripped result' do
36
+ hash = {one: ' one '}
37
+ choc_mool = ChocMool.new(hash)
38
+ expect( choc_mool.fetch(:one) ).to eq 'one'
39
+ end
40
+
41
+ it "returns the value for the key provided, even when using string keys" do
42
+ hash = {:one => 1}
43
+ choc_mool = ChocMool.new(hash)
44
+ expect( choc_mool.fetch('one') ).to eq 1
45
+ end
46
+
47
+ it "returns the value for the key provided, even when using symbol keys" do
48
+ hash = {'one' => 1}
49
+ choc_mool = ChocMool.new(hash)
50
+ expect( choc_mool.fetch(:one) ).to eq 1
51
+ end
52
+
53
+ it "traverses multiple levels using dot syntax" do
54
+ hash = {:one => {:two => {:three => 3}}}
55
+ choc_mool = ChocMool.new(hash)
56
+ expect( choc_mool.fetch('one.two.three') ).to eq 3
57
+ end
58
+
59
+ it "traverses multiple levels using mixed dot syntax" do
60
+ hash = {:one => {:two => {:three => 3}}}
61
+ choc_mool = ChocMool.new(hash)
62
+ expect( choc_mool.fetch(:one, 'two.three') ).to eq 3
63
+ end
64
+
65
+ context 'when the data is accessed via an attribute' do
66
+ it 'returns the value of the key' do
67
+ hash = {one: {'@two' => 2}}
68
+ choc_mool = ChocMool.new(hash)
69
+ expect( choc_mool.fetch(:one, '@two') ).to eq 2
70
+ end
71
+
72
+ it 'returns the value accessed via :attributes' do
73
+ class StringWithAttributes < String
74
+ attr_accessor :attributes
75
+ end
76
+ value_with_attributes = StringWithAttributes.new('one')
77
+ value_with_attributes.attributes = {'two' => 2}
78
+ hash = {one: value_with_attributes}
79
+ choc_mool = ChocMool.new(hash)
80
+ expect( choc_mool.fetch(:one, '@two') ).to eq 2
81
+ end
82
+ end
83
+
84
+ context 'when the data is accessed via an index' do
85
+ it 'returns the value at that index' do
86
+ hash = {one: [2]}
87
+ choc_mool = ChocMool.new(hash)
88
+ expect( choc_mool.fetch(:one, 0) ).to eq 2
89
+ end
90
+ end
91
+ end
92
+
93
+ describe '#fetch_each' do
94
+ it 'returns an instance for each result found at the key' do
95
+ hash = {one: [{two: 2}, {two: 2}]}
96
+ choc_mool = ChocMool.new(hash)
97
+
98
+ expect { |b|
99
+ choc_mool.fetch_each(:one, &b)
100
+ }.to yield_successive_args(ChocMool, ChocMool)
101
+ end
102
+
103
+ it 'returns an instance for each result found at the key' do
104
+ hash = {one: [{two: 2}, {two: 2}]}
105
+ choc_mool = ChocMool.new(hash)
106
+
107
+ choc_mool.fetch_each(:one) do |value|
108
+ expect( value.fetch(:two) ).to eq 2
109
+ end
110
+ end
111
+
112
+ it 'raises an error when result is not an array' do
113
+ hash = {one: 1}
114
+ choc_mool = ChocMool.new(hash)
115
+
116
+ expect {
117
+ choc_mool.fetch_each(:one){ |p| nil }
118
+ }.to raise_error(NoMethodError)
119
+ end
120
+ end
121
+
122
+ describe '#fetch_each_with_index' do
123
+ it 'returns an instance for each result found at the key' do
124
+ hash = {one: [{two: 2}, {two: 2}]}
125
+ choc_mool = ChocMool.new(hash)
126
+
127
+ i = 0
128
+ choc_mool.fetch_each_with_index(:one) do |value, index|
129
+ expect( value.fetch(:two) ).to eq 2
130
+ expect( index ).to eq i
131
+ i += 1
132
+ end
133
+ end
134
+
135
+ it 'raises an error when result is not an array' do
136
+ hash = {one: 1}
137
+ choc_mool = ChocMool.new(hash)
138
+
139
+ expect {
140
+ choc_mool.fetch_each_with_index(:one){ |p| nil }
141
+ }.to raise_error(NoMethodError)
142
+ end
143
+ end
144
+
145
+ describe '#using' do
146
+ it 'applies the regex to the fetched result' do
147
+ hash = {one: 'two, three'}
148
+ choc_mool = ChocMool.new(hash)
149
+ expect( choc_mool.using(/^(.*),/).fetch(:one) ).to eq 'two'
150
+ end
151
+
152
+ it 'does nothing if valid regex does not exist' do
153
+ hash = {one: 'two, three'}
154
+ choc_mool = ChocMool.new(hash)
155
+ expect( choc_mool.using(:invalid_regex).fetch(:one) ).to eq 'two, three'
156
+ end
157
+
158
+ it 'does nothing if regex does not capture proper result' do
159
+ hash = {one: 'two, three'}
160
+ choc_mool = ChocMool.new(hash)
161
+ expect( choc_mool.using(/^.*,.*$/).fetch(:one) ).to eq 'two, three'
162
+ end
163
+
164
+ it 'forgets the regex' do
165
+ hash = {one: 'two, three'}
166
+ choc_mool = ChocMool.new(hash)
167
+ expect( choc_mool.using(/^(.*),/).fetch(:one) ).to eq 'two'
168
+ expect( choc_mool.fetch(:one) ).to eq 'two, three'
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,8 @@
1
+ require 'rspec'
2
+ require 'pry'
3
+
4
+ require_relative '../lib/choc_mool'
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: choc_mool
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mark G
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-06 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '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: '2.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '2.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
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
+ description: Deep dive hash accessor
70
+ email:
71
+ - rtec88@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - .travis.yml
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - choc_mool.gemspec
84
+ - lib/choc_mool.rb
85
+ - lib/choc_mool/version.rb
86
+ - spec/choc_mool_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: http://github.com/attack/choc_mool
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: 1.9.2
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.0.3
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Deep dive hash accessor
112
+ test_files:
113
+ - spec/choc_mool_spec.rb
114
+ - spec/spec_helper.rb
115
+ has_rdoc: