Sup bros. I need to post in this thing more often. Yesterday, someone tipped over my scooter again. I'm getting kind of tired of that.
Anyway, its time for me to write about this. RKelly is pretty much dead. For the past few months, John and I have been working on RKelly's replacement called Johnson. Basically we're now putting a ruby wrapper around Mozilla's Spidermonkey. The project is coming along quite nicely. Ruby objects can be passed in to javascript land, and javascript objects can be passed back in to ruby land.
For example, we can define an alert function in our javascript context:
ctx = Johnson::Context.new
ctx['alert'] = lambda { |x| puts x }
ctx.evaluate('alert("Hello world!");')
Johnson::Context#evaluate will also return the last statement evaluated. We can evaluate an expression, and manipulate that expression in ruby land. For example, I'll create an object in javascript, return it to ruby land, then access a property of the javascript object:
ctx = Johnson::Context.new
obj = ctx.evaluate('var foo = { x: "hello world" }; foo')
puts obj.x # => 'hello world'
We can even do the reverse by stuffing ruby objects in to the context:
ctx = Johnson::Context.new
ctx['alert'] = lambda { |x| puts x }
ctx['a'] = A.new("bar")
ctx.evaluate('alert(a.foo);') # => 'bar'
But it gets better. We added a top level variable called "Ruby" that lets you access constants and globals from Ruby land. We can rewrite the previous example completely in javascript:
ctx.evaluate("var x = new (new Ruby.Struct(Johnson.symbolize('foo')));")
ctx.evaluate("x.foo = 'bar'")
puts ctx.evaluate('x').foo # => 'bar'
puts ctx.evaluate('x').class # => #<Class:0x49714>
Since the 'Ruby' constant delegates to Object, you can access any constant. Including ones you've defined yourself. We could, for example, look up a bunch of User records through rails:
ctx['alert'] = lambda { |x| puts x }
ctx.evaluate(<<-END
for(var user in Ruby.User.find(Johnson.symbolize('all'))) {
alert(user.first_name());
}
END
)
You might be wondering what this Johnson.symbolize business is about. Since Javascript doesn't have a concept of a symbol, we've created a helper to "mark" a string as a symbol and pass it back in to ruby land.
To conclude this update about my Johnson, I'd like to show off an interactive shell for Johnson (thanks to Brohuda Katz). Johnson has an interactive shell that lets you try things out in javascript land or ruby land, and let you quickly switch between the two. Typing 'js' will put in you the javascript shell, 'rb' will switch you to the ruby shell. In the ruby shell, you can use the 'cx' variable to get ahold of you javascript context:
js> var x = { foo: 'bar', hello: function() { return 'world' } };
=> nil
js> rb
rb> cx['x'].foo
=> "bar"
rb> cx['x'].hello()
=> "world"
rb>
We aren't quite ready for a release yet, but if you'd like to play around with Johnson, you can pull it down from github here. Just run 'rake', and you should have it compiled and running!
My next Johnson related post will be about Javascript parse trees and Javascript code generation.
Great stuff! I am considering to use it for RubyJS (Ruby to Javascript compiler and runtime), mainly for testing.
…this is really sweet… can’t wait to mess with this, and hear more about it.
Thanks guys! I’m having a great time working on the project. I think that soon it will be good enough to start integrating in to Ruby Mechanize.
wait - where’s the parser tree? LIAR.
Surprise! Recognition! Awe! Acknowledgement! Appreciation! Obnoxiousness!
[...] Источник: Блог Аарона Паттерсона [...]
[...] Johnson is a ruby gem that executes JavaScript code. (It’s a successor to RKelly, which did the same thing.) I don’t know why I think this is so cool. Most people agreed the main use case for something like this is testing, but it seems to me there might be neater tricks to play. We’ll see how I feel after playing with it for a while. [...]
I love my Stella, too!
Found your website via the mechanize docs and I’ll be adding you to my ruby feeds, so keep the posts coming!
Does this work?
ctx = Johnson::Context.new
ctx << “test”
and << would be an alias to evaluate?
Right now typing .evaluate seems to be a bit cumbersome
Is it possible to make the ‘Ruby’ global optional.
I am interested in using Johnson to run user defined javascript - so Obviously I have concerns about security.
weepy ;…(
Yes, it is quite possible. We have it on by default, but it would only take minor changes to implement.
Should be mentioned that a day after posting this, Runtime became the new Context
http://github.com/jbarnette/johnson/commit/ae3584f2d4e47d82c063c89631bcd8b23acfd9db
Okie sorry, it was a couple days later….