bud 0.0.2
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/LICENSE +9 -0
 - data/README +30 -0
 - data/bin/budplot +134 -0
 - data/bin/budvis +201 -0
 - data/bin/rebl +4 -0
 - data/docs/README.md +13 -0
 - data/docs/bfs.md +379 -0
 - data/docs/bfs.raw +251 -0
 - data/docs/bfs_arch.png +0 -0
 - data/docs/bloom-loop.png +0 -0
 - data/docs/bust.md +83 -0
 - data/docs/cheat.md +291 -0
 - data/docs/deploy.md +96 -0
 - data/docs/diffs +181 -0
 - data/docs/getstarted.md +296 -0
 - data/docs/intro.md +36 -0
 - data/docs/modules.md +112 -0
 - data/docs/operational.md +96 -0
 - data/docs/rebl.md +99 -0
 - data/docs/ruby_hooks.md +19 -0
 - data/docs/visualizations.md +75 -0
 - data/examples/README +1 -0
 - data/examples/basics/hello.rb +12 -0
 - data/examples/basics/out +1103 -0
 - data/examples/basics/out.new +856 -0
 - data/examples/basics/paths.rb +51 -0
 - data/examples/bust/README.md +9 -0
 - data/examples/bust/bustclient-example.rb +23 -0
 - data/examples/bust/bustinspector.html +135 -0
 - data/examples/bust/bustserver-example.rb +18 -0
 - data/examples/chat/README.md +9 -0
 - data/examples/chat/chat.rb +45 -0
 - data/examples/chat/chat_protocol.rb +8 -0
 - data/examples/chat/chat_server.rb +29 -0
 - data/examples/deploy/tokenring-ec2.rb +26 -0
 - data/examples/deploy/tokenring-local.rb +17 -0
 - data/examples/deploy/tokenring.rb +39 -0
 - data/lib/bud/aggs.rb +126 -0
 - data/lib/bud/bud_meta.rb +185 -0
 - data/lib/bud/bust/bust.rb +126 -0
 - data/lib/bud/bust/client/idempotence.rb +10 -0
 - data/lib/bud/bust/client/restclient.rb +49 -0
 - data/lib/bud/collections.rb +937 -0
 - data/lib/bud/depanalysis.rb +44 -0
 - data/lib/bud/deploy/countatomicdelivery.rb +50 -0
 - data/lib/bud/deploy/deployer.rb +67 -0
 - data/lib/bud/deploy/ec2deploy.rb +200 -0
 - data/lib/bud/deploy/localdeploy.rb +41 -0
 - data/lib/bud/errors.rb +15 -0
 - data/lib/bud/graphs.rb +405 -0
 - data/lib/bud/joins.rb +300 -0
 - data/lib/bud/rebl.rb +314 -0
 - data/lib/bud/rewrite.rb +523 -0
 - data/lib/bud/rtrace.rb +27 -0
 - data/lib/bud/server.rb +43 -0
 - data/lib/bud/state.rb +108 -0
 - data/lib/bud/storage/tokyocabinet.rb +170 -0
 - data/lib/bud/storage/zookeeper.rb +178 -0
 - data/lib/bud/stratify.rb +83 -0
 - data/lib/bud/viz.rb +65 -0
 - data/lib/bud.rb +797 -0
 - metadata +330 -0
 
    
        data/lib/bud/bud_meta.rb
    ADDED
    
    | 
         @@ -0,0 +1,185 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'bud/rewrite'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'bud/state'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'parse_tree'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'pp'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class BudMeta #:nodoc: all
         
     | 
| 
      
 7 
     | 
    
         
            +
              attr_reader :depanalysis
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def initialize(bud_instance, declarations)
         
     | 
| 
      
 10 
     | 
    
         
            +
                @bud_instance = bud_instance
         
     | 
| 
      
 11 
     | 
    
         
            +
                @declarations = declarations
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def meta_rewrite
         
     | 
| 
      
 15 
     | 
    
         
            +
                shred_rules
         
     | 
| 
      
 16 
     | 
    
         
            +
                top_stratum = stratify
         
     | 
| 
      
 17 
     | 
    
         
            +
                stratum_map = binaryrel2map(@bud_instance.t_stratum)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                rewritten_strata = Array.new(top_stratum + 2) { [] }
         
     | 
| 
      
 20 
     | 
    
         
            +
                @bud_instance.t_rules.each do |d|
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if d.op.to_s == '<='
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # deductive rules are assigned to strata based on
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # the basic datalog stratification algorithm
         
     | 
