hash_gleaner 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 793b3cd699bd9aaf6752dbf535fbafebe06d8271
4
+ data.tar.gz: b65f1d6ad827696689dee4a8340bb18b59e402c3
5
+ SHA512:
6
+ metadata.gz: 8f531a2c6f8f5ed0989fd59482cf82f824dacb69805ff72fdab81e56405c18c59e939200ce7866c151340620465ab977727585c5b957f451caf8113f59f7de68
7
+ data.tar.gz: 7a1e93023da03433a8488b69a23c99c3f234369656596c067055d2d14209f2eafbfaddf30b8b24c9e9d349898d7a9d12b19976c3b2271952786cec2d95bfd0c7
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hash_gleaner (0.1.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rspec (3.0.0)
11
+ rspec-core (~> 3.0.0)
12
+ rspec-expectations (~> 3.0.0)
13
+ rspec-mocks (~> 3.0.0)
14
+ rspec-core (3.0.2)
15
+ rspec-support (~> 3.0.0)
16
+ rspec-expectations (3.0.2)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.0.0)
19
+ rspec-mocks (3.0.2)
20
+ rspec-support (~> 3.0.0)
21
+ rspec-support (3.0.2)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ hash_gleaner!
28
+ rspec
@@ -0,0 +1,96 @@
1
+ # HashGleaner
2
+ ## Summary
3
+ `HashGleaner` allows you to declare hash structure you want to get like Strong Parameters.
4
+
5
+ ## Example
6
+
7
+ ```ruby
8
+ require 'hash_gleaner'
9
+
10
+ h = {
11
+ :account => {
12
+ :id => 1234,
13
+ :email => 'xxx@xxx.xx',
14
+ :name => 'John Doe',
15
+ :phone => [
16
+ {
17
+ :id => 2345,
18
+ :type => 'mobile',
19
+ :number => 'xxxxxxxxxx',
20
+ },
21
+ {
22
+ :id => 2346,
23
+ :type => 'office',
24
+ :number => 'yyyyyyyyyy',
25
+ },
26
+ ],
27
+ },
28
+ }
29
+
30
+ params = HashGleaner.apply(h) do
31
+ o :account do
32
+ o :name
33
+ o :phone do
34
+ o :type
35
+ o :number
36
+ end
37
+ end
38
+ end
39
+ #=> {:account=>{:name=>"John Doe", :phone=>[{:type=>"mobile", :number=>"xxxxxxxxxx"}, {:type=>"office", :number=>"yyyyyyyyyy"}]}}
40
+ ```
41
+
42
+ When `Hash` object includes `HashGleaner` module, you can use `glean` method.
43
+
44
+ ```ruby
45
+ class << h; include HashGleaner; end
46
+
47
+ params = h.glean do
48
+ o :account do
49
+ o :name
50
+ o :phone do
51
+ o :type
52
+ o :number
53
+ end
54
+ end
55
+ end
56
+ ```
57
+
58
+ hash structure can be described as `Proc` object.
59
+
60
+ ```ruby
61
+ proc = Proc.new{
62
+ o :account do
63
+ o :name
64
+ o :phone do
65
+ o :type
66
+ o :number
67
+ end
68
+ end
69
+ }
70
+
71
+ params = h.glean(proc)
72
+ params = HashGleaner.apply(h, proc)
73
+ ```
74
+
75
+ You can describe `required` or `optional` in hash structure.
76
+ When the hash object misses any required keys,
77
+ `HashGleaner` raises `MissingKeysException`.
78
+
79
+ ```ruby
80
+ proc = Proc.new{
81
+ (required)
82
+ o :account do
83
+ o :name
84
+ o :phone do
85
+ (required)
86
+ o :number
87
+ o :name
88
+ end
89
+ end
90
+ (optional)
91
+ o :admin
92
+ }
93
+
94
+ params = h.glean(proc)
95
+ #=> HashGleaner::MissingKeyException: Missing required keys [:name]
96
+ ```
@@ -0,0 +1,7 @@
1
+ require "bundler"
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task default: :spec
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "hash_gleaner"
6
+ s.authors = ["hosim"]
7
+ s.email = ["github.hosim@gmail.com"]
8
+ s.description = "HashGleaner allows you to declare hash structure you want to get."
9
+ s.summary = "HashGleaner allows you to declare hash structure you want to get."
10
+ s.homepage = "https://github.com/hosim/hash_gleaner"
11
+ s.executables = `git ls-files -- bin/*`.split("\n").map {|f| File.basename(f) }
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.require_paths = ["lib"]
15
+ s.version = "0.1.0"
16
+ s.license = "MIT"
17
+
18
+ s.add_development_dependency 'rspec'
19
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ require 'hash_gleaner/list'
3
+ require 'hash_gleaner/missing_keys'
4
+
5
+ module HashGleaner
6
+ class << self
7
+ def included(base)
8
+ base.__send__ :include, InstanceMethods
9
+ end
10
+
11
+ def apply(hash, proc=nil, &block)
12
+ missing_keys = MissingKeys.new
13
+ action = (block if block_given?) || (proc if proc.is_a? Proc)
14
+ h = List.new(hash, missing_keys).instance_eval(&action) if action
15
+ raise MissingKeysException.new(missing_keys) if missing_keys.has_keys?
16
+ h
17
+ end
18
+ end
19
+
20
+ module InstanceMethods
21
+ def glean(proc=nil, &block)
22
+ HashGleaner.apply(self, proc, &block)
23
+ end
24
+ end
25
+
26
+ class MissingKeysException < StandardError
27
+ def initialize(missing_keys)
28
+ msg = "Missing required keys #{missing_keys.keys.uniq}"
29
+ super(msg)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ require 'hash_gleaner/value_gleaner'
3
+ require 'hash_gleaner/missing_keys'
4
+
5
+ module HashGleaner
6
+ class List
7
+ def initialize(hash, missing_keys)
8
+ @origin = hash
9
+ @hash = {}
10
+ @mode = :optional
11
+ @missing_keys = missing_keys
12
+ @gleaner = ValueGleaner.new(missing_keys)
13
+ end
14
+
15
+ def o(name, *options, &block)
16
+ unless @origin.has_key?(name)
17
+ @missing_keys.add(name) if @mode == :required
18
+ return @hash
19
+ end
20
+
21
+ @hash[name] = @gleaner.glean(@origin[name], block, {})
22
+ @hash
23
+ end
24
+
25
+ def required
26
+ @mode = :required
27
+ @hash
28
+ end
29
+
30
+ def optional
31
+ @mode = :optional
32
+ @hash
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ module HashGleaner
4
+ class MissingKeys
5
+ def initialize
6
+ @keys = []
7
+ end
8
+
9
+ def add(key)
10
+ keys << key
11
+ end
12
+
13
+ def has_keys?
14
+ ! keys.empty?
15
+ end
16
+
17
+ def clear
18
+ @keys = []
19
+ end
20
+
21
+ attr_reader :keys
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ module HashGleaner
4
+ class ValueGleaner
5
+ def initialize(missing_keys)
6
+ @missing_keys = missing_keys
7
+ end
8
+
9
+ def value_type(value)
10
+ return :array if value.is_a? Array
11
+ return :hash if value.is_a? Enumerable
12
+ return :single
13
+ end
14
+
15
+ def glean(value, proc, empty_hash=Empty)
16
+ case value_type(value)
17
+ when :single
18
+ value
19
+ when :hash
20
+ proc ? List.new(value, @missing_keys).instance_eval(&proc) : empty_hash
21
+ when :array
22
+ value.each_with_object([]) {|val, a|
23
+ v = glean(val, proc)
24
+ a << v unless v == Empty
25
+ }
26
+ end
27
+ end
28
+ end
29
+
30
+ class Empty; end
31
+ end
@@ -0,0 +1,282 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe HashGleaner::List do
5
+ let(:missing_keys) { HashGleaner::MissingKeys.new }
6
+
7
+ describe '#o' do
8
+ context 'when an original Hash has the specified key' do
9
+ let(:original_hash) do
10
+ {
11
+ foo: :zzz,
12
+ baa: [:wibble, :wobble, :wubble],
13
+ baz: {
14
+ spam: :pee,
15
+ ham: :kaa,
16
+ egg: :boo,
17
+ },
18
+ qux: [
19
+ {
20
+ toto: :hoge,
21
+ titi: :fuga,
22
+ tutu: :piyo,
23
+ },
24
+ {
25
+ toto: :bla,
26
+ titi: :bla,
27
+ tutu: :bla,
28
+ },
29
+ ],
30
+ xyzzy: [
31
+ [:a, :b, :c],
32
+ {
33
+ spam: :_,
34
+ ham: :_,
35
+ egg: :_,
36
+ },
37
+ {
38
+ hoge: {
39
+ foo: [:wibble],
40
+ },
41
+ fuge: {
42
+ foo: :a,
43
+ },
44
+ piyo: [
45
+ {
46
+ abc: :x,
47
+ def: :y,
48
+ ghi: :z,
49
+ },
50
+ ],
51
+ },
52
+ ],
53
+ }
54
+ end
55
+
56
+ let(:gleaner) do
57
+ described_class.new(original_hash, missing_keys)
58
+ end
59
+
60
+ context 'and the value is not a Hash nor an Array' do
61
+ subject { gleaner.o :foo }
62
+
63
+ it "returns a Hash that contains the key and the value" do
64
+ expect(subject).to have_key(:foo)
65
+ expect(subject[:foo]).to be :zzz
66
+ end
67
+ end
68
+
69
+ context 'and the value is a Hash' do
70
+ context 'and passed no block' do
71
+ subject { gleaner.o :baz }
72
+
73
+ it "returns a Hash that contains the key and " \
74
+ "a blank Hash as its value" do
75
+ expect(subject).to have_key(:baz)
76
+
77
+ value = subject[:baz]
78
+ expect(value).to be_an_instance_of(Hash)
79
+ expect(value).to be_empty
80
+ end
81
+ end
82
+
83
+ context 'and passed a block' do
84
+ subject {
85
+ gleaner.o :baz do
86
+ o :ham
87
+ o :egg
88
+ end
89
+ }
90
+
91
+ it "returns a Hash that contains the key and " \
92
+ "a hash that has pairs the block defined" do
93
+ expect(subject).to have_key(:baz)
94
+
95
+ value = subject[:baz]
96
+ expect(value).to be_an_instance_of(Hash)
97
+ expect(value.keys).to match_array([:ham, :egg])
98
+ expect(value[:ham]).to be :kaa
99
+ expect(value[:egg]).to be :boo
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'and the value is an Array of scalas' do
105
+ subject { gleaner.o :baa }
106
+
107
+ it "returns a Hash that contains the key and the array" do
108
+ expect(subject).to have_key(:baa)
109
+
110
+ value = subject[:baa]
111
+ expect(value).to be_an_instance_of(Array)
112
+ expect(value.size).to be 3
113
+ expect(value[0]).to be :wibble
114
+ expect(value[1]).to be :wobble
115
+ expect(value[2]).to be :wubble
116
+ end
117
+ end
118
+
119
+ context 'and the value is an Array of Hashs' do
120
+ context 'and no block is passed' do
121
+ subject { gleaner.o :qux }
122
+
123
+ it "returns a Hash that contains the key and a blank Array as its value" do
124
+ expect(subject).to have_key(:qux)
125
+
126
+ value = subject[:qux]
127
+ expect(value).to be_an_instance_of(Array)
128
+ expect(value).to be_empty
129
+ end
130
+ end
131
+
132
+ context 'and a block is passed' do
133
+ subject {
134
+ gleaner.o :qux do
135
+ o :toto
136
+ o :tutu
137
+ end
138
+ }
139
+
140
+ it "returns a Hash that contains the key and an Array of Hashs " \
141
+ "that has the pairs of a key and a value that is defined the block" do
142
+ expect(subject).to have_key(:qux)
143
+
144
+ value = subject[:qux]
145
+ expect(value).to be_an_instance_of(Array)
146
+ expect(value[0]).to be_an_instance_of(Hash)
147
+ expect(value[0].keys).to match_array([:toto, :tutu])
148
+ expect(value[0][:toto]).to be :hoge
149
+ expect(value[0][:tutu]).to be :piyo
150
+ expect(value[1]).to be_an_instance_of(Hash)
151
+ expect(value[1].keys).to match_array([:toto, :tutu])
152
+ expect(value[1][:toto]).to be :bla
153
+ expect(value[1][:tutu]).to be :bla
154
+ end
155
+ end
156
+ end
157
+
158
+ context 'and the value is an Array of mixed types' do
159
+ context 'and no block is passed' do
160
+ subject { gleaner.o :xyzzy }
161
+
162
+ it "returns a Hash that contains the key and an Array that " \
163
+ "contains values except for Hashs as its value" do
164
+ expect(subject).to have_key(:xyzzy)
165
+
166
+ value = subject[:xyzzy]
167
+ expect(value).to be_an_instance_of(Array)
168
+ expect(value.size).to be 1
169
+ expect(value.first).to be_an_instance_of(Array)
170
+ expect(value.first.size).to be 3
171
+ expect(value.first[0]).to be :a
172
+ expect(value.first[1]).to be :b
173
+ expect(value.first[2]).to be :c
174
+ end
175
+ end
176
+
177
+ context 'and a block is passed' do
178
+ subject {
179
+ gleaner.o :xyzzy do
180
+ o :spam
181
+ o :hoge do
182
+ o :foo
183
+ end
184
+ o :fuge
185
+ o :piyo do
186
+ o :def
187
+ end
188
+ end
189
+ }
190
+
191
+ it "returns a Hash that contains the specified key" do
192
+ expect(subject).to be_an_instance_of(Hash)
193
+ expect(subject).to have_key(:xyzzy)
194
+ end
195
+
196
+ describe "its value" do
197
+ it "is an Array as the associated value" do
198
+ value = subject[:xyzzy]
199
+ expect(value).to be_an_instance_of(Array)
200
+ expect(value).not_to be_empty
201
+ end
202
+
203
+ it "has an Array if that is defined in a block" do
204
+ value = subject[:xyzzy].first
205
+ expect(value).to be_an_instance_of(Array)
206
+ expect(value.size).to be 3
207
+ expect(value[0]).to be :a
208
+ expect(value[1]).to be :b
209
+ expect(value[2]).to be :c
210
+ end
211
+
212
+ it "has a Hash if that is defined in a block" do
213
+ value = subject[:xyzzy][1]
214
+ expect(value).to be_an_instance_of(Hash)
215
+ expect(value.keys).to match_array([:spam])
216
+ expect(value[:spam]).to be :_
217
+
218
+ value = subject[:xyzzy][2]
219
+ expect(value).to be_an_instance_of(Hash)
220
+ expect(value.keys).to match_array([:hoge, :fuge, :piyo])
221
+ expect(value[:hoge]).to be_an_instance_of(Hash)
222
+ expect(value[:hoge].keys).to match_array([:foo])
223
+ expect(value[:hoge][:foo]).to be_an_instance_of(Array)
224
+ expect(value[:hoge][:foo]).to eq [:wibble]
225
+
226
+ expect(value[:fuge]).to be_an_instance_of(Hash)
227
+ expect(value[:fuge]).to be_empty
228
+
229
+ expect(value[:piyo]).to be_an_instance_of(Array)
230
+ expect(value[:piyo].size).to be 1
231
+ expect(value[:piyo].first).to be_an_instance_of(Hash)
232
+ expect(value[:piyo].first.keys).to match_array([:def])
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ context 'when an original Hash does not have the specified key' do
240
+ let(:original_hash) do
241
+ {
242
+ pee: :foo,
243
+ kaa: :baa,
244
+ boo: :baz,
245
+ }
246
+ end
247
+
248
+ let(:gleaner) do
249
+ described_class.new(original_hash, missing_keys)
250
+ end
251
+
252
+ context 'and it is in default mode' do
253
+ subject { gleaner.o :foo }
254
+
255
+ it "returns a blank Hash" do
256
+ expect(subject).to be_an_instance_of(Hash)
257
+ expect(subject).to be_empty
258
+ end
259
+ end
260
+
261
+ context 'and it is in required mode' do
262
+ before { gleaner.required }
263
+
264
+ it "adds MissingKeys" do
265
+ expect(missing_keys.keys).to be_empty
266
+ gleaner.o :foo
267
+ expect(missing_keys.keys).to match_array([:foo])
268
+ end
269
+ end
270
+
271
+ context 'and it is in optional mode' do
272
+ before { gleaner.optional }
273
+ subject { gleaner.o :foo }
274
+
275
+ it "returns a blank Hash" do
276
+ expect(subject).to be_an_instance_of(Hash)
277
+ expect(subject).to be_empty
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ describe HashGleaner do
5
+ describe ".included" do
6
+ let(:clazz) {
7
+ clazz = Class.new(Hash)
8
+ clazz.__send__ :include, HashGleaner
9
+ clazz
10
+ }
11
+
12
+ let(:instance) {
13
+ clazz.new
14
+ }
15
+
16
+ it "responds to glean method" do
17
+ expect(instance).to respond_to(:glean)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'rspec'
4
+ require 'hash_gleaner'
5
+
6
+ Dir.glob("#{File.dirname(__FILE__)}/support/**/*.rb") do |f|
7
+ require f
8
+ end
9
+
10
+ RSpec.configure do |config|
11
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash_gleaner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - hosim
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: HashGleaner allows you to declare hash structure you want to get.
28
+ email:
29
+ - github.hosim@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - Gemfile.lock
36
+ - README.md
37
+ - Rakefile
38
+ - hash_gleaner.gemspec
39
+ - lib/hash_gleaner.rb
40
+ - lib/hash_gleaner/list.rb
41
+ - lib/hash_gleaner/missing_keys.rb
42
+ - lib/hash_gleaner/value_gleaner.rb
43
+ - spec/hash_gleaner/list_spec.rb
44
+ - spec/hash_gleaner_spec.rb
45
+ - spec/spec_helper.rb
46
+ homepage: https://github.com/hosim/hash_gleaner
47
+ licenses:
48
+ - MIT
49
+ metadata: {}
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 2.0.14
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: HashGleaner allows you to declare hash structure you want to get.
70
+ test_files:
71
+ - spec/hash_gleaner/list_spec.rb
72
+ - spec/hash_gleaner_spec.rb
73
+ - spec/spec_helper.rb