Hacking with ruby2ruby

Post on 12-Nov-2014

1,650 views 5 download

description

ruby2ruby provides a means of generating pure Ruby code easily from ParseTree‘s Sexps. This makes making dynamic language processors much easier in Ruby than ever before. I'll cover my road to discovering Ruby2Ruby via JavaScript, and plenty of demos/examples including a way to write distributed applications.Watch a video at http://www.bestechvideos.com/2008/11/30/rubyconf-2008-hacking-with-ruby2ruby

transcript

Hacking with ruby2rubyMarc Chung

OpenRainblog.marcchung.comblog.openrain.com@heisenthought

Hacking with ruby2rubyMarc Chung

OpenRainblog.marcchung.comblog.openrain.com@heisenthought

ruby2ruby

Ruby2RubyWalks the Ruby AST

To emit Ruby

sudo gem install ruby2ruby

Limitations

• Only Ruby 1.8

• Not 1.9, yet

• Built into Rubinius

function rhino_rocks() { return “Rhino, rocks!”;}

js> rhino_rocks();

Rhino, rocks!

js> rhino_rocks.toString();

function rhino_rocks() { return “Rhino, rocks!”;}

js> eval(rhino_rocks.toString()).call();

Rhino, rocks!

ruby_rocks = proc { return “Ruby, rocks”;}

irb> ruby_rocks.call;

Ruby, rocks!

irb> require “ruby2ruby”irb> require “parse_tree_extensions”irb> ruby_rocks.to_ruby

proc { return \“Ruby, rocks\” }

irb> eval(ruby_rocks.to_ruby).call

Ruby, rocks!

define_methodclass Voice define_method(:shout) do |word| word.upcase! endend

puts Ruby2Ruby.translate(Voice)

class Voice < Object def yell(word) word.upcase! endend

aliasclass AliasClass def old_busted "42" end

alias_method :new_hotness, :old_bustedend

puts Ruby2Ruby.translate(AliasClass)

class AliasClass < Object def new_hotness "42" end def old_busted "42" endend

#module_functionmodule HelperModule def help puts "Help me" end module_function : helpend

puts Ruby2Ruby.translate(HelperModule)

module HelperModule def help puts("Help me") end def self. help puts("Help me") endend

ActiveRecord Models

class Doctor < ActiveRecord::Base has_many :patientsend

See, Ruby, Run!

• Drmap

• git clone git://github.com/mchung/drmap.git

• Optionally, install Journeta

Under the hood

• Abstract syntax trees

• S-expressions

• ParseTree

class Book def title “Ruby programming” endend

case NODE_BREAK:case NODE_NEXT: if (node->nd_stts) add_to_parse_tree(self, current, node->nd_stts, locals);

break;

case NODE_YIELD: if (node->nd_stts) add_to_parse_tree(self, current, node->nd_stts, locals);

if (node->nd_stts && (nd_type(node->nd_stts) == NODE_ARRAY || nd_type(node->nd_stts) == NODE_ZARRAY) && !node->nd_state) rb_ary_push(current, Qtrue);

break;

[:class, :Book, [:const, :Object], [:defn, :title, [:scope, [:block, [:args], [:str, "Ruby programming"]]]]]

class Book < Object def title “Ruby programming” endend

/opt/local/usr/bin

• parse_tree_show

• r2r_show

class Book def title “Ruby programming” endend

$ parse_tree_show book.rb

s(:class, :Book, nil, s(:scope, s(:defn, :title, s(:args), s(:scope, s(:block, s(:str, "Ruby programming"))))))

$ r2r_show book.rb

class Book def title() “Ruby programming” endend

Operator Precedence with Guy Decoux: a b c, d

[[:fcall, :a, [:array, [:fcall, :b, [:array, [:vcall, :c], [:vcall, :d]]]]]]

ParseTree.new.parse_tree_for_string(“a b c, d”)

ruby2java

class JavaClass def self.main puts “Java, rocks!” endend

[:class, :JavaClass, [:const, :Object], [:defs, [:self], :main, [:scope, [:block, [:args], [:fcall, :puts, [:array, [:str, "Hello, Java"]]]]]]]

class Ruby2Java < SexpProcessor def process_class(exp) “public class #{exp.shift} #{next_token(exp, true)}” end

def process_const(exp) “extends #{exp.shift}” end....end

public class JavaClass { public static void main(String argv[]) { return “Hello, Java”; }}

Distributing Ruby

• drmap - multi-machine worker queue

• gisting - map/reduce framework

drmapirb> fn = proc {|x| x**10}

irb> Marshal.dump(fn)TypeError: no marshal_dump is defined for class Proc

irb> YAML::dump(fn)=> "--- !ruby/object:Proc {}\n\n"

irb> YAML::dump(fn.to_ruby)=> "--- proc { |x| (x ** 10) }\n"

irb> Marshal.dump(fn.to_ruby)=> "\004\b\"\eproc { |x| (x ** 10) }"

Enumerable#drmap

module Enumerable

def drmap(&block) pool = Drmap::BeanstalkPool.new

jid = rand(100) each_with_index do |element, idx| pool.put_job(jid, block.to_ruby, element) end

results = [] while results.size < length results << pool.next_result(jid) end results end

end

def process loop do begin job_payload = next_job fn = job_payload[:proc] data = job_payload[:data] result = eval(fn).call(data) save(job_payload[:jid], result) success! rescue => e fail!(e) end endend

drmap

• Trivial

• Distributed

• Powerful

Demo time!

Gisting

• MapReduce in Ruby

• http://github.com/mchung/gisting

map / reduce

• Functional programming

• Iterating over collections

• (5..10).map {|x| x+1} #=> [6, 7, 8, 9, 10, 11]

• (5..10).reduce(:+) #=> 45

Google MapReduce

• Modeled after map() and reduce()

• Programming model

• Programs are trivially parallelizable

Demo time!

Gisting::Spec

input.file_pattern = "file.txt" input.map do |map_input| words = map_input.strip.split("\t") Emit(words[1], "1") end

output.reduce do |reduce_input| count = 0 reduce_input.each do |value| count += value.to_i end Emit(count)end

inputs = args spec = Gisting::Spec.new inputs.each do |file_input| input = spec.add_input input.file_pattern = file_input input.map do |map_input| # 2722 mailbox 2006-05-23 00:08:39 words = map_input.strip.split("\t") Emit(words[1], "1") end end output = spec.output output.filebase = "/Users/mchung/Public/datasets/output" output.num_tasks = 2 output.reduce do |reduce_input| count = 0 reduce_input.each do |value| count += value.to_i end Emit(count) end

result = MapReduce(spec) pp result

Gisting

• MapReduce programming model in Ruby

• Uses ruby2ruby to serialize procs

• Uses EventMachine for Map/ReduceServer

• -: 94978

• mailbox: 20872

• google: 6758

• ebay: 2832

• yahoo: 2674

• yahoo.com: 2198

• myspace.com: 1916

• google.com: 1882

self.give(:thanks)OpenRain

openrain.com

My Git Repogithub.com/mchung

Marc Chungblog.marcchung.com

Ruby::AZrubyaz.org

Resources

Code Generation: The Safety Scissors Of Metaprogrammingmwrc2008.confreaks.com/03bowkett.html

Forbidden Fruit: A Taste of Ruby's Parse Treegoruco2008.confreaks.com/03_wanstrath.html

Ruby2Rubyseattlerb.rubyforge.org/ruby2ruby/

ParseTreeparsetree.rubyforge.org