bud 0.9.4 → 0.9.5
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.
- data/History.txt +23 -0
- data/bin/budlabel +63 -0
- data/bin/budtimelines +1 -1
- data/docs/cheat.md +1 -1
- data/docs/getstarted.md +8 -8
- data/examples/chat/README.md +2 -0
- data/examples/chat/chat.rb +3 -2
- data/examples/chat/chat_protocol.rb +1 -1
- data/examples/chat/chat_server.rb +3 -2
- data/lib/bud/aggs.rb +16 -2
- data/lib/bud/bud_meta.rb +19 -28
- data/lib/bud/collections.rb +157 -39
- data/lib/bud/depanalysis.rb +3 -4
- data/lib/bud/executor/elements.rb +62 -57
- data/lib/bud/executor/group.rb +35 -32
- data/lib/bud/executor/join.rb +0 -11
- data/lib/bud/graphs.rb +1 -1
- data/lib/bud/labeling/bloomgraph.rb +47 -0
- data/lib/bud/labeling/budplot_style.rb +53 -0
- data/lib/bud/labeling/labeling.rb +288 -0
- data/lib/bud/lattice-core.rb +563 -0
- data/lib/bud/lattice-lib.rb +367 -0
- data/lib/bud/monkeypatch.rb +18 -8
- data/lib/bud/rewrite.rb +314 -139
- data/lib/bud/server.rb +13 -2
- data/lib/bud/source.rb +34 -18
- data/lib/bud/state.rb +90 -1
- data/lib/bud/storage/zookeeper.rb +38 -33
- data/lib/bud/viz.rb +0 -1
- data/lib/bud.rb +55 -15
- metadata +15 -8
data/lib/bud/depanalysis.rb
CHANGED
@@ -6,7 +6,7 @@ class DepAnalysis #:nodoc: all
|
|
6
6
|
|
7
7
|
state do
|
8
8
|
# Data inserted by client, usually from t_depends and t_provides
|
9
|
-
scratch :depends, [:lhs, :op, :body, :neg]
|
9
|
+
scratch :depends, [:lhs, :op, :body, :neg, :in_body]
|
10
10
|
scratch :providing, [:pred, :input]
|
11
11
|
|
12
12
|
# Intermediate state
|
@@ -36,7 +36,7 @@ class DepAnalysis #:nodoc: all
|
|
36
36
|
|
37
37
|
cycle <= depends_tc do |d|
|
38
38
|
if d.lhs == d.body
|
39
|
-
unless
|
39
|
+
unless d.neg and !d.temporal
|
40
40
|
[d.lhs, d.via, d.neg, d.temporal]
|
41
41
|
end
|
42
42
|
end
|
@@ -60,11 +60,10 @@ class DepAnalysis #:nodoc: all
|
|
60
60
|
[p.pred, true]
|
61
61
|
end
|
62
62
|
else
|
63
|
-
unless depends_tc.map{|
|
63
|
+
unless depends_tc.map{|dt| dt.lhs if dt.lhs != dt.body}.include? p.pred
|
64
64
|
[p.pred, false]
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'set'
|
2
1
|
require 'bud/collections'
|
3
2
|
|
4
3
|
module Bud
|
@@ -13,7 +12,7 @@ module Bud
|
|
13
12
|
class PushElement < BudCollection
|
14
13
|
attr_accessor :rescan, :invalidated
|
15
14
|
attr_accessor :elem_name
|
16
|
-
attr_reader :found_delta, :
|
15
|
+
attr_reader :found_delta, :wired_by, :outputs
|
17
16
|
|
18
17
|
def initialize(name_in, bud_instance, collection_name=nil, given_schema=nil, defer_schema=false, &blk)
|
19
18
|
super(name_in, bud_instance, given_schema, defer_schema)
|
@@ -25,7 +24,6 @@ module Bud
|
|
25
24
|
@wired_by = []
|
26
25
|
@elem_name = name_in
|
27
26
|
@found_delta = false
|
28
|
-
@refcount = 1
|
29
27
|
@collection_name = collection_name
|
30
28
|
@invalidated = true
|
31
29
|
@rescan = true
|
@@ -58,6 +56,8 @@ module Bud
|
|
58
56
|
print "#{next_accum} "
|
59
57
|
if o.class <= Bud::BudCollection
|
60
58
|
puts "#{(o.object_id*2).to_s(16)}: #{o.qualified_tabname} (#{o.class})"
|
59
|
+
elsif o.class <= Bud::LatticeWrapper
|
60
|
+
puts "#{o.inspect}"
|
61
61
|
else
|
62
62
|
puts "#{(o.object_id*2).to_s(16)}: (#{o.class.name})"
|
63
63
|
end
|
@@ -83,17 +83,15 @@ module Bud
|
|
83
83
|
|
84
84
|
case kind
|
85
85
|
when :output
|
86
|
-
raise Bud::Error unless element.respond_to? :insert
|
87
86
|
@outputs << element
|
88
87
|
when :pending
|
89
|
-
raise Bud::Error unless element.respond_to? :pending_merge
|
90
88
|
@pendings << element
|
91
89
|
when :delete
|
92
|
-
raise Bud::Error unless element.respond_to? :pending_delete
|
93
90
|
@deletes << element
|
94
91
|
when :delete_by_key
|
95
|
-
raise Bud::Error unless element.respond_to? :pending_delete_keys
|
96
92
|
@delete_keys << element
|
93
|
+
else
|
94
|
+
raise Bud::Error, "unrecognized wiring kind: #{kind}"
|
97
95
|
end
|
98
96
|
|
99
97
|
element.wired_by << self if element.respond_to? :wired_by
|
@@ -116,27 +114,36 @@ module Bud
|
|
116
114
|
end
|
117
115
|
|
118
116
|
def push_out(item, do_block=true)
|
119
|
-
if item
|
120
|
-
|
121
|
-
|
122
|
-
|
117
|
+
return if item.nil?
|
118
|
+
|
119
|
+
if do_block && @blk
|
120
|
+
item = item.to_a if @blk.arity > 1
|
121
|
+
item = @blk.call item
|
122
|
+
return if item.nil?
|
123
|
+
end
|
124
|
+
|
125
|
+
@outputs.each do |ou|
|
126
|
+
if ou.class <= Bud::PushElement
|
127
|
+
ou.insert(item, self)
|
128
|
+
elsif ou.class <= Bud::BudCollection
|
129
|
+
ou.do_insert(item, ou.new_delta)
|
130
|
+
elsif ou.class <= Bud::LatticeWrapper
|
131
|
+
ou.insert(item, self)
|
132
|
+
else
|
133
|
+
raise Bud::Error, "expected output target: #{ou.class}"
|
123
134
|
end
|
135
|
+
end
|
124
136
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
ou.insert(item, self)
|
129
|
-
elsif ou.class <= Bud::BudCollection
|
130
|
-
ou.do_insert(item, ou.new_delta)
|
131
|
-
else
|
132
|
-
raise Bud::Error, "expected either a PushElement or a BudCollection"
|
133
|
-
end
|
134
|
-
end
|
137
|
+
# for the following, o is a BudCollection
|
138
|
+
@deletes.each{|o| o.pending_delete([item])}
|
139
|
+
@delete_keys.each{|o| o.pending_delete_keys([item])}
|
135
140
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
141
|
+
# o is a LatticeWrapper or a BudCollection
|
142
|
+
@pendings.each do |o|
|
143
|
+
if o.class <= Bud::LatticeWrapper
|
144
|
+
o <+ item
|
145
|
+
else
|
146
|
+
o.pending_merge([item])
|
140
147
|
end
|
141
148
|
end
|
142
149
|
end
|
@@ -213,34 +220,30 @@ module Bud
|
|
213
220
|
@collection_name)
|
214
221
|
elem.set_block(&blk)
|
215
222
|
self.wire_to(elem)
|
216
|
-
toplevel.push_elems[[self.object_id, :
|
223
|
+
toplevel.push_elems[[self.object_id, :each_with_index, blk]] = elem
|
217
224
|
end
|
218
225
|
|
219
226
|
def join(elem2, &blk)
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
toplevel.push_joins[toplevel.this_stratum] << join
|
229
|
-
# else
|
230
|
-
# cached.refcount += 1
|
231
|
-
# end
|
232
|
-
return toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]]
|
227
|
+
elem2 = elem2.to_push_elem unless elem2.kind_of? PushElement
|
228
|
+
toplevel = @bud_instance.toplevel
|
229
|
+
join = Bud::PushSHJoin.new([self, elem2], toplevel.this_rule_context, [])
|
230
|
+
self.wire_to(join)
|
231
|
+
elem2.wire_to(join)
|
232
|
+
toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]] = join
|
233
|
+
toplevel.push_joins[toplevel.this_stratum] << join
|
234
|
+
return join
|
233
235
|
end
|
234
236
|
def *(elem2, &blk)
|
235
237
|
join(elem2, &blk)
|
236
238
|
end
|
237
239
|
|
238
|
-
def notin(elem2, preds
|
240
|
+
def notin(elem2, *preds, &blk)
|
241
|
+
elem2 = elem2.to_push_elem unless elem2.kind_of? PushElement
|
239
242
|
toplevel = @bud_instance.toplevel
|
240
243
|
notin_elem = Bud::PushNotIn.new([self, elem2], toplevel.this_rule_context, preds, &blk)
|
241
244
|
self.wire_to(notin_elem)
|
242
245
|
elem2.wire_to(notin_elem)
|
243
|
-
toplevel.push_elems[[self.object_id, :notin,
|
246
|
+
toplevel.push_elems[[self.object_id, :notin, [self, elem2], toplevel, blk]] = notin_elem
|
244
247
|
return notin_elem
|
245
248
|
end
|
246
249
|
|
@@ -280,14 +283,12 @@ module Bud
|
|
280
283
|
the_schema = { keynames => aggcols }
|
281
284
|
end
|
282
285
|
|
283
|
-
aggpairs = aggpairs
|
286
|
+
aggpairs = prep_aggpairs(aggpairs)
|
284
287
|
toplevel = @bud_instance.toplevel
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
# end
|
290
|
-
# toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]]
|
288
|
+
g = Bud::PushGroup.new('grp'+Time.new.tv_usec.to_s, toplevel.this_rule_context,
|
289
|
+
@collection_name, keycols, aggpairs, the_schema, &blk)
|
290
|
+
self.wire_to(g)
|
291
|
+
toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]] = g
|
291
292
|
return g
|
292
293
|
end
|
293
294
|
|
@@ -305,19 +306,17 @@ module Bud
|
|
305
306
|
end
|
306
307
|
end
|
307
308
|
aggpairs = [[agg, collection]]
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
# end
|
313
|
-
# return toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]]
|
309
|
+
aa = Bud::PushArgAgg.new('argagg'+Time.new.tv_usec.to_s, toplevel.this_rule_context,
|
310
|
+
@collection_name, gbkey_cols, aggpairs, schema, &blk)
|
311
|
+
self.wire_to(aa)
|
312
|
+
toplevel.push_elems[[self.object_id, :argagg, gbkey_cols, aggpairs, blk]] = aa
|
314
313
|
return aa
|
315
314
|
end
|
316
315
|
def argmax(gbcols, col, &blk)
|
317
|
-
argagg(gbcols,
|
316
|
+
argagg(:max, gbcols, col, &blk)
|
318
317
|
end
|
319
318
|
def argmin(gbcols, col, &blk)
|
320
|
-
argagg(gbcols,
|
319
|
+
argagg(:min, gbcols, col, &blk)
|
321
320
|
end
|
322
321
|
def sort(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
323
322
|
elem = Bud::PushSort.new(name, bud_instance, the_schema, &blk)
|
@@ -447,6 +446,7 @@ module Bud
|
|
447
446
|
class ScannerElement < PushElement
|
448
447
|
attr_reader :collection
|
449
448
|
attr_reader :rescan_set, :invalidate_set
|
449
|
+
attr_accessor :force_rescan
|
450
450
|
|
451
451
|
def initialize(elem_name, bud_instance, collection_in,
|
452
452
|
the_schema=collection_in.schema, &blk)
|
@@ -454,6 +454,7 @@ module Bud
|
|
454
454
|
@collection = collection_in
|
455
455
|
@rescan_set = []
|
456
456
|
@invalidate_set = []
|
457
|
+
@force_rescan = false
|
457
458
|
end
|
458
459
|
|
459
460
|
def rescan
|
@@ -485,7 +486,11 @@ module Bud
|
|
485
486
|
end
|
486
487
|
|
487
488
|
def scan(first_iter)
|
488
|
-
if
|
489
|
+
if @force_rescan
|
490
|
+
# Scan entire storage
|
491
|
+
@collection.each_raw {|item| push_out(item)}
|
492
|
+
@force_rescan = false
|
493
|
+
elsif first_iter
|
489
494
|
if rescan
|
490
495
|
# Scan entire storage
|
491
496
|
@collection.each_raw {|item| push_out(item)}
|
data/lib/bud/executor/group.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'bud/executor/elements'
|
2
|
-
require 'set'
|
3
2
|
|
4
3
|
module Bud
|
5
4
|
class PushGroup < PushStatefulElement
|
@@ -10,21 +9,25 @@ module Bud
|
|
10
9
|
else
|
11
10
|
@keys = keys_in.map{|k| k[1]}
|
12
11
|
end
|
13
|
-
# An aggpair is an array: [agg class instance,
|
14
|
-
#
|
15
|
-
@aggpairs = aggpairs_in.map
|
12
|
+
# An aggpair is an array: [agg class instance, array of indexes of input
|
13
|
+
# agg input columns]. The second field is nil for Count.
|
14
|
+
@aggpairs = aggpairs_in.map do |ap|
|
15
|
+
agg, *rest = ap
|
16
|
+
if rest.empty?
|
17
|
+
[agg, nil]
|
18
|
+
else
|
19
|
+
[agg, rest.map {|r| r[1]}]
|
20
|
+
end
|
21
|
+
end
|
16
22
|
@groups = {}
|
17
23
|
|
18
24
|
# Check whether we need to eliminate duplicates from our input (we might
|
19
25
|
# see duplicates because of the rescan/invalidation logic, as well as
|
20
26
|
# because we don't do duplicate elimination on the output of a projection
|
21
27
|
# operator). We don't need to dupelim if all the args are exemplary.
|
22
|
-
@elim_dups = @aggpairs.any? {|
|
23
|
-
if @elim_dups
|
24
|
-
@input_cache = Set.new
|
25
|
-
end
|
28
|
+
@elim_dups = @aggpairs.any? {|ap| not ap[0].kind_of? ArgExemplary}
|
29
|
+
@input_cache = Set.new if @elim_dups
|
26
30
|
|
27
|
-
@seen_new_data = false
|
28
31
|
super(elem_name, bud_instance, collection_name, schema_in, &blk)
|
29
32
|
end
|
30
33
|
|
@@ -34,19 +37,25 @@ module Bud
|
|
34
37
|
@input_cache << item
|
35
38
|
end
|
36
39
|
|
37
|
-
@seen_new_data = true
|
38
40
|
key = item.values_at(*@keys)
|
39
41
|
group_state = @groups[key]
|
40
42
|
if group_state.nil?
|
41
43
|
@groups[key] = @aggpairs.map do |ap|
|
42
|
-
|
43
|
-
|
44
|
+
if ap[1].nil?
|
45
|
+
ap[0].init(item)
|
46
|
+
else
|
47
|
+
ap[0].init(*item.values_at(*ap[1]))
|
48
|
+
end
|
44
49
|
end
|
45
50
|
else
|
46
51
|
@aggpairs.each_with_index do |ap, agg_ix|
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
state_val = group_state[agg_ix]
|
53
|
+
if ap[1].nil?
|
54
|
+
trans_rv = ap[0].trans(state_val, item)
|
55
|
+
else
|
56
|
+
trans_rv = ap[0].trans(state_val, *item.values_at(*ap[1]))
|
57
|
+
end
|
58
|
+
group_state[agg_ix] = trans_rv[0]
|
50
59
|
end
|
51
60
|
end
|
52
61
|
end
|
@@ -62,14 +71,12 @@ module Bud
|
|
62
71
|
puts "#{self.class}/#{self.tabname} invalidated" if $BUD_DEBUG
|
63
72
|
@groups.clear
|
64
73
|
@input_cache.clear if @elim_dups
|
65
|
-
@seen_new_data = false
|
66
74
|
end
|
67
75
|
|
68
76
|
def flush
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
@seen_new_data = false
|
77
|
+
# Don't emit fresh output unless a rescan is needed
|
78
|
+
return unless @rescan
|
79
|
+
@rescan = false
|
73
80
|
|
74
81
|
@groups.each do |key, group_state|
|
75
82
|
rv = key.clone
|
@@ -87,7 +94,6 @@ module Bud
|
|
87
94
|
raise Bud::Error, "multiple aggpairs #{aggpairs_in.map{|a| a.class.name}} in ArgAgg; only one allowed"
|
88
95
|
end
|
89
96
|
super(elem_name, bud_instance, collection_name, keys_in, aggpairs_in, schema_in, &blk)
|
90
|
-
@agg, @aggcol = @aggpairs[0]
|
91
97
|
@winners = {}
|
92
98
|
end
|
93
99
|
|
@@ -101,18 +107,16 @@ module Bud
|
|
101
107
|
key = @keys.map{|k| item[k]}
|
102
108
|
group_state = @groups[key]
|
103
109
|
if group_state.nil?
|
104
|
-
@seen_new_data = true
|
105
110
|
@groups[key] = @aggpairs.map do |ap|
|
106
111
|
@winners[key] = [item]
|
107
|
-
|
108
|
-
ap[0].init(
|
112
|
+
input_vals = item.values_at(*ap[1])
|
113
|
+
ap[0].init(*input_vals)
|
109
114
|
end
|
110
115
|
else
|
111
116
|
@aggpairs.each_with_index do |ap, agg_ix|
|
112
|
-
|
113
|
-
state_val, flag, *rest = ap[0].trans(group_state[agg_ix],
|
117
|
+
input_vals = item.values_at(*ap[1])
|
118
|
+
state_val, flag, *rest = ap[0].trans(group_state[agg_ix], *input_vals)
|
114
119
|
group_state[agg_ix] = state_val
|
115
|
-
@seen_new_data = true unless flag == :ignore
|
116
120
|
|
117
121
|
case flag
|
118
122
|
when :ignore
|
@@ -133,14 +137,13 @@ module Bud
|
|
133
137
|
end
|
134
138
|
|
135
139
|
def flush
|
136
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
@seen_new_data = false
|
140
|
+
# Don't emit fresh output unless a rescan is needed
|
141
|
+
return unless @rescan
|
142
|
+
@rescan = false
|
140
143
|
|
141
144
|
@groups.each_key do |g|
|
142
145
|
@winners[g].each do |t|
|
143
|
-
push_out(t
|
146
|
+
push_out(t)
|
144
147
|
end
|
145
148
|
end
|
146
149
|
end
|
data/lib/bud/executor/join.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'bud/executor/elements'
|
2
|
-
require 'set'
|
3
2
|
|
4
3
|
$EMPTY = []
|
5
4
|
module Bud
|
@@ -58,12 +57,6 @@ module Bud
|
|
58
57
|
super(@tabname, @bud_instance, nil, @cols)
|
59
58
|
end
|
60
59
|
|
61
|
-
public
|
62
|
-
def copy_on_write
|
63
|
-
@refcount -= 1
|
64
|
-
return Bud::PushSHJoin.new(@all_rels_below, @bud_instance, [])
|
65
|
-
end
|
66
|
-
|
67
60
|
public
|
68
61
|
def state_id # :nodoc: all
|
69
62
|
object_id
|
@@ -366,10 +359,6 @@ module Bud
|
|
366
359
|
# :col1 => :col2 (same as lefttable.col1 => righttable.col2)
|
367
360
|
public
|
368
361
|
def pairs(*preds, &blk)
|
369
|
-
## XXX Need to do this for all the join modifiers
|
370
|
-
unless @refcount == 1
|
371
|
-
return self.copy_on_write.pairs(preds, blk)
|
372
|
-
end
|
373
362
|
@origpreds = preds
|
374
363
|
setup_preds(preds) unless preds.empty?
|
375
364
|
# given new preds, the state for the join will be different. set it up again.
|
data/lib/bud/graphs.rb
CHANGED
@@ -75,7 +75,7 @@ class GraphGen #:nodoc: all
|
|
75
75
|
# its name is "CYC" + concat(sort(predicate names))
|
76
76
|
depends.each do |d|
|
77
77
|
# b/c bud_obj was pruned before serialization...
|
78
|
-
|
78
|
+
bud_obj, rule_id, lhs, op, body, nm, in_body = d.to_a
|
79
79
|
head = lhs
|
80
80
|
body = body
|
81
81
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
require 'graphviz'
|
4
|
+
|
5
|
+
# A simple interface between graphviz and bud
|
6
|
+
module BudGraph
|
7
|
+
state do
|
8
|
+
interface input, :bnode, [:name] => [:meta]
|
9
|
+
interface input, :bedge, [:from, :to, :meta]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module BloomGraph
|
14
|
+
include BudGraph
|
15
|
+
|
16
|
+
state do
|
17
|
+
table :nodes, bnode.schema
|
18
|
+
table :edges, bedge.schema
|
19
|
+
end
|
20
|
+
|
21
|
+
bloom do
|
22
|
+
nodes <= bnode
|
23
|
+
edges <= bedge
|
24
|
+
end
|
25
|
+
|
26
|
+
def finish(ignore, name, fmt=:pdf)
|
27
|
+
it = ignore.to_set
|
28
|
+
tick
|
29
|
+
nodes.to_a.each do |n|
|
30
|
+
unless it.include? n.name.to_sym
|
31
|
+
@graph.add_nodes(n.name, n.meta)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
edges.to_a.each do |e|
|
36
|
+
unless it.include? e.from.to_sym or it.include? e.to.to_sym
|
37
|
+
@graph.add_edges(e.from, e.to, e.meta)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
@graph.output(fmt => name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(opts={:type => :digraph})
|
44
|
+
@graph = GraphViz.new(:G, opts)
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|