bud 0.0.8 → 0.1.0.pre1
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/README +4 -10
- data/bin/budplot +1 -2
- data/docs/cheat.md +2 -15
- data/examples/basics/paths.rb +7 -7
- data/lib/bud/aggs.rb +15 -19
- data/lib/bud/bud_meta.rb +165 -77
- data/lib/bud/bust/bust.rb +11 -4
- data/lib/bud/collections.rb +643 -280
- data/lib/bud/depanalysis.rb +50 -25
- data/lib/bud/executor/elements.rb +592 -0
- data/lib/bud/executor/group.rb +104 -0
- data/lib/bud/executor/join.rb +638 -0
- data/lib/bud/graphs.rb +12 -11
- data/lib/bud/joins.rb +2 -1
- data/lib/bud/meta_algebra.rb +5 -4
- data/lib/bud/metrics.rb +9 -3
- data/lib/bud/monkeypatch.rb +131 -23
- data/lib/bud/rebl.rb +41 -28
- data/lib/bud/rewrite.rb +112 -440
- data/lib/bud/server.rb +3 -2
- data/lib/bud/source.rb +109 -0
- data/lib/bud/state.rb +16 -9
- data/lib/bud/storage/dbm.rb +62 -16
- data/lib/bud/storage/zookeeper.rb +2 -2
- data/lib/bud/viz.rb +8 -4
- data/lib/bud/viz_util.rb +10 -9
- data/lib/bud.rb +413 -199
- metadata +40 -55
- data/examples/deploy/tokenring-ec2.rb +0 -26
- data/examples/deploy/tokenring-fork.rb +0 -15
- data/examples/deploy/tokenring-thread.rb +0 -15
- data/examples/deploy/tokenring.rb +0 -47
- data/lib/bud/deploy/deployer.rb +0 -67
- data/lib/bud/deploy/ec2deploy.rb +0 -199
- data/lib/bud/deploy/forkdeploy.rb +0 -90
- data/lib/bud/deploy/threaddeploy.rb +0 -38
- data/lib/bud/storage/tokyocabinet.rb +0 -190
- data/lib/bud/stratify.rb +0 -85
data/lib/bud/joins.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
$EMPTY = []
|
1
2
|
module Bud
|
2
3
|
class BudJoin < BudCollection
|
3
4
|
attr_accessor :rels, :origrels, :origpreds # :nodoc: all
|
@@ -106,7 +107,7 @@ module Bud
|
|
106
107
|
flat_schema = @rels.map{|r| r.cols}.flatten(1)
|
107
108
|
dupfree_schema = []
|
108
109
|
# while loop here (inefficiently) ensures no collisions
|
109
|
-
while dupfree_schema ==
|
110
|
+
while dupfree_schema == $EMPTY or dupfree_schema.uniq.length < dupfree_schema.length
|
110
111
|
dupfree_schema = []
|
111
112
|
flat_schema.reduce({}) do |memo, r|
|
112
113
|
if r.to_s.include?("_") and ((r.to_s.rpartition("_")[2] =~ /^\d+$/) == 0)
|
data/lib/bud/meta_algebra.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bud'
|
3
|
-
require 'bud/depanalysis'
|
4
3
|
|
5
4
|
module MetaAlgebra
|
6
5
|
state do
|
@@ -48,6 +47,7 @@ module MetaAlgebra
|
|
48
47
|
#stdio <~ seq_lattice_closure{|c| ["SLC: #{c.inspect}"]}
|
49
48
|
#stdio <~ jlr {|j| ["JLR: #{j.inspect}"]}
|
50
49
|
#stdio <~ lub {|l| ["LUB #{l.inspect}, left class #{l.left.class}"]}
|
50
|
+
#stdio <~ clean_dep.inspected
|
51
51
|
end
|
52
52
|
|
53
53
|
bloom :lattice_rules do
|
@@ -92,10 +92,11 @@ module MetaAlgebra
|
|
92
92
|
end
|
93
93
|
|
94
94
|
bloom :make_paths do
|
95
|
-
rule_nm <= t_depends.reduce(
|
95
|
+
rule_nm <= t_depends.reduce(Hash.new) do |memo, i|
|
96
96
|
tag = get_tag(i.nm, i.op)
|
97
|
-
memo[i.rule_id]
|
98
|
-
|
97
|
+
if memo[i.rule_id].nil? or memo[i.rule_id] == :M
|
98
|
+
memo[i.rule_id] = tag
|
99
|
+
end
|
99
100
|
memo
|
100
101
|
end
|
101
102
|
|
data/lib/bud/metrics.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require "bud/errors"
|
2
|
+
if RUBY_VERSION <= "1.9"
|
3
|
+
require 'faster_csv'
|
4
|
+
$mod = FasterCSV
|
5
|
+
else
|
6
|
+
require 'csv'
|
7
|
+
$mod = CSV
|
8
|
+
end
|
3
9
|
|
4
10
|
# metrics are reported in a nested hash representing a collection of relational tables.
|
5
11
|
# The metrics hash has the following form:
|
@@ -14,7 +20,7 @@ require 'faster_csv'
|
|
14
20
|
def report_metrics
|
15
21
|
metrics.each do |k,v|
|
16
22
|
if v.first
|
17
|
-
csvstr =
|
23
|
+
csvstr = $mod.generate(:force_quotes=>true) do |csv|
|
18
24
|
csv << [k.to_s] + v.first[0].keys + [:val]
|
19
25
|
v.each do |row|
|
20
26
|
csv << [nil] + row[0].values + [row[1]]
|
data/lib/bud/monkeypatch.rb
CHANGED
@@ -1,33 +1,129 @@
|
|
1
1
|
# We monkeypatch Module to add support for Bloom's syntax additions: "state",
|
2
2
|
# "bloom", and "bootstrap" blocks, plus the "import" statement.
|
3
|
+
|
4
|
+
require 'bud/source'
|
5
|
+
|
6
|
+
class Class
|
7
|
+
def modules
|
8
|
+
a = self.ancestors
|
9
|
+
a[1..a.index(superclass)-1]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# FIXME: Use a subclass of Struct.
|
14
|
+
class Struct
|
15
|
+
def <=>(o)
|
16
|
+
if o.class == self.class
|
17
|
+
self.each_with_index do |e, i|
|
18
|
+
cmp = e <=> o[i]
|
19
|
+
return cmp if cmp != 0
|
20
|
+
end
|
21
|
+
return 0
|
22
|
+
elsif o.nil?
|
23
|
+
return -1
|
24
|
+
else
|
25
|
+
raise "Comparison (<=>) between #{o.class} and #{self.class} not implemented"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
alias oldeq :==
|
30
|
+
def ==(o)
|
31
|
+
if o.class == self.class
|
32
|
+
return oldeq(o)
|
33
|
+
elsif o.class == Array
|
34
|
+
begin
|
35
|
+
self.each_with_index do |el, i|
|
36
|
+
if el != o[i]
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return true
|
41
|
+
rescue StandardError
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_msgpack(out='')
|
49
|
+
self.to_a.to_msgpack(out)
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
self.to_a.inspect
|
54
|
+
end
|
55
|
+
|
56
|
+
alias :to_s :inspect
|
57
|
+
end
|
58
|
+
|
59
|
+
# XXX: TEMPORARY/UGLY hack to ensure that arrays and structs compare. This can be
|
60
|
+
# removed once tests are rewritten.
|
61
|
+
class Array
|
62
|
+
alias :oldeq :==
|
63
|
+
def ==(o)
|
64
|
+
if o.kind_of? Struct
|
65
|
+
o = (o.to_a)
|
66
|
+
end
|
67
|
+
self.oldeq(o)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
$moduleWrapper = {} # module => wrapper class. See import below.
|
3
73
|
class Module
|
74
|
+
def modules
|
75
|
+
ancestors[1..-1]
|
76
|
+
end
|
77
|
+
|
4
78
|
# import another module and assign to a qualifier symbol: <tt>import MyModule => :m</tt>
|
5
79
|
def import(spec)
|
6
80
|
raise Bud::CompileError unless (spec.class <= Hash and spec.length == 1)
|
7
81
|
mod, local_name = spec.first
|
8
82
|
raise Bud::CompileError unless (mod.class <= Module and local_name.class <= Symbol)
|
9
83
|
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
|
84
|
+
# A statement like this:
|
85
|
+
# import MyModule => :m
|
86
|
+
# is translated as follows. First, module MyModule is made instantiable by wrapping it in a class
|
87
|
+
# class MyModule__wrap__
|
88
|
+
# include Bud
|
89
|
+
# include MyModule
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# Then introduce a method "m", the import binding name, in the calling module/class
|
93
|
+
# (the one with the import statement). This returns an instance of the wrapped class.
|
94
|
+
# inst = MyModule__wrap__.new
|
95
|
+
# def m
|
96
|
+
# inst
|
97
|
+
# end
|
98
|
+
|
99
|
+
mod, local_name = spec.first
|
100
|
+
|
101
|
+
if self.instance_methods.include? local_name.to_s or self.instance_methods.include? local_name
|
102
|
+
raise Bud::CompileError, "#{local_name} is already taken."
|
103
|
+
else
|
104
|
+
src = %Q{
|
105
|
+
def #{local_name}
|
106
|
+
@#{local_name}
|
107
|
+
end
|
108
|
+
def #{local_name}=(val)
|
109
|
+
raise "Type Error: expecting an instance of #{mod}" unless val.kind_of? #{mod}
|
110
|
+
@#{local_name} = val
|
111
|
+
end
|
112
|
+
}
|
113
|
+
#puts src
|
114
|
+
self.class_eval src
|
14
115
|
end
|
15
116
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# module X points to X's own nested import table. If a single module
|
20
|
-
# attempts to import multiple sub-modules with the same local name, we merge
|
21
|
-
# the import tables of all the modules.
|
22
|
-
@bud_import_tbl ||= {}
|
23
|
-
prev_tbl = @bud_import_tbl[local_name]
|
24
|
-
child_tbl = NestedRefRewriter.build_import_table(mod)
|
25
|
-
@bud_import_tbl[local_name] = NestedRefRewriter.merge_import_table(prev_tbl, child_tbl)
|
117
|
+
import_tbl = self.bud_import_table
|
118
|
+
import_tbl[local_name] = mod
|
119
|
+
end
|
26
120
|
|
27
|
-
|
28
|
-
|
121
|
+
def bud_import_table() #:nodoc: all
|
122
|
+
@bud_import_tbl ||= {}
|
123
|
+
@bud_import_tbl
|
29
124
|
end
|
30
125
|
|
126
|
+
|
31
127
|
# the block of Bloom collection declarations. one per module.
|
32
128
|
def state(&block)
|
33
129
|
meth_name = Module.make_state_meth_name(self)
|
@@ -64,12 +160,22 @@ class Module
|
|
64
160
|
if instance_methods(false).include? meth_name
|
65
161
|
raise Bud::CompileError, "duplicate named bloom block: '#{block_name}' in #{self}"
|
66
162
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
163
|
+
ast = Source.read_block(caller[0]) # pass in caller's location via backtrace
|
164
|
+
# ast corresponds only to the statements of the block. Wrap it in a method
|
165
|
+
# definition for backward compatibility for now.
|
166
|
+
# First wrap ast in a block if it is only a single statement
|
167
|
+
unless ast.nil?
|
168
|
+
ast = s(:block, ast) unless ast.sexp_type == :block
|
169
|
+
ast = s(:defn, meth_name.to_sym, s(:args), s(:scope, ast))
|
170
|
+
unless self.respond_to? :__bloom_asts__
|
171
|
+
def self.__bloom_asts__;
|
172
|
+
@__bloom_asts__ ||= {}
|
173
|
+
@__bloom_asts__
|
174
|
+
end
|
175
|
+
end
|
176
|
+
__bloom_asts__[meth_name] = ast
|
177
|
+
define_method(meth_name.to_sym, &block)
|
178
|
+
end
|
73
179
|
end
|
74
180
|
|
75
181
|
private
|
@@ -78,7 +184,9 @@ class Module
|
|
78
184
|
# class/module Y, X's class name is the string "Y::X". We don't want to define
|
79
185
|
# method names with semicolons in them, so just return "X" instead.
|
80
186
|
def self.get_class_name(klass)
|
81
|
-
klass.name.
|
187
|
+
(klass.name.nil? or klass.name == "") \
|
188
|
+
? "Anon#{klass.object_id}" \
|
189
|
+
: klass.name.split("::").last
|
82
190
|
end
|
83
191
|
|
84
192
|
# State method blocks are named using an auto-incrementing counter. This is to
|
data/lib/bud/rebl.rb
CHANGED
@@ -3,11 +3,11 @@ require 'readline'
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'bud'
|
5
5
|
require 'abbrev'
|
6
|
-
|
6
|
+
require 'tempfile'
|
7
7
|
TABLE_TYPES = ["table", "scratch", "channel", "loopback", "periodic", "sync", "store"]
|
8
8
|
|
9
9
|
# The class to which rebl adds user-specified rules and declarations.
|
10
|
-
class
|
10
|
+
class ReblBase
|
11
11
|
include Bud
|
12
12
|
attr_accessor:port, :ip
|
13
13
|
|
@@ -22,8 +22,8 @@ class ReblShell
|
|
22
22
|
@@maxhistsize = 100
|
23
23
|
@@escape_char = '/'
|
24
24
|
@@commands =
|
25
|
-
{"tick" => [lambda {|lib,argv| lib.tick([Integer(argv[1])
|
26
|
-
"tick [x]
|
25
|
+
{"tick" => [lambda {|lib,argv| lib.tick(argv[1].nil? ? 1 : Integer(argv[1]))},
|
26
|
+
"tick [x]:\texecutes x (or 1) timesteps"],
|
27
27
|
|
28
28
|
"run" => [lambda {|lib,_| lib.run},
|
29
29
|
"run\ttick until quiescence, a breakpoint, or #{@@escape_char}stop"],
|
@@ -62,8 +62,9 @@ class ReblShell
|
|
62
62
|
loop do
|
63
63
|
begin
|
64
64
|
rebl_loop(lib)
|
65
|
-
rescue Exception
|
66
|
-
puts "exception: #{
|
65
|
+
rescue Exception => e
|
66
|
+
puts "exception: #{e}"
|
67
|
+
#puts e.backtrace
|
67
68
|
end
|
68
69
|
end
|
69
70
|
end
|
@@ -197,6 +198,7 @@ class LibRebl
|
|
197
198
|
@@builtin_tables = [:stdio, :t_depends, :periodics_tbl, :t_cycle, :localtick,
|
198
199
|
:t_provides, :t_rules, :t_depends_tc, :t_stratum,
|
199
200
|
:rebl_breakpoint]
|
201
|
+
@@classid = 0
|
200
202
|
|
201
203
|
def initialize(ip, port)
|
202
204
|
@ip = ip
|
@@ -222,7 +224,7 @@ class LibRebl
|
|
222
224
|
end
|
223
225
|
|
224
226
|
# Ticks the bud instance a specified integer number of times.
|
225
|
-
def tick(x)
|
227
|
+
def tick(x=1)
|
226
228
|
x.times {@rebl_class_inst.sync_do}
|
227
229
|
end
|
228
230
|
|
@@ -234,13 +236,7 @@ class LibRebl
|
|
234
236
|
puts "Error: non-existent collection \"#{c}\""
|
235
237
|
else
|
236
238
|
tups = @rebl_class_inst.tables[c.to_sym].to_a.sort
|
237
|
-
|
238
|
-
puts "(empty)"
|
239
|
-
else
|
240
|
-
tups.each do |t|
|
241
|
-
puts t.inspect
|
242
|
-
end
|
243
|
-
end
|
239
|
+
puts(tups.empty? ? "(empty)" : tups.sort.map{|t| "#{t}"}.join("\n"))
|
244
240
|
end
|
245
241
|
end
|
246
242
|
|
@@ -278,22 +274,40 @@ class LibRebl
|
|
278
274
|
end
|
279
275
|
end
|
280
276
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
277
|
+
def mk_rebl_class
|
278
|
+
@@classid += 1
|
279
|
+
cls_name = "ReblClass#{@@classid}"
|
280
|
+
|
281
|
+
str = ""
|
282
|
+
str =<<-EOS
|
283
|
+
$BUD_SAFE=1
|
284
|
+
class #{cls_name} < ReblBase
|
285
|
+
include Bud
|
286
|
+
EOS
|
287
|
+
unless @state.empty?
|
288
|
+
str += "state do\n" + @state.values.join("\n") + "\nend\n"
|
289
|
+
end
|
290
|
+
unless @rules.empty?
|
291
|
+
str += "bloom :rebl_rules do\n" + @rules.sort.map {|_,r| r}.join("\n") + "\nend\n"
|
292
|
+
end
|
293
|
+
str += "\nend\n"
|
294
|
+
f = Tempfile.new("rebl")
|
295
|
+
f.write(str)
|
296
|
+
f.close
|
286
297
|
begin
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
if not @state.empty?
|
292
|
-
@rebl_class.class_eval("state do\n" + @state.values.join("\n") + "\nend")
|
293
|
-
end
|
294
|
-
rescue Exception
|
298
|
+
load f.path
|
299
|
+
return eval cls_name # return the class object
|
300
|
+
rescue
|
301
|
+
$stderr.puts "Unable to eval the following code:\n" + str
|
295
302
|
raise
|
303
|
+
ensure
|
304
|
+
f.unlink
|
296
305
|
end
|
306
|
+
end
|
307
|
+
|
308
|
+
private
|
309
|
+
def reinstantiate
|
310
|
+
@rebl_class = mk_rebl_class
|
297
311
|
|
298
312
|
@old_inst = @rebl_class_inst
|
299
313
|
@rebl_class_inst = @rebl_class.new(:signal_handling => :none, :ip => @ip,
|
@@ -314,7 +328,6 @@ class LibRebl
|
|
314
328
|
@rebl_class_inst.channels.merge!(@old_inst.channels.reject do |k,v|
|
315
329
|
@@builtin_tables.include? k
|
316
330
|
end)
|
317
|
-
@rebl_class_inst.tc_tables.merge! @old_inst.tc_tables
|
318
331
|
@rebl_class_inst.dbm_tables.merge! @old_inst.dbm_tables
|
319
332
|
@rebl_class_inst.zk_tables.merge! @old_inst.zk_tables
|
320
333
|
|