drip 0.0.2 → 0.0.3

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: 029a7c4fb43e4852aca41b6f1a2ba481062cec47
4
+ data.tar.gz: d1c6ca3d3291e2cf73171e345125bfb11e06b8dc
5
+ SHA512:
6
+ metadata.gz: 4c0c2bf53465abe23f7b56b51172d0a107477882f037ecb2dddc75bd18d1f94e34797983fd51bf3b99495175b464fb6699f924f15955b7a5645d83e3780071ef
7
+ data.tar.gz: 9e057c27351793c8f95efc022c64baa52faca161cfec6048dca62c37f14ad574ffcd722497a6afad4b89e05f4b19fb92980357048506c613a4c7e4213d559aba
@@ -5,135 +5,16 @@ require 'enumerator'
5
5
 
6
6
  class Drip
7
7
  include DRbUndumped
8
- def inspect; to_s; end
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
- @pool = RBTree.new
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, 0])
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] = @store.write(key, value)
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] = @store.write(key, value)
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
- @pool[key].to_a
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.lower_bound(key + 1)
121
+ key, value, *tags = @pool.read(key)[0]
184
122
  return ary unless key
185
- ary << [key] + value.to_a
123
+ ary << [key] + [value, *tags]
186
124
  end
187
125
  ary
188
126
  end
189
127
 
190
- def read_tag(key, tag, n=1, at_least=1, timeout=nil)
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 head(n=1, tag=nil)
146
+ def curr_head(n=1, tag=nil)
209
147
  ary = []
210
148
  key = nil
211
- while it = older(key, tag)
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 older(key, tag=nil)
158
+ def curr_older(key, tag=nil)
221
159
  key = time_to_key(Time.now) unless key
222
- unless tag
223
- k, v = @pool.upper_bound(key - 1)
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 newer(key, tag=nil)
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
- @pool, @tag, last = File.open(dump, 'rb') {|fp| Marshal.load(fp)}
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
- store = SimpleStore.reader(fn)
335
- restore(store)
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([@pool, @tag, last], fp)
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
@@ -1,3 +1,3 @@
1
- module Drip
2
- VERSION = "0.0.2"
1
+ class Drip
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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
- break
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 = "http://api.twitter.com/1/statuses/home_timeline.json?count=200&include_entities=true"
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
- JSON.parse(r.body)
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
@@ -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
- im = Drip::ImmutableDrip.new
231
+ ab = Drip::ArrayBsearch
179
232
 
180
- assert_equal(0, im.lower_boundary([], 'c'))
181
- assert_equal(0, im.upper_boundary([], 'c'))
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, im.lower_boundary(ary, ''))
186
- assert_equal(0, im.lower_boundary(ary, 'a'))
187
- assert_equal(1, im.lower_boundary(ary, 'b'))
188
- assert_equal(2, im.lower_boundary(ary, 'c'))
189
- assert_equal(5, im.lower_boundary(ary, 'd'))
190
- assert_equal(6, im.lower_boundary(ary, 'e'))
191
- assert_equal(7, im.lower_boundary(ary, 'f'))
192
- assert_equal(8, im.lower_boundary(ary, 'g'))
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, im.upper_boundary(ary, ''))
195
- assert_equal(1, im.upper_boundary(ary, 'a'))
196
- assert_equal(2, im.upper_boundary(ary, 'b'))
197
- assert_equal(5, im.upper_boundary(ary, 'c'))
198
- assert_equal(6, im.upper_boundary(ary, 'd'))
199
- assert_equal(7, im.upper_boundary(ary, 'e'))
200
- assert_equal(8, im.upper_boundary(ary, 'f'))
201
- assert_equal(8, im.upper_boundary(ary, 'g'))
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
- prerelease:
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
- date: 2011-11-16 00:00:00 Z
14
- dependencies:
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
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
- requirements:
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
21
17
  - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
24
20
  type: :runtime
25
- version_requirements: *id001
26
- description: ""
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
- files:
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
- none: false
69
- requirements:
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
70
66
  - - ">="
71
- - !ruby/object:Gem::Version
72
- version: "0"
73
- required_rubygems_version: !ruby/object:Gem::Requirement
74
- none: false
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: "0"
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
79
74
  requirements: []
80
-
81
75
  rubyforge_project: drip
82
- rubygems_version: 1.8.10
76
+ rubygems_version: 2.5.1
83
77
  signing_key:
84
- specification_version: 3
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