| 
      
 24 
     | 
    
         
            +
                    belongs_in = stratum_map[d.lhs]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    belongs_in ||= 0
         
     | 
| 
      
 26 
     | 
    
         
            +
                    rewritten_strata[belongs_in] << d.src
         
     | 
| 
      
 27 
     | 
    
         
            +
                  else
         
     | 
| 
      
 28 
     | 
    
         
            +
                    # all temporal rules are placed in the last stratum
         
     | 
| 
      
 29 
     | 
    
         
            +
                    rewritten_strata[top_stratum + 1] << d.src
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                @depanalysis = DepAnalysis.new
         
     | 
| 
      
 34 
     | 
    
         
            +
                @bud_instance.t_depends_tc.each {|d| @depanalysis.depends_tc << d}
         
     | 
| 
      
 35 
     | 
    
         
            +
                @bud_instance.t_provides.each {|p| @depanalysis.providing << p}
         
     | 
| 
      
 36 
     | 
    
         
            +
                3.times { @depanalysis.tick }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                @depanalysis.underspecified.each do |u|
         
     | 
| 
      
 39 
     | 
    
         
            +
                  puts "Warning: underspecified dataflow: #{u.inspect}"
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @bud_instance.t_underspecified << u
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
                dump_rewrite(rewritten_strata) if @bud_instance.options[:dump_rewrite]
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                return rewritten_strata
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              def binaryrel2map(rel)
         
     | 
| 
      
 48 
     | 
    
         
            +
                map = {}
         
     | 
| 
      
 49 
     | 
    
         
            +
                rel.each do |s|
         
     | 
| 
      
 50 
     | 
    
         
            +
                  raise Bud::BudError unless s.length == 2
         
     | 
| 
      
 51 
     | 
    
         
            +
                  map[s[0]] = s[1]
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
                return map
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              def shred_rules
         
     | 
| 
      
 57 
     | 
    
         
            +
                # to completely characterize the rules of a bud class we must extract
         
     | 
| 
      
 58 
     | 
    
         
            +
                # from all parent classes/modules
         
     | 
| 
      
 59 
     | 
    
         
            +
                # after making this pass, we no longer care about the names of methods.
         
     | 
| 
      
 60 
     | 
    
         
            +
                # we are shredding down to the granularity of rule heads.
         
     | 
| 
      
 61 
     | 
    
         
            +
                seed = 0
         
     | 
| 
      
 62 
     | 
    
         
            +
                rulebag = {}
         
     | 
| 
      
 63 
     | 
    
         
            +
                @bud_instance.class.ancestors.reverse.each do |anc|
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @declarations.each do |meth_name|
         
     | 
| 
      
 65 
     | 
    
         
            +
                    rw = rewrite_rule_block(anc, meth_name, seed)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    if rw
         
     | 
| 
      
 67 
     | 
    
         
            +
                      seed = rw.rule_indx
         
     | 
| 
      
 68 
     | 
    
         
            +
                      rulebag[meth_name] = rw
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                rulebag.each_value do |v|
         
     | 
| 
      
 74 
     | 
    
         
            +
                  v.rules.each {|r| @bud_instance.t_rules << r}
         
     | 
| 
      
 75 
     | 
    
         
            +
                  v.depends.each {|d| @bud_instance.t_depends << d}
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              def rewrite_rule_block(klass, block_name, seed)
         
     | 
| 
      
 80 
     | 
    
         
            +
                parse_tree = ParseTree.translate(klass, block_name)
         
     | 
| 
      
 81 
     | 
    
         
            +
                return unless parse_tree.first
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                pt = Unifier.new.process(parse_tree)
         
     | 
| 
      
 84 
     | 
    
         
            +
                pp pt if @bud_instance.options[:dump_ast]
         
     | 
| 
      
 85 
     | 
    
         
            +
                begin
         
     | 
| 
      
 86 
     | 
    
         
            +
                  check_rule_ast(pt)
         
     | 
| 
      
 87 
     | 
    
         
            +
                rescue Exception => e
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # try to "generate" the source code associated with the problematic
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # block, so as to generate a more meaningful error message.
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # if this parse fails, return the original exception (not the new one).
         
     | 
| 
      
 91 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 92 
     | 
    
         
            +
                    code = Ruby2Ruby.new.process(pt)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  rescue Exception => sub_e
         
     | 
