restruct 0.0.1
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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +22 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +17 -0
- data/lib/restruct.rb +30 -0
- data/lib/restruct/array.rb +188 -0
- data/lib/restruct/hash.rb +124 -0
- data/lib/restruct/id.rb +20 -0
- data/lib/restruct/marshal_array.rb +5 -0
- data/lib/restruct/marshal_hash.rb +5 -0
- data/lib/restruct/marshal_set.rb +5 -0
- data/lib/restruct/marshalizable.rb +13 -0
- data/lib/restruct/nested_hash.rb +108 -0
- data/lib/restruct/set.rb +119 -0
- data/lib/restruct/structure.rb +32 -0
- data/lib/restruct/version.rb +3 -0
- data/restruct.gemspec +30 -0
- data/spec/array_spec.rb +338 -0
- data/spec/coverage_helper.rb +8 -0
- data/spec/hash_spec.rb +242 -0
- data/spec/id_spec.rb +48 -0
- data/spec/minitest_helper.rb +21 -0
- data/spec/nested_hash_spec.rb +205 -0
- data/spec/set_spec.rb +257 -0
- metadata +192 -0
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
[Restruct::Hash, Restruct::MarshalHash].each do |klass|
|
4
|
+
|
5
|
+
describe klass do
|
6
|
+
|
7
|
+
let(:hash) { klass.new }
|
8
|
+
|
9
|
+
def fill(data)
|
10
|
+
data.each { |k,v| data[k] = hash.send(:serialize, v) }
|
11
|
+
redis.call 'HMSET', hash.id, *data.flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'Getters' do
|
15
|
+
|
16
|
+
it '[]' do
|
17
|
+
fill a: 'x', b: 'y'
|
18
|
+
|
19
|
+
hash[:a].must_equal 'x'
|
20
|
+
hash[:b].must_equal 'y'
|
21
|
+
hash[:c].must_be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'fetch' do
|
25
|
+
fill a: 'x', b: 'y'
|
26
|
+
|
27
|
+
hash.fetch(:a).must_equal 'x'
|
28
|
+
hash.fetch(:b).must_equal 'y'
|
29
|
+
hash.fetch(:c, 'z').must_equal 'z'
|
30
|
+
hash.fetch(:c) { |k| k.to_s }.must_equal 'c'
|
31
|
+
|
32
|
+
error = proc { hash.fetch(:c) }.must_raise KeyError
|
33
|
+
error.message.must_equal 'key not found: c'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'key' do
|
37
|
+
fill a: 'x', b: 'y', c: 'y'
|
38
|
+
|
39
|
+
hash.key('x').must_equal 'a'
|
40
|
+
hash.key('y').must_equal 'b'
|
41
|
+
hash.key('z').must_be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'keys' do
|
45
|
+
fill a: 'x', b: 'y', c: 'z'
|
46
|
+
hash.keys.must_equal %w(a b c)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'values' do
|
50
|
+
fill a: 'x', b: 'y', c: 'z'
|
51
|
+
hash.values.must_equal %w(x y z)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'values_at' do
|
55
|
+
fill a: 'x', b: 'y', c: 'z'
|
56
|
+
hash.values_at(:a, :f, :b, :g, :c).must_equal ['x', nil, 'y', nil, 'z']
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'Setters' do
|
62
|
+
|
63
|
+
%w([]= store).each do |method|
|
64
|
+
it method do
|
65
|
+
fill a: 'x', b: 'y'
|
66
|
+
|
67
|
+
hash.send(method, :a, 'a').must_equal 'a'
|
68
|
+
hash.to_h.must_equal 'a' => 'a', 'b' => 'y'
|
69
|
+
|
70
|
+
hash.send(method, :c, 'z').must_equal 'z'
|
71
|
+
hash.to_h.must_equal 'a' => 'a', 'b' => 'y', 'c' => 'z'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
%w(update merge!).each do |method|
|
76
|
+
it method do
|
77
|
+
fill a: 'x', b: 'y'
|
78
|
+
|
79
|
+
hash.send(method, a: 'a', c: 'z').must_equal hash
|
80
|
+
hash.to_h.must_equal 'a' => 'a', 'b' => 'y', 'c' => 'z'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'delete' do
|
85
|
+
fill a: 'x', b: 'y'
|
86
|
+
|
87
|
+
hash.delete(:b).must_equal 'y'
|
88
|
+
hash.to_h.must_equal 'a' => 'x'
|
89
|
+
|
90
|
+
hash.delete(:c).must_be_nil
|
91
|
+
hash.to_h.must_equal 'a' => 'x'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'delete_if' do
|
95
|
+
fill a: 'x', b: 'y'
|
96
|
+
|
97
|
+
hash.delete_if { |k,v| v == 'x' }.must_equal hash
|
98
|
+
hash.to_h.must_equal 'b' => 'y'
|
99
|
+
end
|
100
|
+
|
101
|
+
%w(keep_if select!).each do |method|
|
102
|
+
it method do
|
103
|
+
fill a: 'x', b: 'y'
|
104
|
+
|
105
|
+
hash.send(method) { |k,v| v == 'x' }.must_equal hash
|
106
|
+
hash.to_h.must_equal 'a' => 'x'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'clear' do
|
111
|
+
fill a: 'x', b: 'y'
|
112
|
+
|
113
|
+
hash.clear.must_equal hash
|
114
|
+
hash.must_be_empty
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe 'Info' do
|
120
|
+
|
121
|
+
%w(size count length).each do |method|
|
122
|
+
it method do
|
123
|
+
fill a: 'x', b: 'y'
|
124
|
+
hash.send(method).must_equal 2
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'empty?' do
|
129
|
+
hash.must_be :empty?
|
130
|
+
fill a: 'x', b: 'y'
|
131
|
+
hash.wont_be :empty?
|
132
|
+
end
|
133
|
+
|
134
|
+
%w(key? has_key?).each do |method|
|
135
|
+
it method do
|
136
|
+
fill a: 'x', b: 'y'
|
137
|
+
|
138
|
+
assert hash.send(method, :a)
|
139
|
+
refute hash.send(method, :c)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
%w(value? has_value?).each do |method|
|
144
|
+
it method do
|
145
|
+
fill a: 'x', b: 'y'
|
146
|
+
|
147
|
+
assert hash.send(method, 'x')
|
148
|
+
refute hash.send(method, 'z')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
describe 'Transformations' do
|
155
|
+
|
156
|
+
%w(to_h to_primitive).each do |method|
|
157
|
+
it method do
|
158
|
+
fill a: 'x', b: 'y'
|
159
|
+
hash.send(method).must_equal 'a' => 'x', 'b' => 'y'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'merge' do
|
164
|
+
fill a: 'x', b: 'y'
|
165
|
+
hash.merge('c' => 'z', 'a' => 'a').must_equal 'a' => 'a', 'b' => 'y', 'c' => 'z'
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'flatten' do
|
169
|
+
fill a: 'x', b: 'y'
|
170
|
+
hash.flatten.must_equal %w(a x b y)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'invert' do
|
174
|
+
fill a: 'x', b: 'y'
|
175
|
+
hash.invert.must_equal 'x' => 'a', 'y' => 'b'
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
describe 'Enumerable' do
|
181
|
+
|
182
|
+
it 'included module' do
|
183
|
+
assert Restruct::Hash.included_modules.include? Enumerable
|
184
|
+
end
|
185
|
+
|
186
|
+
%w(each each_pair).each do |method|
|
187
|
+
it method do
|
188
|
+
fill a: 'x', b: 'y'
|
189
|
+
|
190
|
+
keys = []
|
191
|
+
values = []
|
192
|
+
hash.send(method) do |k,v|
|
193
|
+
keys << k
|
194
|
+
values << v
|
195
|
+
end
|
196
|
+
|
197
|
+
keys.must_equal hash.keys
|
198
|
+
values.must_equal hash.values
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'each_key' do
|
203
|
+
fill a: 'x', b: 'y'
|
204
|
+
|
205
|
+
keys = []
|
206
|
+
hash.each_key { |k| keys << k }
|
207
|
+
|
208
|
+
keys.must_equal hash.keys
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'each_value' do
|
212
|
+
fill a: 'x', b: 'y'
|
213
|
+
|
214
|
+
values = []
|
215
|
+
hash.each_value { |v| values << v }
|
216
|
+
|
217
|
+
values.must_equal hash.values
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'Equality' do
|
223
|
+
copy = klass.new id: hash.id
|
224
|
+
assert hash == copy
|
225
|
+
assert hash.eql? copy
|
226
|
+
refute hash.equal? copy
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'Dump/Restore' do
|
230
|
+
fill a: 'x', b: 'y'
|
231
|
+
|
232
|
+
dump = hash.dump
|
233
|
+
other = klass.new
|
234
|
+
other.restore dump
|
235
|
+
|
236
|
+
other.id.wont_equal hash.id
|
237
|
+
other.to_primitive.must_equal hash.to_primitive
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
data/spec/id_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Restruct::Id do
|
4
|
+
|
5
|
+
Id = Restruct::Id
|
6
|
+
|
7
|
+
it 'Return the namespace' do
|
8
|
+
id = Id.new 'foo'
|
9
|
+
id.must_equal 'foo'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'Prepend the namespace' do
|
13
|
+
id = Id.new 'foo'
|
14
|
+
id['bar'].must_equal 'foo:bar'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'Work in more than one level' do
|
18
|
+
id_1 = Id.new 'foo'
|
19
|
+
id_2 = Id.new id_1['bar']
|
20
|
+
id_2['baz'].must_equal 'foo:bar:baz'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'Be chainable' do
|
24
|
+
id = Id.new 'foo'
|
25
|
+
id['bar']['baz'].must_equal 'foo:bar:baz'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'Accept symbols' do
|
29
|
+
id = Id.new :foo
|
30
|
+
id[:bar].must_equal 'foo:bar'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Accept numbers' do
|
34
|
+
id = Id.new 'foo'
|
35
|
+
id[3].must_equal 'foo:3'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'Split in sections' do
|
39
|
+
id = Id.new(:foo)[:bar][:buz]
|
40
|
+
id.sections.must_equal %w(foo bar buz)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'Customize separator' do
|
44
|
+
id = Id.new('foo', '|')['bar']['buz']
|
45
|
+
id.must_equal 'foo|bar|buz'
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'coverage_helper'
|
2
|
+
require 'restruct'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'turn'
|
5
|
+
require 'pry-nav'
|
6
|
+
|
7
|
+
Turn.config do |c|
|
8
|
+
c.format = :pretty
|
9
|
+
c.natural = true
|
10
|
+
c.ansi = true
|
11
|
+
end
|
12
|
+
|
13
|
+
class Minitest::Spec
|
14
|
+
def redis
|
15
|
+
Restruct.redis
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
redis.call('KEYS', Restruct::Id.new(:restruct)['*']).each { |k| redis.call 'DEL', k }
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
class Counter < Restruct::Structure
|
4
|
+
def current
|
5
|
+
(redis.call('GET', id) || 0).to_i
|
6
|
+
end
|
7
|
+
alias_method :to_primitive, :current
|
8
|
+
|
9
|
+
def incr
|
10
|
+
redis.call 'SET', id, current + 1
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
CounterHash = Restruct::NestedHash.new(Counter)
|
16
|
+
|
17
|
+
|
18
|
+
describe Restruct::NestedHash do
|
19
|
+
|
20
|
+
let(:hash) { CounterHash.new }
|
21
|
+
|
22
|
+
describe 'Getters' do
|
23
|
+
|
24
|
+
it '[]' do
|
25
|
+
hash[:a].current.must_equal 0
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'fetch' do
|
29
|
+
hash[:a].incr
|
30
|
+
|
31
|
+
hash.fetch(:a).current.must_equal 1
|
32
|
+
|
33
|
+
error = proc { hash.fetch(:c) }.must_raise KeyError
|
34
|
+
error.message.must_equal 'key not found: c'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'keys' do
|
38
|
+
hash[:a].incr
|
39
|
+
hash[:b].incr
|
40
|
+
|
41
|
+
hash.keys.must_equal %w(a b)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'values' do
|
45
|
+
hash[:a].incr.incr
|
46
|
+
hash[:b].incr
|
47
|
+
|
48
|
+
hash.values.map(&:current).must_equal [2, 1]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'values_at' do
|
52
|
+
hash[:a].incr.incr
|
53
|
+
hash[:b].incr
|
54
|
+
|
55
|
+
hash.values_at(:a, :f, :b, :g).map(&:current).must_equal [2, 0, 1, 0]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'Setters' do
|
61
|
+
|
62
|
+
it 'delete' do
|
63
|
+
hash[:a].incr.incr
|
64
|
+
hash[:b].incr
|
65
|
+
|
66
|
+
hash.delete(:a)
|
67
|
+
|
68
|
+
hash.keys.must_equal %w(b)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'delete_if' do
|
72
|
+
hash[:a].incr.incr
|
73
|
+
hash[:b].incr
|
74
|
+
|
75
|
+
hash.delete_if { |k,v| v.current > 1 }.must_equal hash
|
76
|
+
hash.keys.must_equal %w(b)
|
77
|
+
end
|
78
|
+
|
79
|
+
%w(keep_if select!).each do |method|
|
80
|
+
it method do
|
81
|
+
hash[:a].incr.incr
|
82
|
+
hash[:b].incr
|
83
|
+
|
84
|
+
hash.send(method) { |k,v| v.current > 1 }.must_equal hash
|
85
|
+
hash.keys.must_equal %w(a)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'clear' do
|
90
|
+
hash[:a].incr.incr
|
91
|
+
hash[:b].incr
|
92
|
+
|
93
|
+
hash.clear.must_equal hash
|
94
|
+
hash.must_be_empty
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'Info' do
|
100
|
+
|
101
|
+
%w(size count length).each do |method|
|
102
|
+
it method do
|
103
|
+
hash[:a].incr.incr
|
104
|
+
hash[:b].incr
|
105
|
+
|
106
|
+
hash.send(method).must_equal 2
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'empty?' do
|
111
|
+
hash.must_be :empty?
|
112
|
+
hash[:a].incr
|
113
|
+
hash.wont_be :empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
%w(key? has_key?).each do |method|
|
117
|
+
it method do
|
118
|
+
hash[:a].incr.incr
|
119
|
+
hash[:b].incr
|
120
|
+
|
121
|
+
assert hash.send(method, :a)
|
122
|
+
refute hash.send(method, :c)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'Transformations' do
|
129
|
+
|
130
|
+
%w(to_h to_primitive).each do |method|
|
131
|
+
it method do
|
132
|
+
hash[:a].incr.incr
|
133
|
+
hash[:b].incr
|
134
|
+
|
135
|
+
hash.send(method).must_equal 'a' => 2, 'b' => 1
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
describe 'Enumerable' do
|
142
|
+
|
143
|
+
it 'included module' do
|
144
|
+
assert Restruct::Hash.included_modules.include? Enumerable
|
145
|
+
end
|
146
|
+
|
147
|
+
%w(each each_pair).each do |method|
|
148
|
+
it method do
|
149
|
+
hash[:a].incr.incr
|
150
|
+
hash[:b].incr
|
151
|
+
|
152
|
+
keys = []
|
153
|
+
values = []
|
154
|
+
hash.send(method) do |k,v|
|
155
|
+
keys << k
|
156
|
+
values << v
|
157
|
+
end
|
158
|
+
|
159
|
+
keys.must_equal hash.keys
|
160
|
+
values.must_equal hash.values
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'each_key' do
|
165
|
+
hash[:a].incr.incr
|
166
|
+
hash[:b].incr
|
167
|
+
|
168
|
+
keys = []
|
169
|
+
hash.each_key { |k| keys << k }
|
170
|
+
|
171
|
+
keys.must_equal hash.keys
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'each_value' do
|
175
|
+
hash[:a].incr.incr
|
176
|
+
hash[:b].incr
|
177
|
+
|
178
|
+
values = []
|
179
|
+
hash.each_value { |v| values << v }
|
180
|
+
|
181
|
+
values.must_equal hash.values
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'Equality' do
|
187
|
+
copy = CounterHash.new id: hash.id
|
188
|
+
assert hash == copy
|
189
|
+
assert hash.eql? copy
|
190
|
+
refute hash.equal? copy
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'Dump/Restore' do
|
194
|
+
hash[:a].incr.incr
|
195
|
+
hash[:b].incr
|
196
|
+
|
197
|
+
dump = hash.dump
|
198
|
+
other = CounterHash.new
|
199
|
+
other.restore dump
|
200
|
+
|
201
|
+
other.id.wont_equal hash.id
|
202
|
+
other.to_primitive.must_equal hash.to_primitive
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|