bud 0.0.8 → 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|