| 
      
 94 
     | 
    
         
            +
                    raise e, "Error parsing rule block #{block_name}.  Could not extract source."
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  raise e, "Error parsing rule block #{block_name}:\n#{code}"
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                rewriter = RuleRewriter.new(seed, @bud_instance)
         
     | 
| 
      
 100 
     | 
    
         
            +
                rewriter.process(pt)
         
     | 
| 
      
 101 
     | 
    
         
            +
                return rewriter
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
              # Perform some basic sanity checks on the AST of a rule block. We expect a
         
     | 
| 
      
 105 
     | 
    
         
            +
              # rule block to consist of a :defn, a nested :scope, and then a sequence of
         
     | 
| 
      
 106 
     | 
    
         
            +
              # statements. Each statement is a :call node.
         
     | 
| 
      
 107 
     | 
    
         
            +
              def check_rule_ast(pt)
         
     | 
| 
      
 108 
     | 
    
         
            +
                # :defn format: node tag, block name, args, nested scope
         
     | 
| 
      
 109 
     | 
    
         
            +
                raise Bud::CompileError if pt.sexp_type != :defn
         
     | 
| 
      
 110 
     | 
    
         
            +
                scope = pt[3]
         
     | 
| 
      
 111 
     | 
    
         
            +
                raise Bud::CompileError if scope.sexp_type != :scope
         
     | 
| 
      
 112 
     | 
    
         
            +
                block = scope[1]
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                block.each_with_index do |n,i|
         
     | 
| 
      
 115 
     | 
    
         
            +
                  if i == 0
         
     | 
| 
      
 116 
     | 
    
         
            +
                    raise Bud::CompileError if n != :block
         
     | 
| 
      
 117 
     | 
    
         
            +
                    next
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  raise Bud::CompileError if n.sexp_type != :call
         
     | 
| 
      
 121 
     | 
    
         
            +
                  raise Bud::CompileError unless n.length == 4
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  # Rule format: call tag, lhs, op, rhs
         
     | 
| 
      
 124 
     | 
    
         
            +
                  tag, lhs, op, rhs = n
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  # Check that LHS references a named collection
         
     | 
| 
      
 127 
     | 
    
         
            +
                  raise Bud::CompileError if lhs.nil? or lhs.sexp_type != :call
         
     | 
| 
      
 128 
     | 
    
         
            +
                  lhs_name = lhs[2]
         
     | 
| 
      
 129 
     | 
    
         
            +
                  unless @bud_instance.tables.has_key? lhs_name.to_sym
         
     | 
| 
      
 130 
     | 
    
         
            +
                    raise Bud::CompileError, "Table does not exist: '#{lhs_name}'"
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  # Check that op is a legal Bloom operator
         
     | 
| 
      
 134 
     | 
    
         
            +
                  unless [:<, :<=].include? op
         
     | 
| 
      
 135 
     | 
    
         
            +
                    raise Bud::CompileError, "Illegal operator: '#{op}'"
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  # Check superator invocation. A superator that begins with "<" is parsed
         
     | 
| 
      
 139 
     | 
    
         
            +
                  # as a call to the binary :< operator. The right operand to :< is a :call
         
     | 
| 
      
 140 
     | 
    
         
            +
                  # node; the LHS of the :call is the actual rule body, the :call's oper is
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # the rest of the superator (unary ~, -, +), and the RHS is empty.  Note
         
     | 
| 
      
 142 
     | 
    
         
            +
                  # that ParseTree encodes unary "-" and "+" as :-@ and :-+, respectively.
         
     | 
| 
      
 143 
     | 
    
         
            +
                  # XXX: We don't check for illegal superators (e.g., "<--"). That would be
         
     | 
| 
      
 144 
     | 
    
         
            +
                  # tricky, because they are encoded as a nested unary op in the rule body.
         
     | 
| 
      
 145 
     | 
    
         
            +
                  if op == :<
         
     | 
| 
      
 146 
     | 
    
         
            +
                    raise Bud::CompileError unless rhs.sexp_type == :arglist
         
     | 
| 
      
 147 
     | 
    
         
            +
                    body = rhs[1]
         
     | 
| 
      
 148 
     | 
    
         
            +
                    raise Bud::CompileError unless body.sexp_type == :call
         
     | 
| 
      
 149 
     | 
    
         
            +
                    op_tail = body[2]
         
     | 
| 
      
 150 
     | 
    
         
            +
                    raise Bud::CompileError unless [:~, :-@, :+@].include? op_tail
         
     | 
| 
      
 151 
     | 
    
         
            +
                    rhs_args = body[3]
         
     | 
