drip 0.0.2 → 0.0.3
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/lib/drip.rb +313 -151
- data/lib/drip/version.rb +2 -2
- data/sample/drip_tw.rb +45 -7
- data/test/basic.rb +73 -20
- metadata +33 -39
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 029a7c4fb43e4852aca41b6f1a2ba481062cec47
|
4
|
+
data.tar.gz: d1c6ca3d3291e2cf73171e345125bfb11e06b8dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c0c2bf53465abe23f7b56b51172d0a107477882f037ecb2dddc75bd18d1f94e34797983fd51bf3b99495175b464fb6699f924f15955b7a5645d83e3780071ef
|
7
|
+
data.tar.gz: 9e057c27351793c8f95efc022c64baa52faca161cfec6048dca62c37f14ad574ffcd722497a6afad4b89e05f4b19fb92980357048506c613a4c7e4213d559aba
|
data/lib/drip.rb
CHANGED
@@ -5,135 +5,16 @@ require 'enumerator'
|
|
5
5
|
|
6
6
|
class Drip
|
7
7
|
include DRbUndumped
|
8
|
-
|
9
|
-
|
10
|
-
class ImmutableDrip
|
11
|
-
class Generator
|
12
|
-
def initialize
|
13
|
-
@pool = []
|
14
|
-
@tag = []
|
15
|
-
@shared = Hash.new {|h, k| h[k] = k; k}
|
16
|
-
end
|
17
|
-
|
18
|
-
def add(key, value, *tag)
|
19
|
-
@pool << [key, value]
|
20
|
-
idx = @pool.size - 1
|
21
|
-
tag.uniq.each do |t|
|
22
|
-
@tag << [[@shared[t], key], idx]
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def generate
|
27
|
-
tag = @tag.sort
|
28
|
-
tag.inject(nil) do |last, kv|
|
29
|
-
k = kv[0]
|
30
|
-
k[0] = last if k[0] == last
|
31
|
-
k[0]
|
32
|
-
end
|
33
|
-
ImmutableDrip.new(@pool.sort, tag)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
INF = 1.0/0
|
38
|
-
|
39
|
-
def initialize(pool=[], tag=[])
|
40
|
-
@pool = pool
|
41
|
-
@tag = tag
|
42
|
-
end
|
43
|
-
|
44
|
-
def fetch(key)
|
45
|
-
idx = lower_boundary(@pool, key)
|
46
|
-
k, v = @pool[idx]
|
47
|
-
k == key ? v.to_a : nil
|
48
|
-
end
|
49
|
-
|
50
|
-
def read(key, n=1)
|
51
|
-
idx = lower_boundary(@pool, key + 1)
|
52
|
-
return [] unless idx
|
53
|
-
@pool[idx, n].collect {|kv|
|
54
|
-
[kv[0], *kv[1].to_a]
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
|
-
def read_tag(key, tag, n=1)
|
59
|
-
idx = lower_boundary(@tag, [tag, key + 1])
|
60
|
-
return [] unless idx
|
61
|
-
@tag[idx, n].find_all {|kv| kv[0][0] == tag}.collect {|kv|
|
62
|
-
[kv[0][1], *@pool[kv[1]][1].to_a]
|
63
|
-
}
|
64
|
-
end
|
65
|
-
|
66
|
-
def head_tag(n, tag)
|
67
|
-
lower = lower_boundary(@tag, [tag, 0])
|
68
|
-
upper = upper_boundary(@tag, [tag, INF])
|
69
|
-
lower = [lower, upper - n].max
|
70
|
-
@tag[lower ... upper].collect {|kv|
|
71
|
-
[kv[0][1], *@pool[kv[1]][1].to_a]
|
72
|
-
}
|
73
|
-
end
|
74
|
-
|
75
|
-
def head(n=1, tag=nil)
|
76
|
-
return head_tag(n, tag) if tag
|
77
|
-
n = @pool.size < n ? @pool.size : n
|
78
|
-
@pool[-n, n].collect {|kv|
|
79
|
-
[kv[0], *kv[1].to_a]
|
80
|
-
}
|
81
|
-
end
|
82
|
-
|
83
|
-
def older_tag(key, tag)
|
84
|
-
idx = upper_boundary(@tag, [tag, key-1])
|
85
|
-
k, v = @tag[idx - 1]
|
86
|
-
k && k[0] == tag ? [k[1], *@pool[v][1].to_a] : nil
|
87
|
-
end
|
88
|
-
|
89
|
-
def older(key, tag=nil)
|
90
|
-
key = @pool[-1][0] + 1 unless key
|
91
|
-
return older_tag(key, tag) if tag
|
92
|
-
idx = upper_boundary(@pool, key - 1)
|
93
|
-
k, v = @pool[idx - 1]
|
94
|
-
k && k < key ? [k, *v.to_a] : nil
|
95
|
-
end
|
96
|
-
|
97
|
-
def newer(key, tag=nil)
|
98
|
-
return read(key, 1)[0] unless tag
|
99
|
-
read_tag(key, tag, 1)[0]
|
100
|
-
end
|
101
|
-
|
102
|
-
def lower_boundary(ary, key)
|
103
|
-
lower = -1
|
104
|
-
upper = ary.size
|
105
|
-
while lower + 1 != upper
|
106
|
-
mid = (lower + upper).div(2)
|
107
|
-
if (ary[mid][0] <=> key) < 0
|
108
|
-
lower = mid
|
109
|
-
else
|
110
|
-
upper = mid
|
111
|
-
end
|
112
|
-
end
|
113
|
-
return upper
|
114
|
-
end
|
115
|
-
|
116
|
-
def upper_boundary(ary, key)
|
117
|
-
lower = -1
|
118
|
-
upper = ary.size
|
119
|
-
while lower + 1 != upper
|
120
|
-
mid = (lower + upper).div(2)
|
121
|
-
if (ary[mid][0] <=> key) <= 0
|
122
|
-
lower = mid
|
123
|
-
else
|
124
|
-
upper = mid
|
125
|
-
end
|
126
|
-
end
|
127
|
-
return lower + 1
|
128
|
-
end
|
129
|
-
end
|
8
|
+
INF = 1.0/0.0
|
130
9
|
|
10
|
+
def inspect; to_s; end
|
131
11
|
def initialize(dir, option={})
|
132
|
-
@
|
12
|
+
@past = prepare_store(dir, option)
|
13
|
+
@fence = (@past.head[0][0] rescue 0) || 0
|
14
|
+
@pool = Drip::SortedArray.new([])
|
133
15
|
@tag = RBTree.new
|
134
16
|
@event = Rinda::TupleSpace.new(5)
|
135
|
-
@event.write([:last,
|
136
|
-
prepare_store(dir, option)
|
17
|
+
@event.write([:last, @fence])
|
137
18
|
end
|
138
19
|
|
139
20
|
def write(obj, *tags)
|
@@ -143,19 +24,30 @@ class Drip
|
|
143
24
|
def write_after(at, *value)
|
144
25
|
make_key(at) do |key|
|
145
26
|
value = do_write(key, value)
|
146
|
-
@pool[key
|
27
|
+
@pool.push([key, @store.write(key, value)])
|
147
28
|
end
|
148
29
|
end
|
149
30
|
|
150
31
|
def write_at(at, *value)
|
151
32
|
make_key_at(at) do |key|
|
152
33
|
value = do_write(key, value)
|
153
|
-
@pool[key
|
34
|
+
@pool.push([key, @store.write(key, value)])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_if_latest(cond, *value)
|
39
|
+
make_key(Time.now) do |key|
|
40
|
+
cond.each {|it|
|
41
|
+
return nil unless latest?(it[1], it[0])
|
42
|
+
}
|
43
|
+
value = do_write(key, value)
|
44
|
+
@pool.push([key, @store.write(key, value)])
|
154
45
|
end
|
155
46
|
end
|
156
47
|
|
157
48
|
def fetch(key)
|
158
|
-
@
|
49
|
+
return @past.fetch(key) if @fence >= key
|
50
|
+
@pool.fetch(key)
|
159
51
|
end
|
160
52
|
alias [] fetch
|
161
53
|
|
@@ -171,6 +63,52 @@ class Drip
|
|
171
63
|
end
|
172
64
|
|
173
65
|
def read(key, n=1, at_least=1, timeout=nil)
|
66
|
+
return curr_read(key, n, at_least, timeout) if key > @fence
|
67
|
+
ary = @past.read(key, n)
|
68
|
+
return ary if ary.size >= n
|
69
|
+
ary + curr_read(key, n - ary.size, at_least - ary.size, timeout)
|
70
|
+
end
|
71
|
+
|
72
|
+
def read_tag(key, tag, n=1, at_least=1, timeout=nil)
|
73
|
+
return curr_read_tag(key, tag, n, at_least, timeout) if key > @fence
|
74
|
+
ary = @past.read_tag(key, tag, n)
|
75
|
+
return ary if ary.size >= n
|
76
|
+
ary + curr_read_tag(key, tag, n - ary.size, at_least - ary.size, timeout)
|
77
|
+
end
|
78
|
+
|
79
|
+
def head(n=1, tag=nil)
|
80
|
+
unless tag
|
81
|
+
ary = @pool.head(n)
|
82
|
+
return @past.head(n - ary.size) + ary
|
83
|
+
end
|
84
|
+
ary = curr_head(n, tag)
|
85
|
+
return ary if ary.size == n
|
86
|
+
@past.head(n - ary.size, tag) + ary
|
87
|
+
end
|
88
|
+
|
89
|
+
def latest?(key, tag=nil)
|
90
|
+
now = time_to_key(Time.now)
|
91
|
+
if tag
|
92
|
+
it ,= @tag.upper_bound([tag, now])
|
93
|
+
if it && it[0] == tag
|
94
|
+
return true if it[1] == key
|
95
|
+
return false if it[1] > key
|
96
|
+
end
|
97
|
+
else
|
98
|
+
return true if @pool.latest?(key)
|
99
|
+
end
|
100
|
+
@past.latest?(key, tag)
|
101
|
+
end
|
102
|
+
|
103
|
+
def older(key, tag=nil)
|
104
|
+
curr_older(key, tag) || @past.older(key, tag)
|
105
|
+
end
|
106
|
+
|
107
|
+
def newer(key, tag=nil)
|
108
|
+
@past.newer(key, tag) || curr_newer(key, tag)
|
109
|
+
end
|
110
|
+
|
111
|
+
def curr_read(key, n=1, at_least=1, timeout=nil)
|
174
112
|
renewer = make_renewer(timeout)
|
175
113
|
key = time_to_key(Time.now) unless key
|
176
114
|
ary = []
|
@@ -180,14 +118,14 @@ class Drip
|
|
180
118
|
rescue Rinda::RequestExpiredError
|
181
119
|
return ary
|
182
120
|
end
|
183
|
-
key, value = @pool.
|
121
|
+
key, value, *tags = @pool.read(key)[0]
|
184
122
|
return ary unless key
|
185
|
-
ary << [key] + value
|
123
|
+
ary << [key] + [value, *tags]
|
186
124
|
end
|
187
125
|
ary
|
188
126
|
end
|
189
127
|
|
190
|
-
def
|
128
|
+
def curr_read_tag(key, tag, n=1, at_least=1, timeout=nil)
|
191
129
|
renewer = make_renewer(timeout)
|
192
130
|
key = time_to_key(Time.now) unless key
|
193
131
|
ary = []
|
@@ -205,10 +143,10 @@ class Drip
|
|
205
143
|
ary
|
206
144
|
end
|
207
145
|
|
208
|
-
def
|
146
|
+
def curr_head(n=1, tag=nil)
|
209
147
|
ary = []
|
210
148
|
key = nil
|
211
|
-
while it =
|
149
|
+
while it = curr_older(key, tag)
|
212
150
|
break if n <= 0
|
213
151
|
ary.unshift(it)
|
214
152
|
key = it[0]
|
@@ -217,27 +155,29 @@ class Drip
|
|
217
155
|
ary
|
218
156
|
end
|
219
157
|
|
220
|
-
def
|
158
|
+
def curr_older(key, tag=nil)
|
221
159
|
key = time_to_key(Time.now) unless key
|
222
|
-
|
223
|
-
|
224
|
-
return k ? [k] + v.to_a : nil
|
225
|
-
end
|
160
|
+
|
161
|
+
return @pool.older(key) unless tag
|
226
162
|
|
227
163
|
it ,= @tag.upper_bound([tag, key - 1])
|
228
164
|
return nil unless it && it[0] == tag
|
229
165
|
[it[1]] + fetch(it[1])
|
230
166
|
end
|
231
167
|
|
232
|
-
def
|
168
|
+
def curr_newer(key, tag=nil)
|
233
169
|
return read(key, 1, 0)[0] unless tag
|
234
170
|
read_tag(key, tag, 1, 0)[0]
|
235
171
|
end
|
236
172
|
|
237
|
-
def time_to_key(time)
|
173
|
+
def self.time_to_key(time)
|
238
174
|
time.tv_sec * 1000000 + time.tv_usec
|
239
175
|
end
|
240
176
|
|
177
|
+
def time_to_key(time)
|
178
|
+
self.class.time_to_key(time)
|
179
|
+
end
|
180
|
+
|
241
181
|
def key_to_time(key)
|
242
182
|
Time.at(*key.divmod(1000000))
|
243
183
|
end
|
@@ -314,7 +254,7 @@ class Drip
|
|
314
254
|
def prepare_store(dir, option={})
|
315
255
|
if dir.nil?
|
316
256
|
@store = SimpleStore.new(nil, option)
|
317
|
-
return
|
257
|
+
return ImmutableDrip.new
|
318
258
|
end
|
319
259
|
|
320
260
|
Dir.mkdir(dir) rescue nil
|
@@ -322,26 +262,31 @@ class Drip
|
|
322
262
|
File.basename(fn).to_i(36)
|
323
263
|
end
|
324
264
|
if dump
|
325
|
-
|
326
|
-
@event.take([:last, nil])
|
327
|
-
@event.write([:last, last])
|
265
|
+
pool, tag, _ = File.open(dump, 'rb') {|fp| Marshal.load(fp)}
|
328
266
|
File.unlink(dump)
|
267
|
+
else
|
268
|
+
pool = []
|
269
|
+
tag = []
|
329
270
|
end
|
271
|
+
gen = ImmutableDrip::Generator.new(pool, tag)
|
330
272
|
loaded = dump ? File.basename(dump).to_i(36) : 0
|
331
273
|
Dir.glob(File.join(dir, '*.log')) do |fn|
|
332
274
|
next if loaded > File.basename(fn).to_i(36)
|
333
275
|
begin
|
334
|
-
|
335
|
-
|
276
|
+
SimpleStore.reader(fn).each do |k, v, attic|
|
277
|
+
obj, *tags = v
|
278
|
+
attic.forget
|
279
|
+
gen.add(k, attic, *tags)
|
280
|
+
end
|
336
281
|
rescue
|
337
282
|
end
|
338
283
|
end
|
339
|
-
name = time_to_key(Time.now).to_s(36)
|
340
|
-
_, last = @event.read([:last, nil])
|
284
|
+
name = Drip.time_to_key(Time.now).to_s(36)
|
341
285
|
File.open(File.join(dir, name + '.dump'), 'wb') {|fp|
|
342
|
-
Marshal.dump([
|
286
|
+
Marshal.dump([gen.pool, gen.tag], fp)
|
343
287
|
}
|
344
288
|
@store = SimpleStore.new(File.join(dir, name + '.log'))
|
289
|
+
return gen.generate
|
345
290
|
end
|
346
291
|
|
347
292
|
def shared_text(str)
|
@@ -361,7 +306,8 @@ class Drip
|
|
361
306
|
tag = shared_text(k)
|
362
307
|
@tag[[tag, key]] = key
|
363
308
|
end
|
364
|
-
@pool[key] = [obj] + tags
|
309
|
+
# @pool[key] = [obj] + tags
|
310
|
+
[obj] + tags
|
365
311
|
end
|
366
312
|
|
367
313
|
def restore(store)
|
@@ -400,7 +346,6 @@ class Drip
|
|
400
346
|
@event.write([:last, last])
|
401
347
|
end
|
402
348
|
|
403
|
-
INF = 1.0/0.0
|
404
349
|
def wait(key, renewer)
|
405
350
|
@event.read([:last, key+1 .. INF], renewer)[1]
|
406
351
|
end
|
@@ -425,6 +370,223 @@ class Drip
|
|
425
370
|
end
|
426
371
|
end
|
427
372
|
|
373
|
+
class Drip
|
374
|
+
module ArrayBsearch
|
375
|
+
module_function
|
376
|
+
def lower_boundary(ary, key)
|
377
|
+
lower = -1
|
378
|
+
upper = ary.size
|
379
|
+
while lower + 1 != upper
|
380
|
+
mid = (lower + upper).div(2)
|
381
|
+
if (ary[mid][0] <=> key) < 0
|
382
|
+
lower = mid
|
383
|
+
else
|
384
|
+
upper = mid
|
385
|
+
end
|
386
|
+
end
|
387
|
+
return upper
|
388
|
+
end
|
389
|
+
|
390
|
+
def upper_boundary(ary, key)
|
391
|
+
lower = -1
|
392
|
+
upper = ary.size
|
393
|
+
while lower + 1 != upper
|
394
|
+
mid = (lower + upper).div(2)
|
395
|
+
if (ary[mid][0] <=> key) <= 0
|
396
|
+
lower = mid
|
397
|
+
else
|
398
|
+
upper = mid
|
399
|
+
end
|
400
|
+
end
|
401
|
+
return lower + 1
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
class Drip
|
407
|
+
class FakeRBTree
|
408
|
+
include Drip::ArrayBsearch
|
409
|
+
|
410
|
+
def initialize
|
411
|
+
@tree = Hash.new {|h, k| h[k] = Array.new}
|
412
|
+
end
|
413
|
+
|
414
|
+
def upper_bound(pair)
|
415
|
+
tag, now = pair
|
416
|
+
return nil unless @tree.include?(tag)
|
417
|
+
ary = @tree[tag]
|
418
|
+
idx = upper_boundary(ary, now)
|
419
|
+
[tag, ary[tag][idx]]
|
420
|
+
end
|
421
|
+
|
422
|
+
def lower_bound(pair)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
|
429
|
+
class Drip
|
430
|
+
class SortedArray
|
431
|
+
include Drip::ArrayBsearch
|
432
|
+
|
433
|
+
def initialize(ary)
|
434
|
+
@ary = ary
|
435
|
+
@last_key = ary.empty? ? 0 : ary[-1][0]
|
436
|
+
end
|
437
|
+
|
438
|
+
def push(obj)
|
439
|
+
raise 'InvalidTimeError' if obj[0] <= @last_key
|
440
|
+
@last_key = obj[0]
|
441
|
+
@ary << obj
|
442
|
+
end
|
443
|
+
|
444
|
+
def fetch(key)
|
445
|
+
idx = lower_boundary(@ary, key)
|
446
|
+
k, v = @ary[idx]
|
447
|
+
k == key ? v.to_a : nil
|
448
|
+
end
|
449
|
+
|
450
|
+
def read(key, n=1)
|
451
|
+
idx = lower_boundary(@ary, key + 1)
|
452
|
+
return [] unless idx
|
453
|
+
@ary[idx, n].collect {|kv|
|
454
|
+
[kv[0], *kv[1].to_a]
|
455
|
+
}
|
456
|
+
end
|
457
|
+
|
458
|
+
def empty?
|
459
|
+
@ary.empty?
|
460
|
+
end
|
461
|
+
|
462
|
+
def latest?(key)
|
463
|
+
@last_key == key
|
464
|
+
end
|
465
|
+
|
466
|
+
def head(n)
|
467
|
+
n = @ary.size < n ? @ary.size : n
|
468
|
+
@ary[-n, n].collect {|kv|
|
469
|
+
[kv[0], *kv[1].to_a]
|
470
|
+
}
|
471
|
+
end
|
472
|
+
|
473
|
+
def older(key)
|
474
|
+
return nil if @ary.empty?
|
475
|
+
key = @ary[-1][0] + 1 unless key
|
476
|
+
idx = upper_boundary(@ary, key - 1)
|
477
|
+
k, v = @ary[idx - 1]
|
478
|
+
k && k < key ? [k, *v.to_a] : nil
|
479
|
+
end
|
480
|
+
|
481
|
+
def last
|
482
|
+
@ary[-1]
|
483
|
+
end
|
484
|
+
|
485
|
+
def last_key
|
486
|
+
@ary.empty? ? 0 : last[0]
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
class Drip
|
492
|
+
class ImmutableDrip
|
493
|
+
include Drip::ArrayBsearch
|
494
|
+
|
495
|
+
def initialize(pool=[], tag=[])
|
496
|
+
@pool = Drip::SortedArray.new(pool)
|
497
|
+
@tag = tag
|
498
|
+
end
|
499
|
+
|
500
|
+
def fetch(key)
|
501
|
+
@pool.fetch(key)
|
502
|
+
end
|
503
|
+
|
504
|
+
def read(key, n=1)
|
505
|
+
@pool.read(key, n)
|
506
|
+
end
|
507
|
+
|
508
|
+
def read_tag(key, tag, n=1)
|
509
|
+
idx = lower_boundary(@tag, [tag, key + 1])
|
510
|
+
return [] unless idx
|
511
|
+
@tag[idx, n].find_all {|kv| kv[0][0] == tag}.collect {|kv|
|
512
|
+
[kv[0][1], *fetch(kv[0][1])]
|
513
|
+
}
|
514
|
+
end
|
515
|
+
|
516
|
+
def latest?(key, tag)
|
517
|
+
return false if @pool.empty?
|
518
|
+
return @pool.latest?(key) unless tag
|
519
|
+
|
520
|
+
lower = lower_boundary(@tag, [tag, key])
|
521
|
+
upper = upper_boundary(@tag, [tag, INF])
|
522
|
+
return lower == upper - 1
|
523
|
+
end
|
524
|
+
|
525
|
+
def head_tag(n, tag)
|
526
|
+
lower = lower_boundary(@tag, [tag, 0])
|
527
|
+
upper = upper_boundary(@tag, [tag, INF])
|
528
|
+
lower = [lower, upper - n].max
|
529
|
+
@tag[lower ... upper].collect {|kv|
|
530
|
+
[kv[0][1], *fetch(kv[0][1])]
|
531
|
+
}
|
532
|
+
end
|
533
|
+
|
534
|
+
def head(n=1, tag=nil)
|
535
|
+
tag ? head_tag(n, tag) : @pool.head(n)
|
536
|
+
end
|
537
|
+
|
538
|
+
def older_tag(key, tag)
|
539
|
+
idx = upper_boundary(@tag, [tag, key-1])
|
540
|
+
k, v = @tag[idx - 1]
|
541
|
+
k && k[0] == tag ? [k[1], *fetch(k[1])] : nil
|
542
|
+
end
|
543
|
+
|
544
|
+
def older(key, tag=nil)
|
545
|
+
return nil if @pool.empty?
|
546
|
+
key = @pool.last_key + 1 unless key
|
547
|
+
return older_tag(key, tag) if tag
|
548
|
+
@pool.older(key)
|
549
|
+
end
|
550
|
+
|
551
|
+
def newer(key, tag=nil)
|
552
|
+
return read(key, 1)[0] unless tag
|
553
|
+
read_tag(key, tag, 1)[0]
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
class Drip
|
559
|
+
class ImmutableDrip
|
560
|
+
class Generator
|
561
|
+
def initialize(pool=[], tag=[])
|
562
|
+
@pool = pool
|
563
|
+
@tag = tag
|
564
|
+
@shared = Hash.new {|h, k| h[k] = k; k}
|
565
|
+
@tag.each {|pair| @shared[pair[0]]}
|
566
|
+
end
|
567
|
+
attr_reader :pool, :tag
|
568
|
+
|
569
|
+
def add(key, value, *tag)
|
570
|
+
@pool << [key, value]
|
571
|
+
idx = @pool.size - 1
|
572
|
+
tag.uniq.each do |t|
|
573
|
+
@tag << [[@shared[t], key]]
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def generate
|
578
|
+
tag = @tag.sort
|
579
|
+
tag.inject(nil) do |last, kv|
|
580
|
+
k = kv[0]
|
581
|
+
k[0] = last if k[0] == last
|
582
|
+
k[0]
|
583
|
+
end
|
584
|
+
ImmutableDrip.new(@pool.sort, tag)
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
428
590
|
if __FILE__ == $0
|
429
591
|
require 'my_drip'
|
430
592
|
MyDrip.invoke
|
data/lib/drip/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
class Drip
|
2
|
+
VERSION = "0.0.3"
|
3
3
|
end
|
data/sample/drip_tw.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require 'simple-oauth'
|
2
3
|
require 'drb'
|
3
4
|
require 'pp'
|
@@ -36,6 +37,40 @@ class DripFiber
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
40
|
+
class DripThread
|
41
|
+
def initialize(app)
|
42
|
+
@app = app
|
43
|
+
@queue = Queue.new
|
44
|
+
Thread.new do
|
45
|
+
story
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def story
|
50
|
+
event = @queue.pop
|
51
|
+
pending = []
|
52
|
+
while event['id_str'].nil?
|
53
|
+
pending << event
|
54
|
+
event = @queue.pop
|
55
|
+
end
|
56
|
+
|
57
|
+
@app.fill_timeline(event['id_str'])
|
58
|
+
|
59
|
+
while event = pending.shift
|
60
|
+
@app.write(event)
|
61
|
+
end
|
62
|
+
|
63
|
+
while true
|
64
|
+
event = @queue.pop
|
65
|
+
@app.write(event)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def push(event)
|
70
|
+
@queue.push(event)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
39
74
|
class JSONStream
|
40
75
|
def initialize(drip)
|
41
76
|
@buf = ''
|
@@ -49,8 +84,10 @@ class JSONStream
|
|
49
84
|
@buf.sub!(line,"")
|
50
85
|
line.strip!
|
51
86
|
event = JSON.parse(line)
|
52
|
-
rescue
|
53
|
-
|
87
|
+
rescue JSON::ParserError
|
88
|
+
pp $line if $DEBUG
|
89
|
+
pp $! if $DEBUG
|
90
|
+
next
|
54
91
|
end
|
55
92
|
pp event if $DEBUG
|
56
93
|
@drip.push(event)
|
@@ -147,15 +184,16 @@ class DripDemo
|
|
147
184
|
end
|
148
185
|
|
149
186
|
def home_timeline(since_id, max_id)
|
150
|
-
url = "
|
187
|
+
url = "https://api.twitter.com/1.1/statuses/home_timeline.json?count=200&include_entities=true"
|
151
188
|
url += "&since_id=#{since_id}" if since_id
|
152
189
|
url += "&max_id=#{max_id}" if max_id
|
153
190
|
r = oauth.request(:GET, url)
|
154
|
-
|
191
|
+
body = r.body
|
192
|
+
JSON.parse(body)
|
155
193
|
end
|
156
194
|
|
157
195
|
def user_timeline(since_id, max_id)
|
158
|
-
url = "http://api.twitter.com/1/statuses/user_timeline.json?count=200&include_entities=true&trim_user=t"
|
196
|
+
url = "http://api.twitter.com/1.1/statuses/user_timeline.json?count=200&include_entities=true&trim_user=t"
|
159
197
|
url += "&since_id=#{since_id}" if since_id
|
160
198
|
url += "&max_id=#{max_id}" if max_id
|
161
199
|
r = oauth.request(:GET, url)
|
@@ -225,13 +263,13 @@ class DripDemo
|
|
225
263
|
def update(str, in_reply_to=nil)
|
226
264
|
hash = { :status => str }
|
227
265
|
hash[:in_reply_to_status_id] = in_reply_to if in_reply_to
|
228
|
-
r = oauth.post('http://api.twitter.com/1/statuses/update.xml',
|
266
|
+
r = oauth.post('http://api.twitter.com/1.1/statuses/update.xml',
|
229
267
|
hash)
|
230
268
|
pp r.body if $DEBUG
|
231
269
|
end
|
232
270
|
|
233
271
|
def test
|
234
|
-
r = oauth.post('http://api.twitter.com/1/statuses/update.xml',
|
272
|
+
r = oauth.post('http://api.twitter.com/1.1/statuses/update.xml',
|
235
273
|
{:status => 'test'})
|
236
274
|
pp r.body if $DEBUG
|
237
275
|
end
|
data/test/basic.rb
CHANGED
@@ -124,6 +124,36 @@ class TestDrip < Test::Unit::TestCase
|
|
124
124
|
oid = @drip.write('dup', 'hello', 'hello', 'hello')
|
125
125
|
assert_equal(@drip[oid], ['dup', 'hello'])
|
126
126
|
end
|
127
|
+
|
128
|
+
def test_latest?
|
129
|
+
key = @drip.write(:start)
|
130
|
+
10.times do |n|
|
131
|
+
@drip.write(n)
|
132
|
+
end
|
133
|
+
assert_equal(@drip.latest?(key), false)
|
134
|
+
key = @drip.write(:stop)
|
135
|
+
assert_equal(@drip.latest?(key), true)
|
136
|
+
|
137
|
+
key = @drip.write(:tag_start, 'tag')
|
138
|
+
@drip.write(:tag, 'ignore tag')
|
139
|
+
assert_equal(@drip.latest?(key, 'tag'), true)
|
140
|
+
@drip.write(:tag, 'tag')
|
141
|
+
assert_equal(@drip.latest?(key, 'tag'), false)
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_write_if_latest
|
145
|
+
t1 = @drip.write('t1', 't1')
|
146
|
+
t2 = @drip.write('t2', 't2')
|
147
|
+
t3 = @drip.write('t3', 't3')
|
148
|
+
assert_equal(@drip.latest?(t1, 't1'), true)
|
149
|
+
assert(@drip.write_if_latest([['t1', t1],
|
150
|
+
['t2', t2],
|
151
|
+
['t3', t3]], 'hello', 't1'))
|
152
|
+
assert_equal(@drip.latest?(t1, 't1'), false)
|
153
|
+
assert_equal(@drip.write_if_latest([['t1', t1],
|
154
|
+
['t2', t2],
|
155
|
+
['t3', t3]], 'hello', 't1'), nil)
|
156
|
+
end
|
127
157
|
end
|
128
158
|
|
129
159
|
class TestDripUsingStorage < TestDrip
|
@@ -139,13 +169,36 @@ class TestDripUsingStorage < TestDrip
|
|
139
169
|
def teardown
|
140
170
|
remove_drip
|
141
171
|
end
|
172
|
+
|
173
|
+
def test_twice_latest?
|
174
|
+
assert_equal(@drip.latest?(1), false)
|
175
|
+
tag1 = @drip.write('tag1', 'tag1')
|
176
|
+
assert_equal(@drip.latest?(tag1), true)
|
177
|
+
@drip.write('nop', 'tag1')
|
178
|
+
@drip.write('nop', 'tag1')
|
179
|
+
tag2 = @drip.write('tag2', 'tag1')
|
180
|
+
assert_equal(@drip.latest?(1), false)
|
181
|
+
drip = Drip.new('test_db')
|
182
|
+
assert_equal(drip.latest?(1), false)
|
183
|
+
assert_equal(drip.latest?(tag1, 'tag1'), false)
|
184
|
+
assert_equal(drip.latest?(tag2, 'tag1'), true)
|
185
|
+
assert_equal(drip.latest?(tag2, 'tag0'), false)
|
186
|
+
end
|
142
187
|
|
143
188
|
def test_twice
|
144
189
|
11.times do |n|
|
145
190
|
@drip.write("n=#{n}" => 'x' * n, n => n, "n" => n, :symbol => n)
|
146
191
|
end
|
147
|
-
|
192
|
+
|
148
193
|
drip = Drip.new('test_db')
|
194
|
+
ary = drip.head(3)
|
195
|
+
assert_equal(ary.size, 3)
|
196
|
+
assert_equal(ary[0][1]['n'], 8)
|
197
|
+
assert_equal(ary[1][1]['n'], 9)
|
198
|
+
assert_equal(ary[2][1]['n'], 10)
|
199
|
+
ary = drip.head(1)
|
200
|
+
assert_equal(ary.size, 1)
|
201
|
+
assert_equal(ary[0][1]['n'], 10)
|
149
202
|
ary = drip.read(0, 3)
|
150
203
|
assert_equal(ary.size, 3)
|
151
204
|
assert_equal(ary[0][1]['n'], 0)
|
@@ -175,30 +228,30 @@ end
|
|
175
228
|
|
176
229
|
class TestImmutableDrip < Test::Unit::TestCase
|
177
230
|
def test_bsearch
|
178
|
-
|
231
|
+
ab = Drip::ArrayBsearch
|
179
232
|
|
180
|
-
assert_equal(0,
|
181
|
-
assert_equal(0,
|
233
|
+
assert_equal(0, ab.lower_boundary([], 'c'))
|
234
|
+
assert_equal(0, ab.upper_boundary([], 'c'))
|
182
235
|
|
183
236
|
ary = %w(a b c c c d e f).collect {|x| [x]}
|
184
237
|
|
185
|
-
assert_equal(0,
|
186
|
-
assert_equal(0,
|
187
|
-
assert_equal(1,
|
188
|
-
assert_equal(2,
|
189
|
-
assert_equal(5,
|
190
|
-
assert_equal(6,
|
191
|
-
assert_equal(7,
|
192
|
-
assert_equal(8,
|
238
|
+
assert_equal(0, ab.lower_boundary(ary, ''))
|
239
|
+
assert_equal(0, ab.lower_boundary(ary, 'a'))
|
240
|
+
assert_equal(1, ab.lower_boundary(ary, 'b'))
|
241
|
+
assert_equal(2, ab.lower_boundary(ary, 'c'))
|
242
|
+
assert_equal(5, ab.lower_boundary(ary, 'd'))
|
243
|
+
assert_equal(6, ab.lower_boundary(ary, 'e'))
|
244
|
+
assert_equal(7, ab.lower_boundary(ary, 'f'))
|
245
|
+
assert_equal(8, ab.lower_boundary(ary, 'g'))
|
193
246
|
|
194
|
-
assert_equal(0,
|
195
|
-
assert_equal(1,
|
196
|
-
assert_equal(2,
|
197
|
-
assert_equal(5,
|
198
|
-
assert_equal(6,
|
199
|
-
assert_equal(7,
|
200
|
-
assert_equal(8,
|
201
|
-
assert_equal(8,
|
247
|
+
assert_equal(0, ab.upper_boundary(ary, ''))
|
248
|
+
assert_equal(1, ab.upper_boundary(ary, 'a'))
|
249
|
+
assert_equal(2, ab.upper_boundary(ary, 'b'))
|
250
|
+
assert_equal(5, ab.upper_boundary(ary, 'c'))
|
251
|
+
assert_equal(6, ab.upper_boundary(ary, 'd'))
|
252
|
+
assert_equal(7, ab.upper_boundary(ary, 'e'))
|
253
|
+
assert_equal(8, ab.upper_boundary(ary, 'f'))
|
254
|
+
assert_equal(8, ab.upper_boundary(ary, 'g'))
|
202
255
|
end
|
203
256
|
|
204
257
|
def add_to_gen(gen, key, value, *tag)
|
metadata
CHANGED
@@ -1,38 +1,36 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: drip
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: 0.0.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
6
5
|
platform: ruby
|
7
|
-
authors:
|
6
|
+
authors:
|
8
7
|
- Masatoshi Seki
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2015-12-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
16
14
|
name: rbtree
|
17
|
-
|
18
|
-
|
19
|
-
none: false
|
20
|
-
requirements:
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
21
17
|
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version:
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
24
20
|
type: :runtime
|
25
|
-
|
26
|
-
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: ''
|
27
28
|
email:
|
28
29
|
executables: []
|
29
|
-
|
30
30
|
extensions: []
|
31
|
-
|
32
31
|
extra_rdoc_files: []
|
33
|
-
|
34
|
-
|
35
|
-
- .gitignore
|
32
|
+
files:
|
33
|
+
- ".gitignore"
|
36
34
|
- Gemfile
|
37
35
|
- README
|
38
36
|
- Rakefile
|
@@ -58,30 +56,26 @@ files:
|
|
58
56
|
- test/basic.rb
|
59
57
|
homepage: https://github.com/seki/Drip
|
60
58
|
licenses: []
|
61
|
-
|
59
|
+
metadata: {}
|
62
60
|
post_install_message:
|
63
61
|
rdoc_options: []
|
64
|
-
|
65
|
-
require_paths:
|
62
|
+
require_paths:
|
66
63
|
- lib
|
67
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
-
|
69
|
-
requirements:
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
70
66
|
- - ">="
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
version:
|
73
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
-
|
75
|
-
requirements:
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
76
71
|
- - ">="
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
version:
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
79
74
|
requirements: []
|
80
|
-
|
81
75
|
rubyforge_project: drip
|
82
|
-
rubygems_version:
|
76
|
+
rubygems_version: 2.5.1
|
83
77
|
signing_key:
|
84
|
-
specification_version:
|
78
|
+
specification_version: 4
|
85
79
|
summary: Simple RD-Stream for Rinda::TupleSpace lovers.
|
86
|
-
test_files:
|
80
|
+
test_files:
|
87
81
|
- test/basic.rb
|