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.
@@ -0,0 +1,20 @@
1
+ module Restruct
2
+ class Id < String
3
+
4
+ attr_reader :separator
5
+
6
+ def initialize(id, separator=nil)
7
+ @separator = separator || Restruct.id_separator
8
+ super id.to_s
9
+ end
10
+
11
+ def [](id)
12
+ Id.new "#{to_s}#{separator}#{id}", separator
13
+ end
14
+
15
+ def sections
16
+ split(separator).map { |s| Id.new s, separator }
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module Restruct
2
+ class MarshalArray < Array
3
+ include Marshalizable
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Restruct
2
+ class MarshalHash < Hash
3
+ include Marshalizable
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Restruct
2
+ class MarshalSet < Set
3
+ include Marshalizable
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ module Restruct
2
+ module Marshalizable
3
+
4
+ def serialize(element)
5
+ Marshal.dump element
6
+ end
7
+
8
+ def deserialize(element)
9
+ Marshal.load element if element
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,108 @@
1
+ module Restruct
2
+ class NestedHash
3
+
4
+ def self.new(type)
5
+ Class.new Structure do
6
+
7
+ include Enumerable
8
+
9
+ const_set :TYPE, type
10
+
11
+ def [](key)
12
+ self.class::TYPE.new id: id[key], redis: redis, parent: self
13
+ end
14
+
15
+ def fetch(key)
16
+ raise KeyError, "key not found: #{key}" unless key? key
17
+ self[key]
18
+ end
19
+
20
+ def delete(key)
21
+ self[key].tap(&:destroy)
22
+ end
23
+
24
+ def delete_if
25
+ each { |k,v| delete k if yield k, v }
26
+ self
27
+ end
28
+
29
+ def keep_if
30
+ each { |k,v| delete k unless yield k, v }
31
+ self
32
+ end
33
+ alias_method :select!, :keep_if
34
+
35
+ def clear
36
+ destroy
37
+ self
38
+ end
39
+
40
+ def keys
41
+ sections = id.sections.count + 1
42
+ redis.call('KEYS', id['*']).map do |k|
43
+ Id.new(k).sections.take(sections).last
44
+ end.uniq.sort
45
+ end
46
+
47
+ def values
48
+ keys.map { |key| self[key] }
49
+ end
50
+
51
+ def values_at(*keys)
52
+ keys.map { |key| self[key] }
53
+ end
54
+
55
+ def key?(key)
56
+ keys.include? key.to_s
57
+ end
58
+ alias_method :has_key?, :key?
59
+
60
+ def size
61
+ keys.count
62
+ end
63
+ alias_method :count, :size
64
+ alias_method :length, :size
65
+
66
+ def empty?
67
+ size == 0
68
+ end
69
+
70
+ def each
71
+ keys.each { |key| yield key, self[key] }
72
+ end
73
+ alias_method :each_pair, :each
74
+
75
+ def each_key
76
+ each { |k,v| yield k }
77
+ end
78
+
79
+ def each_value
80
+ each { |k,v| yield v }
81
+ end
82
+
83
+ def to_h
84
+ each_with_object({}) do |(key, value), hash|
85
+ hash[key] = value.respond_to?(:to_primitive) ? value.to_primitive : value
86
+ end
87
+ end
88
+ alias_method :to_primitive, :to_h
89
+
90
+ def dump
91
+ each_with_object({}) do |(key, value), hash|
92
+ hash[key] = value.dump
93
+ end
94
+ end
95
+
96
+ def restore(dump)
97
+ dump.each { |f,d| self[f].restore d }
98
+ end
99
+
100
+ def destroy
101
+ values.each(&:destroy)
102
+ end
103
+
104
+ end
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,119 @@
1
+ module Restruct
2
+ class Set < Structure
3
+
4
+ include Enumerable
5
+ extend Forwardable
6
+
7
+ def_delegators :to_set, :union, :|, :+,
8
+ :intersection, :&,
9
+ :difference, :-,
10
+ :proper_subset?, :subset?,
11
+ :proper_superset?, :superset?,
12
+ :^
13
+
14
+ def add(member)
15
+ _add member
16
+ self
17
+ end
18
+ alias_method :<<, :add
19
+
20
+ def add?(member)
21
+ _add(member) == 0 ? nil : self
22
+ end
23
+
24
+ def merge(members)
25
+ _add *members
26
+ self
27
+ end
28
+
29
+ def delete(member)
30
+ _delete member
31
+ self
32
+ end
33
+
34
+ def delete?(member)
35
+ _delete(member) == 0 ? nil : self
36
+ end
37
+
38
+ def subtract(members)
39
+ _delete *members
40
+ self
41
+ end
42
+
43
+ def delete_if
44
+ each { |e| delete e if yield e }
45
+ self
46
+ end
47
+
48
+ def keep_if
49
+ each { |e| delete e unless yield e }
50
+ self
51
+ end
52
+ alias_method :select!, :keep_if
53
+
54
+ def clear
55
+ destroy
56
+ self
57
+ end
58
+
59
+ def size
60
+ redis.call 'SCARD', id
61
+ end
62
+ alias_method :count, :size
63
+ alias_method :length, :size
64
+
65
+ def empty?
66
+ size == 0
67
+ end
68
+
69
+ def include?(member)
70
+ redis.call('SISMEMBER', id, serialize(member)) == 1
71
+ end
72
+
73
+ def each(&block)
74
+ to_a.each(&block)
75
+ end
76
+
77
+ def to_a
78
+ redis.call('SMEMBERS', id).map { |e| deserialize e }
79
+ end
80
+
81
+ def to_set
82
+ to_a.to_set
83
+ end
84
+ alias_method :to_primitive, :to_set
85
+
86
+ alias_method :<, :proper_subset?
87
+ alias_method :<=, :subset?
88
+ alias_method :>, :proper_superset?
89
+ alias_method :>=, :superset?
90
+
91
+ def intersect?(set)
92
+ !disjoint? set
93
+ end
94
+
95
+ def disjoint?(set)
96
+ (to_a & set.to_a).empty?
97
+ end
98
+
99
+ private
100
+
101
+ def _add(*members)
102
+ redis.call 'SADD', id, *members.map { |m| serialize m }
103
+ end
104
+
105
+ def _delete(*members)
106
+ redis.call 'SREM', id, *members.map { |m| serialize m }
107
+ end
108
+
109
+
110
+ def serialize(string)
111
+ string
112
+ end
113
+
114
+ def deserialize(string)
115
+ string
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,32 @@
1
+ module Restruct
2
+ class Structure
3
+
4
+ attr_reader :redis, :id
5
+
6
+ def initialize(options={})
7
+ @redis = options[:redis] || Restruct.redis
8
+ @id = Id.new options[:id] || Restruct.generate_id
9
+ end
10
+
11
+ def ==(object)
12
+ object.class == self.class &&
13
+ object.id == id &&
14
+ object.redis == redis
15
+ end
16
+ alias_method :eql?, :==
17
+
18
+ def dump
19
+ redis.call 'DUMP', id
20
+ end
21
+
22
+ def restore(dump)
23
+ destroy
24
+ redis.call 'RESTORE', id, 0, dump
25
+ end
26
+
27
+ def destroy
28
+ redis.call 'DEL', id
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Restruct
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'restruct/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'restruct'
8
+ spec.version = Restruct::VERSION
9
+ spec.authors = ['Gabriel Naiman']
10
+ spec.email = ['gabynaiman@gmail.com']
11
+ spec.summary = 'Redis structures'
12
+ spec.description = 'Redis structures'
13
+ spec.homepage = 'https://github.com/gabynaiman/restruct'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'redic', '~> 1.1.1'
22
+ spec.add_dependency 'class_config', '~> 0.0.1'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'minitest', '~> 4.7'
27
+ spec.add_development_dependency 'turn', '~> 0.9'
28
+ spec.add_development_dependency 'simplecov'
29
+ spec.add_development_dependency 'pry-nav'
30
+ end
@@ -0,0 +1,338 @@
1
+ require 'minitest_helper'
2
+
3
+ [Restruct::Array, Restruct::MarshalArray].each do |klass|
4
+
5
+ describe klass do
6
+
7
+ let(:array) { klass.new }
8
+
9
+ def fill(elements)
10
+ redis.call 'RPUSH', array.id, *(elements.map { |e| array.send(:serialize, e) })
11
+ end
12
+
13
+ describe 'Getters' do
14
+
15
+ it '[]' do
16
+ fill %w(a b c d e)
17
+
18
+ array[0].must_equal 'a'
19
+ array[1].must_equal 'b'
20
+ array[2].must_equal 'c'
21
+ array[6].must_be_nil
22
+
23
+ array[0..-1].must_equal %w(a b c d e)
24
+ array[1..3].must_equal %w(b c d)
25
+ array[4..7].must_equal ['e']
26
+ array[5..10].must_equal []
27
+ array[6..10].must_be_nil
28
+
29
+ array[-3,3].must_equal %w(c d e)
30
+ array[1,2].must_equal %w(b c)
31
+ array[5,1].must_equal []
32
+ array[6,1].must_be_nil
33
+
34
+ error = proc { array[1,2,3,4] }.must_raise ArgumentError
35
+ error.message.must_equal 'wrong number of arguments (4 for 1..2)'
36
+
37
+ error = proc { array['x'] }.must_raise TypeError
38
+ error.message.must_equal 'no implicit conversion from string to integer'
39
+ end
40
+
41
+ it 'at' do
42
+ fill %w(a b c d)
43
+
44
+ array.at(0).must_equal 'a'
45
+ array.at(1).must_equal 'b'
46
+ array.at(-1).must_equal 'd'
47
+ array.at(10).must_be_nil
48
+ end
49
+
50
+ it 'values_at' do
51
+ fill %w(a b c d e f)
52
+
53
+ array.values_at(1, 3, 5).must_equal %w(b d f)
54
+ array.values_at(1, 3, 5, 7).must_equal ['b', 'd', 'f', nil]
55
+ array.values_at(-1, -2, -2, -7).must_equal ['f', 'e', 'e', nil]
56
+ end
57
+
58
+ it 'fetch' do
59
+ fill %w(a b c)
60
+
61
+ array.fetch(-1).must_equal 'c'
62
+ array.fetch(0).must_equal 'a'
63
+ array.fetch(4, 'x').must_equal 'x'
64
+ array.fetch(4) { |i| (i + 1).to_s }.must_equal '5'
65
+
66
+ error = proc { array.fetch(4) }.must_raise IndexError
67
+ error.message.must_equal 'index 4 outside of array bounds: -3...3'
68
+ end
69
+
70
+ it 'first' do
71
+ fill %w(a b c)
72
+ array.first.must_equal 'a'
73
+ end
74
+
75
+ it 'last' do
76
+ fill %w(a b c)
77
+ array.last.must_equal 'c'
78
+ end
79
+
80
+ end
81
+
82
+ describe 'Setters' do
83
+
84
+ it '[]=' do
85
+ fill %w(a b c d)
86
+
87
+ (array[0] = 'x').must_equal 'x'
88
+ array.to_a.must_equal %w(x b c d)
89
+
90
+ (array[-1] = 'z').must_equal 'z'
91
+ array.to_a.must_equal %w(x b c z)
92
+
93
+ error = proc { array[10] = '.' }.must_raise IndexError
94
+ error.message.must_equal 'index 10 outside of array bounds: -4...4'
95
+
96
+ error = proc { array['k'] = '.' }.must_raise TypeError
97
+ error.message.must_equal 'no implicit conversion from string to integer'
98
+ end
99
+
100
+ it 'push' do
101
+ fill %w(a b c)
102
+
103
+ array.push('d').must_equal array
104
+ array.to_a.must_equal %w(a b c d)
105
+
106
+ array.push('x', 'y', 'z').must_equal array
107
+ array.to_a.must_equal %w(a b c d x y z)
108
+ end
109
+
110
+ it '<<' do
111
+ fill %w(a b c)
112
+
113
+ (array << 'd').must_equal array
114
+ array.to_a.must_equal %w(a b c d)
115
+ end
116
+
117
+ it 'pop' do
118
+ fill %w(a b c d e f)
119
+
120
+ (array.pop).must_equal 'f'
121
+ array.to_a.must_equal %w(a b c d e)
122
+
123
+ (array.pop(2)).must_equal %w(d e)
124
+ array.to_a.must_equal %w(a b c)
125
+
126
+ (array.pop(5)).must_equal %w(a b c)
127
+ array.to_a.must_equal []
128
+ end
129
+
130
+ it 'shift' do
131
+ fill %w(a b c d e f)
132
+
133
+ (array.shift).must_equal 'a'
134
+ array.to_a.must_equal %w(b c d e f)
135
+
136
+ (array.shift(2)).must_equal %w(b c)
137
+ array.to_a.must_equal %w(d e f)
138
+
139
+ (array.shift(5)).must_equal %w(d e f)
140
+ array.to_a.must_equal []
141
+ end
142
+
143
+ it 'insert' do
144
+ fill %w(a b c d)
145
+
146
+ array.insert(0, 'A').must_equal array
147
+ array.to_a.must_equal %w(A a b c d)
148
+
149
+ array.insert(2, 'B', 'B').must_equal array
150
+ array.to_a.must_equal %w(A a B B b c d)
151
+
152
+ array.insert(-2, 'x', 'y', 'z').must_equal array
153
+ array.to_a.must_equal %w(A a B B b c x y z d)
154
+
155
+ array.insert(-5, 'w').must_equal array
156
+ array.to_a.must_equal %w(A a B B b c w x y z d)
157
+ end
158
+
159
+ it 'concat' do
160
+ fill %w(a b c)
161
+
162
+ array.concat(%w(x y z)).must_equal array
163
+ array.to_a.must_equal %w(a b c x y z)
164
+ end
165
+
166
+ it 'delete' do
167
+ fill %w(a b a b a b)
168
+
169
+ array.delete('b').must_equal 'b'
170
+ array.to_a.must_equal %w(a a a)
171
+
172
+ array.delete('c').must_be_nil
173
+ array.to_a.must_equal %w(a a a)
174
+ end
175
+
176
+ it 'delete_at' do
177
+ fill %w(a b c a b c a b c)
178
+
179
+ array.delete_at(3).must_equal 'a'
180
+ array.to_a.must_equal %w(a b c b c a b c)
181
+
182
+ array.delete_at(-4).must_equal 'c'
183
+ array.to_a.must_equal %w(a b c b a b c)
184
+
185
+ array.delete_at(10).must_be_nil
186
+ array.to_a.must_equal %w(a b c b a b c)
187
+ end
188
+
189
+ it 'delete_if' do
190
+ fill %w(a b c a b c a b c)
191
+
192
+ array.delete_if { |e| e == 'a' }.must_equal array
193
+ array.to_a.must_equal %w(b c b c b c)
194
+ end
195
+
196
+ %w(keep_if select!).each do |method|
197
+ it method do
198
+ fill %w(a b c a b c a b c)
199
+
200
+ array.send(method) { |e| e == 'a' }.must_equal array
201
+ array.to_a.must_equal %w(a a a)
202
+ end
203
+ end
204
+
205
+ it 'clear' do
206
+ fill %w(a b c d)
207
+
208
+ array.clear.must_equal array
209
+ array.must_be_empty
210
+ end
211
+
212
+ end
213
+
214
+ describe 'Info' do
215
+
216
+ %w(size count length).each do |method|
217
+ it method do
218
+ fill %w(a b c)
219
+ array.send(method).must_equal 3
220
+ end
221
+ end
222
+
223
+ it 'empty?' do
224
+ array.must_be :empty?
225
+ fill %w(a b c)
226
+ array.wont_be :empty?
227
+ end
228
+
229
+ it 'include?' do
230
+ fill %w(a b c)
231
+
232
+ assert array.include? 'a'
233
+ refute array.include? 'z'
234
+ end
235
+
236
+ end
237
+
238
+ describe 'Transformations' do
239
+
240
+ %w(to_a to_ary to_primitive).each do |method|
241
+ it method do
242
+ fill %w(a b c)
243
+ array.send(method).must_equal %w(a b c)
244
+ end
245
+ end
246
+
247
+ it 'join' do
248
+ fill %w(a b c)
249
+
250
+ array.join.must_equal 'abc'
251
+ array.join('-').must_equal 'a-b-c'
252
+ end
253
+
254
+ it 'uniq' do
255
+ fill %w(a1 a1 a2 a2 b1 b1 b2 b2)
256
+
257
+ array.uniq.must_equal %w(a1 a2 b1 b2)
258
+ array.uniq { |e| e[1] }.must_equal %w(a1 a2)
259
+ end
260
+
261
+ it 'reverse' do
262
+ fill %w(a b c)
263
+ array.reverse.must_equal %w(c b a)
264
+ end
265
+
266
+ end
267
+
268
+ describe 'Enumerable' do
269
+
270
+ it 'included module' do
271
+ assert klass.included_modules.include? Enumerable
272
+ end
273
+
274
+ it 'each' do
275
+ fill %w(a b c)
276
+
277
+ list = []
278
+ array.each { |e| list << e }
279
+
280
+ list.must_equal array.to_a
281
+ end
282
+
283
+ it 'each_index' do
284
+ fill %w(a b c)
285
+
286
+ list = []
287
+ array.each_index { |i| list << i }
288
+
289
+ list.must_equal [0,1,2]
290
+ end
291
+
292
+ end
293
+
294
+ describe 'Sets' do
295
+
296
+ it '+' do
297
+ fill %w(a b c)
298
+ (array + %w(x y z)).must_equal %w(a b c x y z)
299
+ end
300
+
301
+ it '-' do
302
+ fill %w(a b c)
303
+ (array - %w(a z)).must_equal %w(b c)
304
+ end
305
+
306
+ it '&' do
307
+ fill %w(a b c)
308
+ (array & %w(b a z)).must_equal %w(a b)
309
+ end
310
+
311
+ it '|' do
312
+ fill %w(a b c)
313
+ (array | %w(b c d)).must_equal %w(a b c d)
314
+ end
315
+
316
+ end
317
+
318
+ it 'Equality' do
319
+ copy = klass.new id: array.id
320
+ assert array == copy
321
+ assert array.eql? copy
322
+ refute array.equal? copy
323
+ end
324
+
325
+ it 'Dump/Restore' do
326
+ fill %w(a b c)
327
+
328
+ dump = array.dump
329
+ other = klass.new
330
+ other.restore dump
331
+
332
+ other.id.wont_equal array.id
333
+ other.to_primitive.must_equal array.to_primitive
334
+ end
335
+
336
+ end
337
+
338
+ end