| 
      
 152 
     | 
    
         
            +
                    raise Bud::CompileError unless rhs_args.sexp_type == :arglist
         
     | 
| 
      
 153 
     | 
    
         
            +
                    raise Bud::CompileError if rhs_args.length != 1
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
              end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
              def stratify
         
     | 
| 
      
 159 
     | 
    
         
            +
                strat = Stratification.new
         
     | 
| 
      
 160 
     | 
    
         
            +
                @bud_instance.t_depends.each {|d| strat.depends << d}
         
     | 
| 
      
 161 
     | 
    
         
            +
                strat.tick
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                # Copy computed data back into Bud runtime
         
     | 
| 
      
 164 
     | 
    
         
            +
                strat.stratum.each {|s| @bud_instance.t_stratum << s}
         
     | 
| 
      
 165 
     | 
    
         
            +
                strat.depends_tc.each {|d| @bud_instance.t_depends_tc << d}
         
     | 
| 
      
 166 
     | 
    
         
            +
                strat.cycle.each {|c| @bud_instance.t_cycle << c}
         
     | 
| 
      
 167 
     | 
    
         
            +
                if strat.top_strat.length > 0
         
     | 
| 
      
 168 
     | 
    
         
            +
                  top = strat.top_strat.first.stratum
         
     | 
| 
      
 169 
     | 
    
         
            +
                else
         
     | 
| 
      
 170 
     | 
    
         
            +
                  top = 1
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
                return top
         
     | 
| 
      
 173 
     | 
    
         
            +
              end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
              def dump_rewrite(strata)
         
     | 
| 
      
 176 
     | 
    
         
            +
                fout = File.new("#{@bud_instance.class}_rewritten.txt", "w")
         
     | 
| 
      
 177 
     | 
    
         
            +
                fout.puts "Declarations:"
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                strata.each_with_index do |src_ary, i|
         
     | 
| 
      
 180 
     | 
    
         
            +
                  text = src_ary.join("\n")
         
     | 
| 
      
 181 
     | 
    
         
            +
                  fout.puts "R[#{i}]:\n#{text}"
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
                fout.close
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,126 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'bud'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'socket'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'cgi'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            HTTP_VERBS = ["GET", "POST"] #, "DELETE"]
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # a RESTful interface to Bloom code
         
     | 
| 
      
 11 
     | 
    
         
            +
            module Bust
         
     | 
| 
      
 12 
     | 
    
         
            +
              include Bud
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              # used this for inspiration:
         
     | 
| 
      
 15 
     | 
    
         
            +
              # http://blogs.msdn.com/b/abhinaba/archive/2005/10/14/474841.aspx
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              # allow state to be queried easily
         
     | 
| 
      
 18 
     | 
    
         
            +
              state do
         
     | 
| 
      
 19 
     | 
    
         
            +
                table :t_table_info, [:tab_name, :tab_type]
         
     | 
| 
      
 20 
     | 
    
         
            +
                table :t_table_schema, [:tab_name, :scheme]
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              bootstrap do
         
     | 
| 
      
 24 
     | 
    
         
            +
                # copied from peter's code; this should probably be in the Bud runtime or in
         
     | 
| 
      
 25 
     | 
    
         
            +
                # some meta module
         
     | 
| 
      
 26 
     | 
    
         
            +
                @tables.each do |t|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  t_table_schema << [t[0], t[1].schema.clone]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  t_table_info << [t[0], t[1].class.to_s]
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                q = Queue.new
         
     | 
| 
      
 32 
     | 
    
         
            +
                Thread.start(self) do |bud|
         
     | 
| 
      
 33 
     | 
    
         
            +
                  BustClass.new(bud, q)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
                # Wait for socket to be ready before we return from bootstrap.
         
     | 
| 
      
 36 
     | 
    
         
            +
                q.pop
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              class BustClass
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                class BustHandler
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def initialize(session, request, body, bud)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @session = session
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @request = request
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @body = body
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @bud = bud
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def serve()
         
     | 
| 
      
 50 
     | 
    
         
            +
                    puts "Request: " + @request
         
     | 
| 
      
 51 
     | 
    
         
            +
                    puts "Body: " + @body.inspect if @body
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    for type in HTTP_VERBS
         
     | 
| 
      
 54 
     | 
    
         
            +
                      if @request =~ Regexp.new(type + " .* HTTP*")
         
     | 
| 
      
 55 
     | 
    
         
            +
                        break reqstr = @request.gsub(Regexp.new(type + " "), '').gsub(/ HTTP.*/, '')
         
     | 
