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