bud 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.9.2 / ???
2
+
3
+ * Add new aggregate functions: bool_and() and bool_or()
4
+ * Fix bugs in notin() stratification and implementation (#271)
5
+ * Fix a bug in processing multi-way joins defined inside modules
6
+ * Fix two bugs in reduce() operator
7
+ * Incorrect default value was sometimes returned
8
+ * Didn't handle reduce() outputs that aren't tuples with two fields
9
+ * Improve reduce() operator error reporting
10
+ * Improve MRI 1.9 compatibility
11
+
1
12
  == 0.9.1 / 2012-04-10
2
13
 
3
14
  * Reject attempts to insert a tuple into a collection with more fields than are
data/docs/cheat.md CHANGED
@@ -237,7 +237,7 @@ Finally, we output every tuple of `bc` that does *not* appear in `t`.
237
237
  ## SQL-style grouping/aggregation (and then some) ##
238
238
 
239
239
  * `bc.group([:col1, :col2], min(:col3))`. *akin to min(col3) GROUP BY col1,col2*
240
- * exemplary aggs: `min`, `max`, `choose`
240
+ * exemplary aggs: `min`, `max`, `bool_and`, `bool_or`, `choose`
241
241
  * summary aggs: `sum`, `avg`, `count`
242
242
  * structural aggs: `accum`
243
243
  * `bc.argmax([:attr1], :attr2)`      *returns the bc items per attr1 that have highest attr2*
data/lib/bud/aggs.rb CHANGED
@@ -70,6 +70,34 @@ module Bud
70
70
  [Max.new, x]
71
71
  end
72
72
 
73
+ class BooleanAnd < ArgExemplary #:nodoc: all
74
+ def trans(the_state, val)
75
+ if val == false
76
+ return val, :replace
77
+ else
78
+ return the_state, :ignore
79
+ end
80
+ end
81
+ end
82
+
83
+ def bool_and(x)
84
+ [BooleanAnd.new, x]
85
+ end
86
+
87
+ class BooleanOr < ArgExemplary #:nodoc: all
88
+ def trans(the_state, val)
89
+ if val == true
90
+ return val, :replace
91
+ else
92
+ return the_state, :ignore
93
+ end
94
+ end
95
+ end
96
+
97
+ def bool_or(x)
98
+ [BooleanOr.new, x]
99
+ end
100
+
73
101
  class Choose < ArgExemplary #:nodoc: all
74
102
  def trans(the_state, val)
75
103
  if the_state.nil?
@@ -680,16 +680,14 @@ module Bud
680
680
  public
681
681
  def *(collection)
682
682
  elem1 = to_push_elem
683
- j = elem1.join(collection)
684
- return j
683
+ return elem1.join(collection)
685
684
  end
686
685
 
687
686
  def group(key_cols, *aggpairs, &blk)
688
687
  elem = to_push_elem
689
688
  key_cols = key_cols.map{|k| canonicalize_col(k)} unless key_cols.nil?
690
689
  aggpairs = aggpairs.map{|ap| [ap[0], canonicalize_col(ap[1])].compact} unless aggpairs.nil?
691
- g = elem.group(key_cols, *aggpairs, &blk)
692
- return g
690
+ return elem.group(key_cols, *aggpairs, &blk)
693
691
  end
694
692
 
695
693
  def notin(collection, *preds, &blk)
@@ -1,8 +1,6 @@
1
1
  require 'set'
2
2
  require 'bud/collections'
3
3
 
4
- ELEMENT_BUFSIZE = 1
5
-
6
4
  module Bud
7
5
  # Usage example:
8
6
  # p = PushElement.new(:r) do |inp|
@@ -520,8 +518,9 @@ module Bud
520
518
  class PushReduce < PushStatefulElement
521
519
  def initialize(elem_name, bud_instance, collection_name,
522
520
  schema_in, initial, &blk)
523
- @memo = initial
521
+ @initial = initial
524
522
  @blk = blk
523
+ reset_memo
525
524
  super(elem_name, bud_instance, collection_name, schema)
526
525
  end
527
526
 
@@ -530,13 +529,21 @@ module Bud
530
529
  end
531
530
 
532
531
  def invalidate_cache
533
- @memo.clear
532
+ puts "#{self.class}/#{self.tabname} invalidated" if $BUD_DEBUG
533
+ reset_memo
534
+ end
535
+
536
+ def reset_memo
537
+ @memo = Marshal.load(Marshal.dump(@initial))
534
538
  end
535
539
 
536
540
  public
537
541
  def flush
538
- @memo.each do |k,v|
539
- push_out([k,v], false)
542
+ unless @memo.kind_of? Enumerable
543
+ raise Bud::TypeError, "output of reduce must be Enumerable: #{@memo.inspect}"
544
+ end
545
+ @memo.each do |t|
546
+ push_out(t, false)
540
547
  end
541
548
  end
542
549
  end
@@ -21,7 +21,6 @@ module Bud
21
21
  agg = (@groups[key].nil? or @groups[key][agg_ix].nil?) ? ap[0].send(:init, agg_input) : ap[0].send(:trans, @groups[key][agg_ix], agg_input)[0]
22
22
  @groups[key] ||= Array.new(@aggpairs.length)
23
23
  @groups[key][agg_ix] = agg
24
- push_out(nil)
25
24
  end
26
25
  end
27
26
 
@@ -40,7 +39,6 @@ module Bud
40
39
  (1..grp.length-1).each {|i| outval << grp[i]}
41
40
  push_out(outval)
42
41
  end
43
- #@groups = {}
44
42
  end
45
43
  end
46
44
 
@@ -82,23 +80,20 @@ module Bud
82
80
  @winners[key].delete t unless @winners[key].empty?
83
81
  end
84
82
  else
85
- raise "strange result from argagg finalizer"
83
+ raise Bud::Error, "strange result from argagg finalizer"
86
84
  end
87
85
  end
88
86
  @groups[key] ||= Array.new(@aggpairs.length)
89
87
  @groups[key][agg_ix] = agg
90
- #push_out(nil)
91
88
  end
92
89
  end
93
90
 
94
91
  def flush
95
- @groups.keys.each {|g|
96
- @winners[g].each{|t|
92
+ @groups.each_key do |g|
93
+ @winners[g].each do |t|
97
94
  push_out(t, false)
98
- }
99
- }
100
- #@groups = {}
101
- #@winners = {}
95
+ end
96
+ end
102
97
  end
103
98
  end
104
99
  end
@@ -14,7 +14,6 @@ module Bud
14
14
  @origpreds = preds
15
15
  @localpreds = nil
16
16
  @selfjoins = []
17
- @input_bufs = [[],[]]
18
17
  @missing_keys = Set.new
19
18
 
20
19
  # if any elements on rellist are PushSHJoins, suck up their contents
@@ -265,20 +264,6 @@ module Bud
265
264
  return retval
266
265
  end
267
266
 
268
- # given a * expression over 2 collections, form all combos of items that
269
- # satisfy +preds+, and for any item from the 1st collection that has no
270
- # matches in the 2nd, nil-pad it and include it in the output.
271
- public
272
- def join(elem2, &blk)
273
- elem2 = elem2.to_push_elem unless elem2.class <= PushElement
274
- # This constructs a left-deep tree!
275
- join = Bud::PushSHJoin.new([self,elem2], @bud_instance, [])
276
- @bud_instance.push_joins[@bud_instance.this_stratum] << join
277
- elem2.wire_to(join)
278
- self.wire_to(join)
279
- return join
280
- end
281
-
282
267
  undef do_insert
283
268
 
284
269
  public
@@ -296,11 +281,7 @@ module Bud
296
281
  end
297
282
  raise Bud::Error, "item #{item.inspect} inserted into join from unknown source #{source.elem_name}" if offsets == $EMPTY
298
283
  offsets.each do |offset|
299
- buf = @input_bufs[offset]
300
- buf << item
301
- if buf.length >= ELEMENT_BUFSIZE
302
- flush_buf(buf, offset)
303
- end
284
+ insert_item(item, offset)
304
285
  end
305
286
  end
306
287
 
@@ -395,25 +376,6 @@ module Bud
395
376
  end
396
377
  end
397
378
 
398
- def flush_buf(buf, offset)
399
- buf.each do |item|
400
- insert_item(item, offset)
401
- end
402
- @input_bufs[offset] = []
403
- end
404
-
405
- public
406
- def flush
407
- @input_bufs.each_with_index do |buf, offset|
408
- flush_buf(buf, offset) if buf.length > 0
409
- end
410
- end
411
-
412
- public
413
- def stratum_end
414
- flush
415
- end
416
-
417
379
  ####
418
380
  # and now, the Bloom-facing methods
419
381
  # given a * expression over n collections, form all combinations of items
@@ -573,23 +535,66 @@ module Bud
573
535
  end
574
536
  end
575
537
 
576
- class PushNotIn < PushSHJoin
538
+
539
+ # Consider "u <= s.notin(t, s.a => t.b)". notin is a non-monotonic operator, where u depends positively on s,
540
+ # but negatively on t. Stratification ensures that t is fully computed in a lower stratum, which means that we
541
+ # can expect multiple iterators on s's side only. If t's scanner were to push its elemends down first, every
542
+ # insert of s merely needs to be cross checked with the cached elements of 't', and pushed down to the next
543
+ # element if s notin t. However, if s's scanner were to fire first, we have to wait until the first flush, at which
544
+ # point we are sure to have seen all the t-side tuples in this tick.
545
+ class PushNotIn < PushStatefulElement
577
546
  def initialize(rellist, bud_instance, preds=nil, &blk) # :nodoc: all
578
- if preds.nil? or preds.empty?
579
- preds = positionwise_preds(bud_instance, rellist)
547
+ @lhs, @rhs = rellist
548
+ @lhs_keycols = nil
549
+ @rhs_keycols = nil
550
+ name_in = "#{@lhs.tabname}_notin_#{@rhs.tabname}"
551
+ super(name_in, bud_instance)
552
+ setup_preds(preds) unless preds.empty?
553
+ @rhs_rcvd = false
554
+ @hash_tables = [{},{}]
555
+ @rhs_rcvd = false
556
+ if @lhs_keycols.nil? and blk.nil?
557
+ # pointwise comparison. Could use zip, but it creates an array for each field pair
558
+ blk = lambda {|lhs, rhs|
559
+ lhs.to_a == rhs.to_a
560
+ }
580
561
  end
581
- super(rellist, bud_instance, preds)
582
562
  set_block(&blk)
583
- @cols = rellist[0].cols
584
- @exclude = Set.new
585
563
  end
586
564
 
587
- def positionwise_preds(bud_instance, rels)
588
- # pairwise colnames, for the minimum number of columns from either
589
- return [] if rels[0].cols.length != rels[1].cols.length
590
- pairs = rels[0].cols.zip(rels[1].cols)
591
- # make a hash of each pair, and return an array of hashes as expected by setup_pred
592
- [pairs.reduce(Hash.new) {|h, it| h[it[0]]=it[1]; h}]
565
+ def setup_preds(preds)
566
+ # This is simpler than PushSHJoin's setup_preds, because notin is a binary operator where both lhs and rhs are
567
+ # collections.
568
+ # preds an array of hash_pairs. For now assume that the attributes are in the same order as the tables.
569
+ @lhs_keycols, @rhs_keycols = preds.reduce([[], []]) do |memo, item|
570
+ # each item is a hash
571
+ l = item.keys[0]
572
+ r = item.values[0]
573
+ memo[0] << find_col(l, @lhs)
574
+ memo[1] << find_col(r, @rhs)
575
+ memo
576
+ end
577
+ end
578
+ def find_col(colspec, rel)
579
+ if colspec.is_a? Symbol
580
+ col_desc = rel.send(colspec)
581
+ raise "Unknown column #{rel} in #{@rel.tabname}" if col_desc.nil?
582
+ elsif colspec.is_a? Array
583
+ col_desc = colspec
584
+ else
585
+ raise "Symbol or column spec expected. Got #{colspec}"
586
+ end
587
+ col_desc[1] # col_desc is of the form [tabname, colnum, colname]
588
+ end
589
+
590
+ def get_key(item, offset)
591
+ keycols = offset == 0 ? @lhs_keycols : @rhs_keycols
592
+ keycols.nil? ? $EMPTY : keycols.map{|col| item[col]}
593
+ end
594
+
595
+ public
596
+ def invalidate_at_tick
597
+ true
593
598
  end
594
599
 
595
600
  public
@@ -597,40 +602,68 @@ module Bud
597
602
  true
598
603
  end
599
604
 
600
- def push_out(item) # item is a two element array, a tuple from rels[0] and rels[1]
601
- # called from PushSHJoin::process_matches, but we don't push the item downstream until stratum end
602
- do_exclude = @blk.nil? ? true : @blk.call(item)
603
- #puts "#{item} ===> #{do_exclude}"
604
- @exclude << item[0] if do_exclude
605
+ def insert(item, source)
606
+ offset = source == @lhs ? 0 : 1
607
+ key = get_key(item, offset)
608
+ #puts "#{key}, #{item}, #{offset}"
609
+ (@hash_tables[offset][key] ||= Set.new).add item
610
+ if @rhs_rcvd and offset == 0
611
+ push_lhs(key, item)
612
+ end
605
613
  end
606
614
 
607
- public
608
- def invalidate_cache
609
- @exclude.clear
615
+ def flush
616
+ # When flush is called the first time, both lhs and rhs scanners have been invoked, and because of stratification
617
+ # we know that the rhs is not growing any more, until the next tick.
618
+ unless @rhs_rcvd
619
+ @rhs_rcvd = true
620
+ @hash_tables[0].map{|key,values|
621
+ values.each{|item|
622
+ push_lhs(key, item)
623
+ }
624
+ }
625
+ end
610
626
  end
611
627
 
612
- def stratum_end
613
- flush
614
- # Scan through all the cached left rel values, and push out those that are
615
- # not in exclude
616
- @hash_tables[0].each_value do |s|
617
- s.each do |item|
618
- next if @exclude.member? item
619
- @outputs.each do |ou|
620
- if ou.class <= Bud::PushElement
621
- ou.insert(item, self)
622
- elsif ou.class <= Bud::BudCollection
623
- ou.do_insert(item, ou.new_delta)
624
- else
625
- raise Bud::Error, "expected either a PushElement or a BudCollection"
626
- end
627
- end
628
- # for all the following, o is a BudCollection
629
- @deletes.each{|o| o.pending_delete([item])}
630
- @delete_keys.each{|o| o.pending_delete_keys([item])}
631
- @pendings.each{|o| o.pending_merge([item])}
628
+ def push_lhs(key, lhs_item)
629
+ rhs_values = @hash_tables[1][key]
630
+ process_match(lhs_item, rhs_values)
631
+ end
632
+
633
+ def process_match(lhs_item, rhs_values)
634
+ exclude = true
635
+ if rhs_values.nil?
636
+ # no corresponding rhs. Include in output
637
+ exclude = false
638
+ elsif not @blk.nil?
639
+ # for any lhs * rhs pair, if block returns true, do not push lhs. lhs is pushed
640
+ # only if there is no match (anti-join)
641
+ exclude = rhs_values.any?{|rhs_item| @blk.call(lhs_item, rhs_item)}
642
+ end
643
+ unless exclude
644
+ push_out(lhs_item)
645
+ end
646
+ end
647
+
648
+ public
649
+ def push_out(item)
650
+ @outputs.each do |ou|
651
+ if ou.class <= Bud::PushElement
652
+ ou.insert(item, self)
653
+ elsif ou.class <= Bud::BudCollection
654
+ ou.do_insert(item, ou.new_delta)
655
+ else
656
+ raise Bud::Error, "expected either a PushElement or a BudCollection"
632
657
  end
633
658
  end
659
+ # for all the following, o is a BudCollection
660
+ @deletes.each{|o| o.pending_delete([item])}
661
+ @delete_keys.each{|o| o.pending_delete_keys([item])}
662
+ @pendings.each{|o| o.pending_merge([item])}
634
663
  end
635
664
  end
665
+ def stratum_end
666
+ @hash_tables = [{},{}]
667
+ @rhs_rcvd = false
668
+ end
636
669
  end
data/lib/bud/graphs.rb CHANGED
@@ -74,8 +74,10 @@ class GraphGen #:nodoc: all
74
74
  # bottom if the predicate is not in a NEG/+ cycle. otherwise,
75
75
  # its name is "CYC" + concat(sort(predicate names))
76
76
  depends.each do |d|
77
- head = d.lhs
78
- body = d.body
77
+ # b/c bud_obj was pruned before serialization...
78
+ (bud_obj, rule_id, lhs, op, body, nm) = d.to_a
79
+ head = lhs
80
+ body = body
79
81
 
80
82
  if @builtin_tables.has_key?(head.to_sym) or @builtin_tables.has_key?(body.to_sym)
81
83
  next
@@ -83,9 +85,9 @@ class GraphGen #:nodoc: all
83
85
 
84
86
  head = name_of(head)
85
87
  body = name_of(body)
86
- addonce(head, (head != d.lhs), true)
87
- addonce(body, (body != d.body))
88
- addedge(body, head, d.op, d.nm, (head != d.lhs), d.rule_id)
88
+ addonce(head, (head != lhs), true)
89
+ addonce(body, (body != body))
90
+ addedge(body, head, op, nm, (head != lhs), rule_id)
89
91
  end
90
92
  end
91
93
 
data/lib/bud/rebl.rb CHANGED
@@ -175,7 +175,7 @@ class ReblShell
175
175
  def self.do_exit
176
176
  begin
177
177
  lines = Readline::HISTORY.to_a.reverse.uniq.reverse
178
- lines = lines[-@@maxhistsize, @@maxhistsize] if lines.nitems>@@maxhistsize
178
+ lines = lines[-@@maxhistsize, @@maxhistsize] if lines.size > @@maxhistsize
179
179
  File::open(@@histfile, File::WRONLY|File::CREAT|File::TRUNC) do |io|
180
180
  io.puts lines.join("\n")
181
181
  end
data/lib/bud/rewrite.rb CHANGED
@@ -53,16 +53,40 @@ class RuleRewriter < Ruby2Ruby # :nodoc: all
53
53
  ty
54
54
  end
55
55
 
56
+ def call_to_id(exp)
57
+ # convert a series of nested calls, a sexp of the form
58
+ # s(:call,
59
+ # s(:call, s(:call, nil, :a, s(:arglist)), :b, s(:arglist)),
60
+ # :bar ,
61
+ # s(:arglist)))
62
+ # to the string "a.b.bar"
63
+ raise "Malformed exp: #{exp}" unless (exp[0] == :call)
64
+ _, recv, op, args = exp
65
+ return recv.nil? ? op.to_s : call_to_id(recv) + "." + op.to_s
66
+ end
67
+
56
68
  def process_call(exp)
57
69
  recv, op, args = exp
58
70
  if OP_LIST.include?(op) and @context[1] == :block and @context.length == 4
59
71
  # NB: context.length is 4 when see a method call at the top-level of a
60
72
  # :defn block -- this is where we expect Bloom statements to appear
61
73
  do_rule(exp)
74
+ elsif op == :notin
75
+ # special case. In the rule "z <= x.notin(y)", z depends positively on x, but negatively on y
76
+ # See further explanation in the "else" section for why this is a special case.
77
+ notintab = call_to_id(args[1]) # args expected to be of the form (:arglist (:call nil :y ...))
78
+ @tables[notintab.to_s] = true # "true" denotes non-monotonic dependency.
79
+ super
62
80
  else
81
+ # Parse a call of the form a.b.c.foo.
82
+ # In the most general case, a.b is a nested module, a.b.c is a collection in that module, and
83
+ # a.b.c.foo is either a method or a field. If it is a method, and non-monotonic at that, we
84
+ # register a dependency between lhs and the table a.b.c.
85
+ # Note that notin is treated differently because in a.b.c.notin(d.e.f), we register a non-monotonic
86
+ # dependency of lhs on "d.e.f", not with "a.b.c"
63
87
  ty, qn, _ = exp_id_type(recv, op, args) # qn = qualified name
64
88
  if ty == :collection
65
- @tables[qn] = @nm if @collect
89
+ (@tables[qn] = @nm if @collect) unless @tables[qn]
66
90
  #elsif ty == :import .. do nothing
67
91
  elsif ty == :not_coll_id
68
92
  # check if receiver is a collection, and further if the current exp
data/lib/bud/viz.rb CHANGED
@@ -4,6 +4,7 @@ require 'gchart'
4
4
  require 'bud/state'
5
5
 
6
6
  class VizOnline #:nodoc: all
7
+ attr_reader :logtab
7
8
  def initialize(bud_instance)
8
9
  @bud_instance = bud_instance
9
10
  @meta_tables = {'t_rules' => 1, 't_depends' => 1, 't_table_info' => 1, 't_cycle' => 1, 't_stratum' => 1, 't_depends_tc' => 1, 't_table_schema' => 1, 't_provides' => 1}
@@ -62,7 +63,7 @@ class VizOnline #:nodoc: all
62
63
  # bud instances cannot/must not be serialized.
63
64
  if row[0].class <= Bud
64
65
  row = row.to_a if row.class != Array
65
- row = row[1..-1] if row[0].class <= Bud
66
+ row = [row[0].class.to_s] + row[1..-1] if row[0].class <= Bud
66
67
  end
67
68
  newrow = [tab, @bud_instance.budtime, row]
68
69
  begin
data/lib/bud/viz_util.rb CHANGED
@@ -31,7 +31,7 @@ class VizHelper
31
31
 
32
32
  def summarize(dir, schema)
33
33
  table_io = {}
34
- cardinalities.sort{|a, b| a[0] <=> b[0]}.each do |card|
34
+ cardinalities.to_a.sort{|a, b| a[0] <=> b[0]}.each do |card|
35
35
  table_io["#{card.table}_#{card.bud_time}"] = start_table(dir, card.table, card.bud_time, schema[card.table])
36
36
  end
37
37
 
@@ -44,7 +44,7 @@ class VizHelper
44
44
  end
45
45
 
46
46
  # fix: nested loops
47
- times.sort.each do |time|
47
+ times.to_a.sort.each do |time|
48
48
  card_info = {}
49
49
  cardinalities.each do |card|
50
50
  if card.bud_time == time.bud_time
@@ -178,14 +178,16 @@ module VizUtil #:nodoc: all
178
178
  rules = {}
179
179
  convertor = Syntax::Convertors::HTML.for_syntax "ruby"
180
180
  shredded_rules.each do |s|
181
- fout = File.new("#{output_base}/#{s.rule_id}.html", "w+")
181
+ # b/c accessors don't make it through serialization anymore
182
+ bud_obj, rule_id, lhs, op, src, orig_src, nm_funcs_called = s.to_a
183
+ fout = File.new("#{output_base}/#{rule_id}.html", "w+")
182
184
  fout.puts header
183
- fout.puts "<h1>Rule #{s.rule_id}</h1><br>"
185
+ fout.puts "<h1>Rule #{rule_id}</h1><br>"
184
186
 
185
- c = convertor.convert(s.orig_src)
187
+ c = convertor.convert(orig_src)
186
188
  c.sub!(/^<pre>/, "<pre class=\"code\" style='font-size:20px'>\n")
187
189
  fout.puts c
188
- rules[s.rule_id] = [s.lhs, s.orig_src]
190
+ rules[rule_id] = [lhs, orig_src]
189
191
  fout.close
190
192
  end
191
193
 
@@ -299,14 +301,13 @@ END_JS
299
301
  key = MessagePack.unpack(k)
300
302
  tab = key.shift
301
303
  time = key.shift
302
- # paa
304
+ # paa: after switch to 1.9, v appears to always be empty
303
305
  tup = key[0]
304
306
  MessagePack.unpack(v).each {|val| tup << val}
305
307
  if meta_tabs[tab]
306
308
  raise "non-zero budtime.(tab=#{tab}, time=#{time}) sure this is metadata?" if time != 0 #and strict
307
309
  meta[meta_tabs[tab]] ||= []
308
310
  meta[meta_tabs[tab]] << tup
309
- #ret << tup
310
311
  else
311
312
  data << [time, tab, tup]
312
313
  end
@@ -322,11 +323,15 @@ END_JS
322
323
  unless meta[:schminf][tab]
323
324
  meta[:schminf][tab] = []
324
325
  end
325
- meta[:schminf][tab][ts[2]] = ts[1]
326
+ meta[:schminf][tab][ts[2]] = ts[1] if ts[2]
326
327
  end
327
328
  return meta, data
328
329
  end
329
330
 
331
+ def mapstr(list)
332
+ list.map{|s| "<th> #{s} </th>"}.join(" ")
333
+ end
334
+
330
335
  def start_table(dir, tab, time, schema)
331
336
  str = "#{dir}/#{tab}_#{time}.html"
332
337
  fout = File.new(str, "w")
@@ -334,12 +339,14 @@ END_JS
334
339
  fout.puts "<table border=1>"
335
340
  # issue with _snd schemas
336
341
  if !schema.nil? and schema[0] == "c_bud_time"
337
- fout.puts "<tr>"
342
+ fout.puts "<tr>"
338
343
  if schema[1].length == 2 and schema[1][0].class == Array and schema[1][1].class == Array
339
- fout.puts schema[1][0].map{|s| "<th> #{s} </th>"}.join(" ")
340
- fout.puts schema[1][1].map{|s| "<th> #{s} </th>"}.join(" ")
344
+ fout.puts mapstr(schema[1][0])
345
+ fout.puts mapstr(schema[1][1])
346
+ elsif schema[1].class == String
347
+ fout.puts mapstr(schema[1..-1])
341
348
  else
342
- fout.puts schema[1].map{|s| "<th> #{s} </th>"}.join(" ")
349
+ fout.puts mapstr(schema[1])
343
350
  end
344
351
  fout.puts "<tr>"
345
352
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,11 +13,11 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2012-04-10 00:00:00.000000000 Z
16
+ date: 2012-05-20 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: eventmachine
20
- requirement: &70136220748940 !ruby/object:Gem::Requirement
20
+ requirement: !ruby/object:Gem::Requirement
21
21
  none: false
22
22
  requirements:
23
23
  - - ! '>='
@@ -25,10 +25,15 @@ dependencies:
25
25
  version: '0'
26
26
  type: :runtime
27
27
  prerelease: false
28
- version_requirements: *70136220748940
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
29
34
  - !ruby/object:Gem::Dependency
30
35
  name: fastercsv
31
- requirement: &70136220747220 !ruby/object:Gem::Requirement
36
+ requirement: !ruby/object:Gem::Requirement
32
37
  none: false
33
38
  requirements:
34
39
  - - ! '>='
@@ -36,10 +41,15 @@ dependencies:
36
41
  version: '0'
37
42
  type: :runtime
38
43
  prerelease: false
39
- version_requirements: *70136220747220
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
40
50
  - !ruby/object:Gem::Dependency
41
51
  name: gchart
42
- requirement: &70136220763100 !ruby/object:Gem::Requirement
52
+ requirement: !ruby/object:Gem::Requirement
43
53
  none: false
44
54
  requirements:
45
55
  - - ! '>='
@@ -47,10 +57,15 @@ dependencies:
47
57
  version: '0'
48
58
  type: :runtime
49
59
  prerelease: false
50
- version_requirements: *70136220763100
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
51
66
  - !ruby/object:Gem::Dependency
52
67
  name: getopt
53
- requirement: &70136220762540 !ruby/object:Gem::Requirement
68
+ requirement: !ruby/object:Gem::Requirement
54
69
  none: false
55
70
  requirements:
56
71
  - - ! '>='
@@ -58,10 +73,15 @@ dependencies:
58
73
  version: '0'
59
74
  type: :runtime
60
75
  prerelease: false
61
- version_requirements: *70136220762540
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
62
82
  - !ruby/object:Gem::Dependency
63
83
  name: msgpack
64
- requirement: &70136220761500 !ruby/object:Gem::Requirement
84
+ requirement: !ruby/object:Gem::Requirement
65
85
  none: false
66
86
  requirements:
67
87
  - - ! '>='
@@ -69,10 +89,15 @@ dependencies:
69
89
  version: '0'
70
90
  type: :runtime
71
91
  prerelease: false
72
- version_requirements: *70136220761500
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
73
98
  - !ruby/object:Gem::Dependency
74
99
  name: ruby-graphviz
75
- requirement: &70136220760140 !ruby/object:Gem::Requirement
100
+ requirement: !ruby/object:Gem::Requirement
76
101
  none: false
77
102
  requirements:
78
103
  - - ! '>='
@@ -80,10 +105,15 @@ dependencies:
80
105
  version: '0'
81
106
  type: :runtime
82
107
  prerelease: false
83
- version_requirements: *70136220760140
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
84
114
  - !ruby/object:Gem::Dependency
85
115
  name: ruby2ruby
86
- requirement: &70136220759200 !ruby/object:Gem::Requirement
116
+ requirement: !ruby/object:Gem::Requirement
87
117
  none: false
88
118
  requirements:
89
119
  - - <
@@ -91,10 +121,15 @@ dependencies:
91
121
  version: 1.3.1
92
122
  type: :runtime
93
123
  prerelease: false
94
- version_requirements: *70136220759200
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - <
128
+ - !ruby/object:Gem::Version
129
+ version: 1.3.1
95
130
  - !ruby/object:Gem::Dependency
96
131
  name: ruby_parser
97
- requirement: &70136220758700 !ruby/object:Gem::Requirement
132
+ requirement: !ruby/object:Gem::Requirement
98
133
  none: false
99
134
  requirements:
100
135
  - - ! '>='
@@ -102,10 +137,15 @@ dependencies:
102
137
  version: '0'
103
138
  type: :runtime
104
139
  prerelease: false
105
- version_requirements: *70136220758700
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
106
146
  - !ruby/object:Gem::Dependency
107
147
  name: superators19
108
- requirement: &70136220758040 !ruby/object:Gem::Requirement
148
+ requirement: !ruby/object:Gem::Requirement
109
149
  none: false
110
150
  requirements:
111
151
  - - ! '>='
@@ -113,10 +153,15 @@ dependencies:
113
153
  version: '0'
114
154
  type: :runtime
115
155
  prerelease: false
116
- version_requirements: *70136220758040
156
+ version_requirements: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
117
162
  - !ruby/object:Gem::Dependency
118
163
  name: syntax
119
- requirement: &70136220757320 !ruby/object:Gem::Requirement
164
+ requirement: !ruby/object:Gem::Requirement
120
165
  none: false
121
166
  requirements:
122
167
  - - ! '>='
@@ -124,10 +169,15 @@ dependencies:
124
169
  version: '0'
125
170
  type: :runtime
126
171
  prerelease: false
127
- version_requirements: *70136220757320
172
+ version_requirements: !ruby/object:Gem::Requirement
173
+ none: false
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
128
178
  - !ruby/object:Gem::Dependency
129
179
  name: uuid
130
- requirement: &70136220756700 !ruby/object:Gem::Requirement
180
+ requirement: !ruby/object:Gem::Requirement
131
181
  none: false
132
182
  requirements:
133
183
  - - ! '>='
@@ -135,10 +185,15 @@ dependencies:
135
185
  version: '0'
136
186
  type: :runtime
137
187
  prerelease: false
138
- version_requirements: *70136220756700
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ none: false
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
139
194
  - !ruby/object:Gem::Dependency
140
195
  name: minitest
141
- requirement: &70136220756180 !ruby/object:Gem::Requirement
196
+ requirement: !ruby/object:Gem::Requirement
142
197
  none: false
143
198
  requirements:
144
199
  - - ! '>='
@@ -146,7 +201,12 @@ dependencies:
146
201
  version: '0'
147
202
  type: :development
148
203
  prerelease: false
149
- version_requirements: *70136220756180
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ none: false
206
+ requirements:
207
+ - - ! '>='
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
150
210
  description: A prototype of the Bloom distributed programming language as a Ruby DSL.
151
211
  email:
152
212
  - bloomdevs@gmail.com
@@ -228,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
228
288
  version: '0'
229
289
  requirements: []
230
290
  rubyforge_project: bloom-lang
231
- rubygems_version: 1.8.17
291
+ rubygems_version: 1.8.24
232
292
  signing_key:
233
293
  specification_version: 3
234
294
  summary: A prototype Bloom DSL for distributed programming.