bud 0.9.7 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/History.txt +18 -0
- data/Rakefile +91 -0
- data/bin/budplot +7 -2
- data/docs/README.md +8 -17
- data/docs/cheat.md +1 -1
- data/docs/getstarted.md +95 -82
- data/docs/operational.md +3 -3
- data/examples/basics/paths.rb +2 -2
- data/examples/chat/chat_protocol.rb +1 -1
- data/lib/bud.rb +67 -51
- data/lib/bud/bud_meta.rb +64 -42
- data/lib/bud/collections.rb +29 -26
- data/lib/bud/executor/elements.rb +6 -6
- data/lib/bud/executor/join.rb +63 -52
- data/lib/bud/lattice-core.rb +5 -0
- data/lib/bud/monkeypatch.rb +38 -11
- data/lib/bud/rebl.rb +2 -2
- data/lib/bud/rewrite.rb +22 -11
- data/lib/bud/state.rb +2 -2
- data/lib/bud/storage/zookeeper.rb +7 -0
- data/lib/bud/version.rb +3 -0
- data/lib/bud/viz.rb +3 -3
- metadata +84 -82
data/lib/bud/collections.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
$struct_classes = {}
|
2
1
|
module Bud
|
3
2
|
########
|
4
3
|
#--
|
@@ -17,7 +16,7 @@ module Bud
|
|
17
16
|
attr_accessor :bud_instance # :nodoc: all
|
18
17
|
attr_reader :tabname, :cols, :key_cols # :nodoc: all
|
19
18
|
attr_reader :struct
|
20
|
-
attr_reader :
|
19
|
+
attr_reader :new_delta, :pending # :nodoc: all
|
21
20
|
attr_reader :wired_by, :scanner_cnt
|
22
21
|
attr_accessor :invalidated, :rescan
|
23
22
|
attr_accessor :is_source
|
@@ -63,7 +62,7 @@ module Bud
|
|
63
62
|
if @cols.empty?
|
64
63
|
@cols = nil
|
65
64
|
else
|
66
|
-
@struct =
|
65
|
+
@struct = Bud::TupleStruct.new_struct(@cols)
|
67
66
|
@structlen = @struct.members.length
|
68
67
|
end
|
69
68
|
setup_accessors
|
@@ -250,7 +249,7 @@ module Bud
|
|
250
249
|
def sort(&blk)
|
251
250
|
if @bud_instance.wiring?
|
252
251
|
pusher = self.pro
|
253
|
-
pusher.sort("sort#{object_id}", @bud_instance, @cols, &blk)
|
252
|
+
pusher.sort("sort#{object_id}".to_sym, @bud_instance, @cols, &blk)
|
254
253
|
else
|
255
254
|
@storage.values.sort(&blk)
|
256
255
|
end
|
@@ -275,7 +274,12 @@ module Bud
|
|
275
274
|
|
276
275
|
public
|
277
276
|
def each_raw(&block)
|
278
|
-
@storage
|
277
|
+
each_from([@storage], &block)
|
278
|
+
end
|
279
|
+
|
280
|
+
public
|
281
|
+
def each_delta(&block)
|
282
|
+
each_from([@delta], &block)
|
279
283
|
end
|
280
284
|
|
281
285
|
public
|
@@ -301,37 +305,26 @@ module Bud
|
|
301
305
|
public
|
302
306
|
def tick_metrics
|
303
307
|
strat_num = bud_instance.this_stratum
|
304
|
-
rule_num = bud_instance.this_rule
|
305
|
-
addr = nil
|
306
308
|
addr = bud_instance.ip_port unless bud_instance.port.nil?
|
309
|
+
key = { :addr=>addr, :tabname=>qualified_tabname,
|
310
|
+
:strat_num=>strat_num}
|
311
|
+
|
307
312
|
bud_instance.metrics[:collections] ||= {}
|
308
|
-
bud_instance.metrics[:collections][
|
309
|
-
bud_instance.metrics[:collections][
|
313
|
+
bud_instance.metrics[:collections][key] ||= 0
|
314
|
+
bud_instance.metrics[:collections][key] += 1
|
310
315
|
end
|
311
316
|
|
312
317
|
private
|
313
318
|
def each_from(bufs, &block) # :nodoc: all
|
319
|
+
do_metrics = bud_instance.options[:metrics]
|
314
320
|
bufs.each do |b|
|
315
321
|
b.each_value do |v|
|
316
|
-
tick_metrics if
|
322
|
+
tick_metrics if do_metrics
|
317
323
|
yield v
|
318
324
|
end
|
319
325
|
end
|
320
326
|
end
|
321
327
|
|
322
|
-
public
|
323
|
-
def each_from_sym(buf_syms, &block) # :nodoc: all
|
324
|
-
bufs = buf_syms.map do |s|
|
325
|
-
case s
|
326
|
-
when :storage then @storage
|
327
|
-
when :delta then @delta
|
328
|
-
when :new_delta then @new_delta
|
329
|
-
else raise Bud::Error, "bad symbol passed into each_from_sym"
|
330
|
-
end
|
331
|
-
end
|
332
|
-
each_from(bufs, &block)
|
333
|
-
end
|
334
|
-
|
335
328
|
private
|
336
329
|
def init_storage
|
337
330
|
@storage = {}
|
@@ -374,7 +367,7 @@ module Bud
|
|
374
367
|
# checks for +item+ in the collection
|
375
368
|
public
|
376
369
|
def include?(item)
|
377
|
-
return true if key_cols.nil?
|
370
|
+
return true if key_cols.nil?
|
378
371
|
return false if item.nil?
|
379
372
|
key = get_key_vals(item)
|
380
373
|
return (item == self[key])
|
@@ -650,6 +643,11 @@ module Bud
|
|
650
643
|
end
|
651
644
|
end
|
652
645
|
|
646
|
+
superator "<~" do |o|
|
647
|
+
# Overridden when <~ is defined (i.e., channels and terminals)
|
648
|
+
raise Bud::CompileError, "#{tabname} cannot appear on the lhs of a <~ operator"
|
649
|
+
end
|
650
|
+
|
653
651
|
def tick
|
654
652
|
raise Bud::Error, "tick must be overriden in #{self.class}"
|
655
653
|
end
|
@@ -1111,6 +1109,11 @@ module Bud
|
|
1111
1109
|
return true
|
1112
1110
|
end
|
1113
1111
|
|
1112
|
+
public
|
1113
|
+
def bootstrap
|
1114
|
+
# override BudCollection; pending should not be moved into delta.
|
1115
|
+
end
|
1116
|
+
|
1114
1117
|
public
|
1115
1118
|
def flush #:nodoc: all
|
1116
1119
|
out_io = get_out_io
|
@@ -1257,7 +1260,7 @@ module Bud
|
|
1257
1260
|
|
1258
1261
|
def invalidated=(val)
|
1259
1262
|
# Might be reset to false at end-of-tick, but shouldn't be set to true
|
1260
|
-
raise Bud::Error, "cannot
|
1263
|
+
raise Bud::Error, "cannot set invalidate on table '#{@tabname}'" if val
|
1261
1264
|
super
|
1262
1265
|
end
|
1263
1266
|
|
@@ -1383,7 +1386,7 @@ module Bud
|
|
1383
1386
|
end
|
1384
1387
|
|
1385
1388
|
class BudFileReader < BudReadOnly # :nodoc: all
|
1386
|
-
def initialize(name, filename,
|
1389
|
+
def initialize(name, filename, bud_instance) # :nodoc: all
|
1387
1390
|
super(name, bud_instance, {[:lineno] => [:text]})
|
1388
1391
|
@filename = filename
|
1389
1392
|
@storage = {}
|
@@ -199,7 +199,7 @@ module Bud
|
|
199
199
|
public
|
200
200
|
def pro(the_name=elem_name, the_schema=schema, &blk)
|
201
201
|
toplevel = @bud_instance.toplevel
|
202
|
-
elem = Bud::PushElement.new("project#{object_id}",
|
202
|
+
elem = Bud::PushElement.new("project#{object_id}".to_sym,
|
203
203
|
toplevel.this_rule_context,
|
204
204
|
@collection_name, the_schema)
|
205
205
|
self.wire_to(elem)
|
@@ -213,7 +213,7 @@ module Bud
|
|
213
213
|
public
|
214
214
|
def each_with_index(&blk)
|
215
215
|
toplevel = @bud_instance.toplevel
|
216
|
-
elem = Bud::PushEachWithIndex.new("each_with_index#{object_id}",
|
216
|
+
elem = Bud::PushEachWithIndex.new("each_with_index#{object_id}".to_sym,
|
217
217
|
toplevel.this_rule_context,
|
218
218
|
@collection_name)
|
219
219
|
elem.set_block(&blk)
|
@@ -284,7 +284,7 @@ module Bud
|
|
284
284
|
|
285
285
|
aggpairs = prep_aggpairs(aggpairs)
|
286
286
|
toplevel = @bud_instance.toplevel
|
287
|
-
g = Bud::PushGroup.new(
|
287
|
+
g = Bud::PushGroup.new("grp#{Time.new.tv_usec}".to_sym, toplevel.this_rule_context,
|
288
288
|
@collection_name, keycols, aggpairs, the_schema, &blk)
|
289
289
|
self.wire_to(g)
|
290
290
|
toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]] = g
|
@@ -302,7 +302,7 @@ module Bud
|
|
302
302
|
end
|
303
303
|
|
304
304
|
aggpairs = [[agg, collection]]
|
305
|
-
aa = Bud::PushArgAgg.new(
|
305
|
+
aa = Bud::PushArgAgg.new("argagg#{Time.new.tv_usec}".to_sym, toplevel.this_rule_context,
|
306
306
|
@collection_name, gbkey_cols, aggpairs, schema, &blk)
|
307
307
|
self.wire_to(aa)
|
308
308
|
toplevel.push_elems[[self.object_id, :argagg, gbkey_cols, aggpairs, blk]] = aa
|
@@ -346,7 +346,7 @@ module Bud
|
|
346
346
|
end
|
347
347
|
|
348
348
|
def reduce(initial, &blk)
|
349
|
-
retval = Bud::PushReduce.new("reduce#{Time.new.tv_usec}",
|
349
|
+
retval = Bud::PushReduce.new("reduce#{Time.new.tv_usec}".to_sym,
|
350
350
|
@bud_instance, @collection_name,
|
351
351
|
schema, initial, &blk)
|
352
352
|
self.wire_to(retval)
|
@@ -498,7 +498,7 @@ module Bud
|
|
498
498
|
end
|
499
499
|
|
500
500
|
# send deltas out in all cases
|
501
|
-
@collection.
|
501
|
+
@collection.each_delta {|item| push_out(item)}
|
502
502
|
end
|
503
503
|
end
|
504
504
|
|
data/lib/bud/executor/join.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'bud/executor/elements'
|
2
2
|
|
3
|
-
$EMPTY = []
|
4
3
|
module Bud
|
5
4
|
class PushSHJoin < PushStatefulElement
|
6
5
|
attr_reader :all_rels_below, :origpreds, :relnames, :keys, :localpreds
|
@@ -41,18 +40,6 @@ module Bud
|
|
41
40
|
@selfjoins << name if cnt == 2
|
42
41
|
end
|
43
42
|
|
44
|
-
# derive schema: one column for each table.
|
45
|
-
# duplicated inputs get distinguishing numeral
|
46
|
-
@cols = []
|
47
|
-
retval = @all_rels_below.reduce({}) do |memo, r|
|
48
|
-
r_name = r.qualified_tabname.to_s
|
49
|
-
memo[r_name] ||= 0
|
50
|
-
newstr = r_name + (memo[r_name] > 0 ? "_#{memo[r_name]}" : "")
|
51
|
-
@cols << newstr.to_sym
|
52
|
-
memo[r_name] += 1
|
53
|
-
memo
|
54
|
-
end
|
55
|
-
|
56
43
|
setup_preds(preds) unless preds.empty?
|
57
44
|
setup_state
|
58
45
|
|
@@ -143,14 +130,6 @@ module Bud
|
|
143
130
|
if source_elem.rescan
|
144
131
|
puts "#{qualified_tabname} rel:#{i}(#{source_elem.qualified_tabname}) invalidated" if $BUD_DEBUG
|
145
132
|
@hash_tables[i] = {}
|
146
|
-
if i == 0
|
147
|
-
# Only if i == 0 because outer joins in Bloom are left outer joins.
|
148
|
-
# If i == 1, missing_keys will be corrected when items are populated
|
149
|
-
# in the rhs fork.
|
150
|
-
# XXX This is not modular. We are doing invalidation work for outer
|
151
|
-
# joins, which is part of a separate module PushSHOuterJoin.
|
152
|
-
@missing_keys.clear
|
153
|
-
end
|
154
133
|
end
|
155
134
|
end
|
156
135
|
end
|
@@ -165,7 +144,7 @@ module Bud
|
|
165
144
|
# referenced in entry.
|
166
145
|
subtuple = 0
|
167
146
|
all_rels_below[0..all_rels_below.length-1].each_with_index do |t,i|
|
168
|
-
if t.qualified_tabname ==
|
147
|
+
if t.qualified_tabname == name
|
169
148
|
subtuple = i
|
170
149
|
break
|
171
150
|
end
|
@@ -183,7 +162,7 @@ module Bud
|
|
183
162
|
elsif k.class <= Array
|
184
163
|
[k,v]
|
185
164
|
elsif k.class <= Symbol
|
186
|
-
if @all_rels_below
|
165
|
+
if @all_rels_below.length == 2
|
187
166
|
[find_attr_match(k, @all_rels_below[0]), find_attr_match(v, @all_rels_below[1])]
|
188
167
|
else
|
189
168
|
[find_attr_match(k), find_attr_match(v)]
|
@@ -235,9 +214,10 @@ module Bud
|
|
235
214
|
|
236
215
|
protected
|
237
216
|
def canonicalize_localpreds(rel_list, preds) # :nodoc:all
|
238
|
-
|
239
|
-
|
240
|
-
|
217
|
+
second_rel = rel_list[1].qualified_tabname
|
218
|
+
preds.map do |p|
|
219
|
+
# reverse if lhs is second_rel *unless* it's a self-join!
|
220
|
+
(p[0][0] == second_rel and p[0][0] != p[1][0]) ? p.reverse : p
|
241
221
|
end
|
242
222
|
end
|
243
223
|
|
@@ -251,16 +231,15 @@ module Bud
|
|
251
231
|
# again if we didn't rescan now.
|
252
232
|
replay_join if @rescan
|
253
233
|
|
254
|
-
|
234
|
+
source_tbl = source.qualified_tabname
|
235
|
+
if @selfjoins.include? source_tbl
|
255
236
|
offsets = []
|
256
|
-
@relnames.each_with_index{|r,i| offsets << i if r ==
|
237
|
+
@relnames.each_with_index{|r,i| offsets << i if r == source_tbl}
|
257
238
|
else
|
258
|
-
offsets = [@relnames.index(
|
259
|
-
end
|
260
|
-
raise Bud::Error, "item #{item.inspect} inserted into join from unknown source #{source.elem_name}" if offsets == $EMPTY
|
261
|
-
offsets.each do |offset|
|
262
|
-
insert_item(item, offset)
|
239
|
+
offsets = [@relnames.index(source_tbl)]
|
263
240
|
end
|
241
|
+
|
242
|
+
offsets.each {|offset| insert_item(item, offset)}
|
264
243
|
end
|
265
244
|
|
266
245
|
protected
|
@@ -332,14 +311,32 @@ module Bud
|
|
332
311
|
####
|
333
312
|
# and now, the Bloom-facing methods
|
334
313
|
# given a * expression over n collections, form all combinations of items
|
335
|
-
# subject to an array of predicates,
|
336
|
-
# currently supports two options for equijoin predicates:
|
314
|
+
# subject to an array of predicates, +preds+.
|
315
|
+
# currently supports two syntax options for equijoin predicates:
|
337
316
|
# general form: an array of arrays capturing a conjunction of equiv. classes
|
338
317
|
# [[table1.col1, table2.col2, table3.col3], [table1.col2, table2.col3]]
|
339
318
|
# common form: a hash capturing equality of a column on left with one on right.
|
340
319
|
# :col1 => :col2 (same as lefttable.col1 => righttable.col2)
|
341
320
|
public
|
342
321
|
def pairs(*preds, &blk)
|
322
|
+
if @cols.nil?
|
323
|
+
# derive schema if needed: one column for each table. duplicated inputs
|
324
|
+
# get distinguishing numeral.
|
325
|
+
#
|
326
|
+
# XXX: actually, this seems completely bogus. The schema for the output
|
327
|
+
# of the join should depend on the join's *targetlist*.
|
328
|
+
@cols = []
|
329
|
+
retval = @all_rels_below.reduce({}) do |memo, r|
|
330
|
+
r_name = r.qualified_tabname.to_s
|
331
|
+
memo[r_name] ||= 0
|
332
|
+
newstr = r_name + (memo[r_name] > 0 ? "_#{memo[r_name]}" : "")
|
333
|
+
@cols << newstr.to_sym
|
334
|
+
memo[r_name] += 1
|
335
|
+
memo
|
336
|
+
end
|
337
|
+
setup_accessors
|
338
|
+
end
|
339
|
+
|
343
340
|
@origpreds = preds
|
344
341
|
setup_preds(preds) unless preds.empty?
|
345
342
|
# given new preds, the state for the join will be different. set it up again.
|
@@ -361,20 +358,24 @@ module Bud
|
|
361
358
|
end
|
362
359
|
|
363
360
|
public
|
364
|
-
def
|
365
|
-
|
366
|
-
|
361
|
+
def lefts(*preds, &blk)
|
362
|
+
if blk.nil?
|
363
|
+
@cols = @bud_instance.toplevel.tables[@rels[0].qualified_tabname].cols
|
364
|
+
setup_accessors
|
365
|
+
end
|
367
366
|
pairs(*preds) do |x,y|
|
368
|
-
blk.nil? ?
|
367
|
+
blk.nil? ? x : blk.call(x)
|
369
368
|
end
|
370
369
|
end
|
371
370
|
|
372
371
|
public
|
373
|
-
def
|
374
|
-
|
375
|
-
|
372
|
+
def rights(*preds, &blk)
|
373
|
+
if blk.nil?
|
374
|
+
@cols = @bud_instance.toplevel.tables[@rels[1].qualified_tabname].cols
|
375
|
+
setup_accessors
|
376
|
+
end
|
376
377
|
pairs(*preds) do |x,y|
|
377
|
-
blk.nil? ?
|
378
|
+
blk.nil? ? y : blk.call(y)
|
378
379
|
end
|
379
380
|
end
|
380
381
|
|
@@ -405,13 +406,13 @@ module Bud
|
|
405
406
|
public
|
406
407
|
def flatten(*preds, &blk)
|
407
408
|
if blk.nil?
|
408
|
-
@cols = dupfree_schema(@
|
409
|
+
@cols = dupfree_schema(@rels[0].cols + @rels[1].cols)
|
409
410
|
else
|
410
411
|
@cols = []
|
411
412
|
end
|
412
413
|
setup_accessors
|
413
414
|
pairs(*preds) do |x,y|
|
414
|
-
blk.nil? ? x
|
415
|
+
blk.nil? ? x + y : blk.call(x + y)
|
415
416
|
end
|
416
417
|
end
|
417
418
|
|
@@ -484,6 +485,14 @@ module Bud
|
|
484
485
|
end
|
485
486
|
end
|
486
487
|
end
|
488
|
+
|
489
|
+
public
|
490
|
+
def invalidate_cache
|
491
|
+
super
|
492
|
+
# Only if need to check left join rel because outer joins in Bloom are
|
493
|
+
# left outer joins.
|
494
|
+
@missing_keys.clear if @rels.first.rescan
|
495
|
+
end
|
487
496
|
end
|
488
497
|
|
489
498
|
|
@@ -497,11 +506,11 @@ module Bud
|
|
497
506
|
# first flush, at which point we are sure to have seen all the t-side tuples
|
498
507
|
# in this tick.
|
499
508
|
class PushNotIn < PushStatefulElement
|
500
|
-
def initialize(rellist, bud_instance, preds
|
509
|
+
def initialize(rellist, bud_instance, preds, &blk) # :nodoc: all
|
501
510
|
@lhs, @rhs = rellist
|
502
511
|
@lhs_keycols = nil
|
503
512
|
@rhs_keycols = nil
|
504
|
-
name_in = "#{@lhs.qualified_tabname}_notin_#{@rhs.qualified_tabname}"
|
513
|
+
name_in = "#{@lhs.qualified_tabname}_notin_#{@rhs.qualified_tabname}".to_sym
|
505
514
|
super(name_in, bud_instance, nil, @lhs.schema)
|
506
515
|
setup_preds(preds) unless preds.empty?
|
507
516
|
@rhs_rcvd = false
|
@@ -532,12 +541,13 @@ module Bud
|
|
532
541
|
end
|
533
542
|
|
534
543
|
def find_col(colspec, rel)
|
535
|
-
|
544
|
+
case colspec
|
545
|
+
when Symbol
|
536
546
|
unless rel.respond_to? colspec
|
537
547
|
raise Bud::Error, "attribute :#{colspec} not found in #{rel.qualified_tabname}"
|
538
548
|
end
|
539
549
|
col_desc = rel.send(colspec)
|
540
|
-
|
550
|
+
when Array
|
541
551
|
col_desc = colspec
|
542
552
|
else
|
543
553
|
raise Bud::Error, "symbol or column spec expected. Got #{colspec}"
|
@@ -546,8 +556,8 @@ module Bud
|
|
546
556
|
end
|
547
557
|
|
548
558
|
def get_key(item, offset)
|
549
|
-
keycols = offset == 0 ? @lhs_keycols : @rhs_keycols
|
550
|
-
keycols.nil? ?
|
559
|
+
keycols = (offset == 0 ? @lhs_keycols : @rhs_keycols)
|
560
|
+
keycols.nil? ? [] : item.values_at(*keycols)
|
551
561
|
end
|
552
562
|
|
553
563
|
public
|
@@ -580,8 +590,9 @@ module Bud
|
|
580
590
|
# growing any more, until the next tick.
|
581
591
|
unless @rhs_rcvd
|
582
592
|
@rhs_rcvd = true
|
593
|
+
rhs_hash = @hash_tables[1]
|
583
594
|
@hash_tables[0].each do |key,values|
|
584
|
-
rhs_values =
|
595
|
+
rhs_values = rhs_hash[key]
|
585
596
|
values.each {|item| process_match(item, rhs_values)}
|
586
597
|
end
|
587
598
|
end
|
data/lib/bud/lattice-core.rb
CHANGED
@@ -500,6 +500,11 @@ class Bud::LatticeWrapper
|
|
500
500
|
end
|
501
501
|
end
|
502
502
|
|
503
|
+
superator "<~" do |o|
|
504
|
+
# Overridden when <~ is defined (i.e., channels and terminals)
|
505
|
+
raise Bud::CompileError, "#{tabname} cannot appear on the lhs of a <~ operator"
|
506
|
+
end
|
507
|
+
|
503
508
|
# XXX: refactor with BudCollection to avoid duplication of code
|
504
509
|
def add_merge_target
|
505
510
|
toplevel = @bud_instance.toplevel
|
data/lib/bud/monkeypatch.rb
CHANGED
@@ -10,17 +10,27 @@ class Class
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
$struct_classes = {}
|
14
|
+
$struct_lock = Mutex.new
|
15
|
+
|
13
16
|
# FIXME: Should likely override #hash and #eql? as well.
|
14
17
|
class Bud::TupleStruct < Struct
|
15
18
|
include Comparable
|
16
19
|
|
20
|
+
def self.new_struct(cols)
|
21
|
+
$struct_lock.synchronize {
|
22
|
+
($struct_classes[cols] ||= Bud::TupleStruct.new(*cols))
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# XXX: This only considers two TupleStruct instances to be equal if they have
|
27
|
+
# the same schema (column names) AND the same contents; unclear if structural
|
28
|
+
# equality (consider only values, not column names) would be better.
|
17
29
|
def <=>(o)
|
18
30
|
if o.class == self.class
|
19
31
|
self.each_with_index do |e, i|
|
20
32
|
other = o[i]
|
21
33
|
next if e == other
|
22
|
-
return nil if e.nil?
|
23
|
-
return nil if other.nil?
|
24
34
|
return e <=> other
|
25
35
|
end
|
26
36
|
return 0
|
@@ -35,18 +45,27 @@ class Bud::TupleStruct < Struct
|
|
35
45
|
if o.class == self.class
|
36
46
|
return super
|
37
47
|
elsif o.class == Array
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
return true
|
43
|
-
rescue StandardError
|
44
|
-
return false
|
48
|
+
return false if self.length != o.length
|
49
|
+
self.each_with_index do |el, i|
|
50
|
+
return false if el != o[i]
|
45
51
|
end
|
52
|
+
return true
|
46
53
|
end
|
47
54
|
false
|
48
55
|
end
|
49
56
|
|
57
|
+
def hash
|
58
|
+
self.values.hash
|
59
|
+
end
|
60
|
+
|
61
|
+
def eql?(o)
|
62
|
+
self == o
|
63
|
+
end
|
64
|
+
|
65
|
+
def +(o)
|
66
|
+
self.to_ary + o.to_ary
|
67
|
+
end
|
68
|
+
|
50
69
|
def to_msgpack(out=nil)
|
51
70
|
self.to_a.to_msgpack(out)
|
52
71
|
end
|
@@ -56,15 +75,23 @@ class Bud::TupleStruct < Struct
|
|
56
75
|
end
|
57
76
|
|
58
77
|
alias :to_s :inspect
|
78
|
+
alias :to_ary :to_a
|
59
79
|
end
|
60
80
|
|
61
81
|
# XXX: TEMPORARY/UGLY hack to ensure that arrays and structs compare. This can be
|
62
82
|
# removed once tests are rewritten.
|
63
83
|
class Array
|
64
|
-
alias :
|
84
|
+
alias :old_eq :==
|
85
|
+
alias :old_eql? :eql?
|
86
|
+
|
65
87
|
def ==(o)
|
66
88
|
o = o.to_a if o.kind_of? Bud::TupleStruct
|
67
|
-
self.
|
89
|
+
self.old_eq(o)
|
90
|
+
end
|
91
|
+
|
92
|
+
def eql?(o)
|
93
|
+
o = o.to_a if o.kind_of? Bud::TupleStruct
|
94
|
+
self.old_eql?(o)
|
68
95
|
end
|
69
96
|
end
|
70
97
|
|