hash_gleaner 0.1.0

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: 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