Tomato is a project mainly written in C++ and JAVASCRIPT, based on the MIT license.
Leverages Google's V8 JavaScript library to interface Ruby code with JavaScript code.
= tomato
Leverages Google's V8 JavaScript library to interface Ruby code with JavaScript code.
== IMPORTANT
I've left the version number in the 0.0.x range because it's WAAAY too early for a proper release. This is some pretty cool code IMO, but keep in mind if you want to check it out that it's still development code (not even pre-Alpha). I do hope to release it as a real gem after it's reached a sufficient level of functionality.
== Examples
=== Instantiation
require 'tomato' tomato = Tomato.new
=== Running JavaScript code
When JS code is executed, it'll do its thing internally and then return the result:
tomato.run("(1+1);") # => 2
==== Bindings
You can bind Ruby methods to JavaScript, as well. You can bind an instance method of Tomato like so:
tomato.bind_method(:inspect)
tomato.run("inspect();") #=> "#
Or, perhaps more usefully, you can bind an instance method of some other object:
tomato.bind_method(:inspect, 5) tomato.run("inspect();") #=> "5"
It's also easy to bind methods to arbitrary JavaScript objects. If a JS object in the chain doesn't exist, Tomato will silently generate it for you on-the-fly:
def say(something) puts something end
tomato.bind_method(:say, self, :to => "person.mouth")
tomato.run "person.mouth.say('Hello there');"
#
puts tomato.run("this")
#
In the spirit of true Ruby dynamicism, (is that a word?), you can feel free to re-bind new methods over old ones:
tomato.bind_method(:say) tomato.run("say('something');")
tomato.bind_method(:say, self) def say(something); puts something; end tomato.run("say('something');")
You can also easily bind an entire object to JavaScript:
tomato.bind_object(Time.now) tomato.run("ruby.time.to_s();")
tomato.bind_object(Time.now, "time.current") tomato.run("time.current.to_s();")
Even better, you can also bind a whole class and instantiate it from within JavaScript. If you return the object to Ruby, it'll be seamlessly converted into the corresponding Ruby object.
class Person def initialize(name) @name = name end attr_accessor :name, :age end
tomato.bind_object(Person, "world.Person") tomato.run <<-end_js var colin = new world.Person("Colin"); colin.age = 25; colin; end_js
==== Error Handling
When JS code encounters an error of some kind, it'll get raised in Ruby:
tomato.run("throw 'error';")
#
You can also catch Ruby errors in JavaScript:
tomato.bind_method(:raise_err, self) def raise_err raise ArgumentError, "Not what I meant!" end tomato.run("try { raise_err(); } catch(e) {}");
#
Or let them bubble up to Ruby:
tomato.bind_method(:raise_err, self) def raise_err raise ArgumentError, "Not what I meant!" end begin tomato.run("raise_err();"); rescue ArgumentError => err puts "Error: #{err.message}" end
=== Object Types
Some objects in Ruby don't exist in JS. So far the ones I've implemented are Hashes and Symbols.
==== Symbols
A Symbol is converted to an object in JS with a "symbol" attribute and a "toString()" method. That same object, if returned to Ruby, is converted back to a Symbol. Examples:
def fetch_a_symbol :a_symbol end
t = Tomato.new t.bind_method(:fetch_a_symbol, self) t.run("fetch_a_symbol()") #=> :a_symbol t.run("fetch_a_symbol().symbol") #=> "a_symbol" t.run("fetch_a_symbol().toString()") #=> "a_symbol"
==== Hashes
A Hash is converted to an object of some form or another. It's not an Array. I haven't added much to the JS side of this object yet, so I don't know if it's even usable in JS. But it does, at least, return to Ruby as a Hash. So you can have a method that returns a Hash, call the method from JS, return that object to Ruby, and get the hash. Dunno if that's useful, but it's an implementation detail that had to be looked at regardless. Hashes still have a lot of development ahead of them.
== Requirements
Note: I've not tested it on Windows, but if you can satisfy the above requirements and have a C++ compiler (gcc recommended), it should work. Make sure Python is in your path.
== Installation
To install the latest and greatest prerelease: gem install tomato --pre
To install the current stable release: gem install tomato
That should be all there is to it. The package is pretty large as gems go, so be patient.
== Performance
ruby-1.9.1-p378 > javascript = <<-end_js ruby-1.9.1-p378"> var str = ""; ruby-1.9.1-p378"> for (var i = 0; i < 1000000; i++) ruby-1.9.1-p378"> str = 'a'; ruby-1.9.1-p378"> end_js ruby-1.9.1-p378 > require 'benchmark' ruby-1.9.1-p378 > require 'tomato' ruby-1.9.1-p378 > t = Tomato.new ruby-1.9.1-p378 > ruby-1.9.1-p378 > # Setup complete, let's start the test ruby-1.9.1-p378 > puts Benchmark.measure { for i in 1..1000000; str = 'a'; end } ruby-1.9.1-p378 > 0.350000 0.000000 0.350000 ( 0.358106) ruby-1.9.1-p378 > ruby-1.9.1-p378 > puts Benchmark.measure { t.run(javascript) } ruby-1.9.1-p378 > 0.010000 0.000000 0.010000 ( 0.012603)
'Nuff said.
Since V8's JavaScript code is compiled into byte code, this can have a positive impact on performance in some applications. Just translate your code to JavaScript and let Tomato compile it into byte code.
== Note on Patches/Pull Requests
== Copyright
Copyright (c) 2010 Colin MacKenzie IV. See LICENSE for details.