| 
      
 56 
     | 
    
         
            +
                      end
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    uri = URI.parse(reqstr)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    uri_params = {}
         
     | 
| 
      
 61 
     | 
    
         
            +
                    uri_params = CGI.parse(uri.query) if uri.query
         
     | 
| 
      
 62 
     | 
    
         
            +
                    table_name = uri.path[1..-1].split(".")[0] # hack; we always return JSON
         
     | 
| 
      
 63 
     | 
    
         
            +
                    # "Access-Control-Allow-Origin: *" disables same-origin policy to allow
         
     | 
| 
      
 64 
     | 
    
         
            +
                    # XMLHttpRequests from any origin
         
     | 
| 
      
 65 
     | 
    
         
            +
                    success = "HTTP/1.1 200 OK\r\nServer: Bud\r\nContent-type: application/json\r\nAccess-Control-Allow-Origin: *\r\n\r\n"
         
     | 
| 
      
 66 
     | 
    
         
            +
                    failure = "HTTP/1.1 404 Object Not Found\r\nServer: Bud\r\nAccess-Control-Allow-Origin: *\r\n\r\n"
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 69 
     | 
    
         
            +
                      if @request =~ /GET .* HTTP*/
         
     | 
| 
      
 70 
     | 
    
         
            +
                        puts "GET shouldn't have body" if @body
         
     | 
| 
      
 71 
     | 
    
         
            +
                        # select the appropriate elements from the table
         
     | 
| 
      
 72 
     | 
    
         
            +
                        desired_elements = (eval "@bud." + table_name).find_all do |t|
         
     | 
| 
      
 73 
     | 
    
         
            +
                          uri_params.all? {|k, v| (eval "t." + k.to_s) == v[0]}
         
     | 
| 
      
 74 
     | 
    
         
            +
                        end
         
     | 
| 
      
 75 
     | 
    
         
            +
                        @session.print success
         
     | 
| 
      
 76 
     | 
    
         
            +
                        @session.print desired_elements.to_json
         
     | 
| 
      
 77 
     | 
    
         
            +
                      elsif @request =~ /POST .* HTTP*/
         
     | 
| 
      
 78 
     | 
    
         
            +
                        # instantiate a new tuple
         
     | 
| 
      
 79 
     | 
    
         
            +
                        tuple_to_insert = []
         
     | 
| 
      
 80 
     | 
    
         
            +
                        @body.each do |k, v|
         
     | 
| 
      
 81 
     | 
    
         
            +
                          index = (eval "@bud." + table_name).schema.find_index(k.to_sym)
         
     | 
| 
      
 82 
     | 
    
         
            +
                          for i in (tuple_to_insert.size..index)
         
     | 
| 
      
 83 
     | 
    
         
            +
                            tuple_to_insert << nil
         
     | 
| 
      
 84 
     | 
    
         
            +
                          end
         
     | 
| 
      
 85 
     | 
    
         
            +
                          tuple_to_insert[index] = v[0]
         
     | 
| 
      
 86 
     | 
    
         
            +
                        end
         
     | 
| 
      
 87 
     | 
    
         
            +
                        # actually insert the puppy
         
     | 
| 
      
 88 
     | 
    
         
            +
                        @bud.async_do { (eval "@bud." + table_name) << tuple_to_insert }
         
     | 
| 
      
 89 
     | 
    
         
            +
                        @session.print success
         
     | 
| 
      
 90 
     | 
    
         
            +
                      end
         
     | 
| 
      
 91 
     | 
    
         
            +
                    rescue Exception
         
     | 
| 
      
 92 
     | 
    
         
            +
                      puts "exception: #{$!}"
         
     | 
| 
      
 93 
     | 
    
         
            +
                      @session.print failure
         
     | 
| 
      
 94 
     | 
    
         
            +
                    ensure
         
     | 
| 
      
 95 
     | 
    
         
            +
                      @session.close
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def initialize(bud, q)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  # allow user-configurable port
         
     | 
| 
      
 103 
     | 
    
         
            +
                  server = TCPServer.new(bud.ip, (bud.options[:bust_port] or 8080))
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # We're now ready to accept connections.
         
     | 
| 
      
 105 
     | 
    
         
            +
                  q << true
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 108 
     | 
    
         
            +
                    session = server.accept
         
     | 
| 
      
 109 
     | 
    
         
            +
                    request = session.gets
         
     | 
| 
      
 110 
     | 
    
         
            +
                    length = nil
         
     | 
| 
      
 111 
     | 
    
         
            +
                    body = nil
         
     | 
| 
      
 112 
     | 
    
         
            +
                    while req = session.gets
         
     | 
| 
      
 113 
     | 
    
         
            +
                      length = Integer(req.split(" ")[1]) if req.match(/^Content-Length:/)
         
     | 
| 
      
 114 
     | 
    
         
            +
                      if req.match(/^\r\n$/)
         
     | 
| 
      
 115 
     | 
    
         
            +
                        body = CGI.parse(session.read(length)) if length
         
     | 
| 
      
 116 
     | 
    
         
            +
                        break
         
     | 
| 
      
 117 
     | 
    
         
            +
                      end
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                    Thread.start(session, request, body, bud) do |session, request, body, bud|
         
     | 
| 
      
 120 
     | 
    
         
            +
                      BustHandler.new(session, request, body, bud).serve()
         
     | 
| 
      
 121 
     | 
    
         
            +
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
              end
         
     | 
| 
      
 126 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'bud'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'nestful'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'bud/bust/client/idempotence'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module RestClient
         
     | 
| 
      
 7 
     | 
    
         
            +
              include Idempotence # :nodoc: all
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              state do
         
     | 
| 
      
 10 
     | 
    
         
            +
                # complains about underspecified dataflow because it can't see the
         
     | 
| 
      
 11 
     | 
    
         
            +
                # nested rules...
         
     | 
| 
      
 12 
     | 
    
         
            +
                interface input, :rest_req, [:rid, :verb, :form, :url, :params]
         
     | 
| 
      
 13 
     | 
    
         
            +
                interface output, :rest_response, [:rid, :resp, :exception]
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # we don't really need to store this i suppose
         
     | 
| 
      
 16 
     | 
    
         
            +
                scratch :rest_req_thread, [:thread]
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              bloom :rest_client do
         
     | 
| 
      
 20 
     | 
    
         
            +
                rest_req_thread <= rest_req.map do |req|
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # start up a new thread to deal with the response
         
     | 
| 
      
 22 
     | 
    
         
            +
                  [Thread.start(req, self) do |r, bud|
         
     | 
| 
      
 23 
     | 
    
         
            +
                     params = (r.params or {})
         
     | 
| 
      
 24 
     | 
    
         
            +
                     begin
         
     | 
| 
      
 25 
     | 
    
         
            +
                       case r.verb
         
     | 
| 
      
 26 
     | 
    
         
            +
                       when :get
         
     | 
| 
      
 27 
     | 
    
         
            +
                         get_hash = {:params => params}
         
     | 
| 
      
 28 
     | 
    
         
            +
                         get_hash[:format] = r.form if r.form
         
     | 
| 
      
 29 
     | 
    
         
            +
                         resp_tuple = [r.rid, Nestful.get(r.url, get_hash), false]
         
     | 
| 
      
 30 
     | 
    
         
            +
                       when :post
         
     | 
| 
      
 31 
     | 
    
         
            +
                         # not sure if this is a sensible default for format?
         
     | 
| 
      
 32 
     | 
    
         
            +
                         format = (r.form or :form)
         
     | 
| 
      
 33 
     | 
    
         
            +
                         resp_tuple = [r.rid, Nestful.post(r.url, :format => format,
         
     | 
| 
      
 34 
     | 
    
         
            +
                                                           :params => params), false]
         
     | 
| 
      
 35 
     | 
    
         
            +
                       else
         
     | 
| 
      
 36 
     | 
    
         
            +
                         raise "invalid verb"
         
     | 
| 
      
 37 
     | 
    
         
            +
                       end
         
     | 
| 
      
 38 
     | 
    
         
            +
                     rescue
         
     | 
| 
      
 39 
     | 
    
         
            +
                       resp_tuple = [r.rid, "#{$!}", true]
         
     | 
| 
      
 40 
     | 
    
         
            +
                     end
         
     | 
| 
      
 41 
     | 
    
         
            +
                     # insert the response
         
     | 
| 
      
 42 
     | 
    
         
            +
                     bud.async_do do
         
     | 
| 
      
 43 
     | 
    
         
            +
                       rest_response <+ [resp_tuple]
         
     | 
| 
      
 44 
     | 
    
         
            +
                     end
         
     | 
| 
      
 45 
     | 
    
         
            +
                   end] if bust_idempotent [[req.rid, req.verb, req.form, req.url, req.params,
         
     | 
| 
      
 46 
     | 
    
         
            +
                                             @budtime]]
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
            end
         
     |