00:00:20.000
My name is Charles Lowell, and I'm here to talk about the Ruby Racer. I don't know how many of you have actually used the Ruby Racer, so can I get a quick show of hands?
00:00:31.920
Okay, not too many. That's fine. So we're starting from scratch, which is good. I'm going to talk a little bit about what it is and why you might want to use it.
00:00:44.160
Then, we’re actually going to go through some pretty deep code samples so you can see the things that are possible with it. Because of this, I think it's probably better if you ask questions as we go along.
00:00:56.239
I know we’re supposed to do Q&A at the end, but it’s better to understand everything up front since the examples build on each other. So don’t be shy; just stop me, and we can all go over it together.
00:01:08.640
The Ruby Racer allows you to embed the Google V8 JavaScript engine right into your Ruby process. It does this through a C extension, giving you all the power of that JavaScript engine at your disposal.
00:01:19.119
But why might you want to do this? One obvious reason is that the new hotness is Rails 3.1, which has a big controversy because it comes with CoffeeScript enabled by default. CoffeeScript is implemented in JavaScript and uses a gem called exec.js to compile your CoffeeScript into JavaScript.
00:01:36.799
If you have it installed, the default runtime actually is the Ruby Racer, so you may be using it without even knowing, just by accident, with several layers in between. But that's only the tip of the iceberg; there are actually many other uses for it.
00:02:06.399
Before we dive into the code, let’s go over a few of the other applications. The first one is unit testing JavaScript. This was actually the very first use case for the Ruby Racer. It's a great entry point because with the Ruby Racer, you can spin up a JavaScript environment, load whatever you want into it, and it doesn't require a display or a browser.
00:02:31.440
This is what we use on our CI server. We actually use JS DOM and Node HTML parser to simulate a browser environment so we can run all of our JavaScript tests on our CI server without worrying about displays or anything like that, and they run really fast.
00:03:06.080
Right now, there’s an absolute explosion—a Precambrian explosion—of diversity in the JavaScript environment. A lot of this has to do with Node, but not exclusively. There are many utilities that you can access that you can't if you're only working with a regular Ruby environment.
00:03:30.720
This list is by no means complete but we use several JavaScript utilities every day in our development and production. I compare it to using JRuby. With JRuby, you have access to all these goodies implemented in other languages like Java, Clojure, and Scala.
00:03:41.200
You get access to all these great JavaScript libraries that aren't necessarily dependent on the DOM; and if they are dependent on the DOM, you can implement DOMs in JavaScript. You can also share code between your client and server. I realize this could be considered the holy grail for many, and while there may not be a definitive right way to do it, we share our views implemented in Mustache and Handlebars between our client and server.
00:04:20.959
There are people who have contacted me on the list about sharing their validation code and some interesting implementations where they build large parts of their business logic in JavaScript, allowing them to include it via Ruby modules into their Ruby classes.
00:05:12.639
Finally, let's talk about performance. We've only begun to explore this area, but some others have been doing this more extensively. If you have a tight loop or something that you need to optimize, you can do it in JavaScript, which is a lot easier than doing it in C. It's just simpler and faster. This isn't an exhaustive list of why you would want to use it, but people keep coming up with new uses all the time.
00:05:56.640
Now let’s go straight into how it works.
00:06:01.199
First, we need to ask: what does it mean to have a JavaScript runtime embedded in your Ruby process? This is conceptually a bit of a hill for some people to get over, but I think it means mainly three things. The first is that you can evaluate JavaScript code.
00:06:35.840
Here, I'm creating a new V8 context. This is a completely hermetically sealed JavaScript world with just the JavaScript syntax—objects, functions, arrays, and regular expressions—along with the standard library. It's absolutely tiny, with no access to Ruby, and no DOM. This spartan nature is one of the things I love about it. Whenever you create one of these new contexts, you create a new world. If programmers are gods, this is your world, and you can literally put anything you want into it.
00:07:24.000
Once I've created the context, I can evaluate JavaScript code. The strings there have actual JavaScript syntax and I'm evaluating objects. You can also directly reference JavaScript objects, which masquerade as Ruby references. For example, we'll evaluate an object literal and assign it to the variable 'horse'. We can pull it out of the context and from Ruby code, we can invoke that JavaScript method and get the return value back into Ruby.
00:08:18.399
There's a great deal of synergy between JavaScript objects and Ruby objects. JavaScript objects behave like property bags and look similar to Ruby hashes, so when you get a reference, you can use hash syntax. You can access properties by name, and it doesn’t matter if you use a symbol or a string; they are treated the same.
00:09:15.679
If your property name is a valid Ruby identifier, you can send it as a method and it will work correctly because JavaScript methods are essentially properties. Remember, JavaScript has only two classes—object and function.
00:09:39.600
You can assign and evaluate those properties as you normally would. For instance, if you have a reference to a JavaScript object, you can evaluate it as an object literal and set a property via Ruby; that value becomes accessible through JavaScript.
00:10:07.520
JavaScript objects are enumerable just like hashes, so you can iterate through all their keys and values. You almost never need to use eval; that's something newcomers often think they have to do, but you can do almost everything directly through Ruby, which is actually faster.
00:10:44.399
When it comes to accessing methods in JavaScript from Ruby, things can behave unexpectedly sometimes. JavaScript functions differ from Ruby functions, as JavaScript methods are actually properties that are functions. There are three main ways to invoke a JavaScript function.
00:11:30.639
You can invoke it as a bare function, you can pass a context to it—setting what the 'this' variable will be—or you can invoke a function as a constructor. The example I'm providing shows calling it as a proc: we’ll evaluate it and get a reference to our JavaScript function called 'greet'. Then we can invoke it with the 'call' method, which looks like any other Ruby callable. Notice that 'this.title' is not defined, meaning it doesn’t print out.
00:12:34.560
However, there’s a method called 'methodCall' that accepts arguments. The first argument sets what 'this' will refer to, followed by the natural arguments. By changing the context, we make it print something entirely different. You can also invoke any JavaScript function as a constructor by using a new method.
00:13:40.159
In this case, we define a square constructor in JavaScript, creating a new square with a radius. It’s interesting to realize that if I were to take this away, you'd not even know that it wasn't a Ruby class; it’s actually pure JavaScript being used.
00:14:28.479
It’s important to remember that JavaScript methods happen to just be functions. Another fascinating consequence is that you can take a function property off of one object and invoke it on another. For example, I've demonstrated pulling the area function off of the first square and invoking it on a second square.
00:15:04.799
To summarize, one significant aspect of this interaction is that you can evaluate JavaScript code and call it from Ruby. Later, we’ll see how you can also call Ruby from JavaScript. This half of the bridge involves evaluating JavaScript directly, getting in there to manipulate properties of a JavaScript object, and invoking JavaScript functions from Ruby.
00:15:52.639
But, interestingly enough, we need to talk about how to have your JavaScript code call back into your Ruby code. Now, let’s explore that.
00:16:23.399
Here’s a simple class called 'Dog' with one method, 'bark'. We’ll take an instance of that Ruby object and put it in our V8 context.
00:16:44.079
When I say 'rover.bark', it behaves just like a normal JavaScript object, invoking the Ruby class or method directly. I don't need to embed the instance; I can pull off the 'bark' method from the 'Dog' object and place it inside the context, so it can be evaluated from JavaScript.
00:17:11.440
Lambdas require a special note since they avoid an interesting pitfall present in procs. Returns inside procs can result in unexpected behavior, breaking the V8 stack. It's better to use lambdas, as they do not have the same side effects.
00:18:04.640
You can embed a Ruby class into your context, which creates a corresponding JavaScript constructor. This allows instantiation of the Ruby class from JavaScript, returning a reference that allows you to manipulate the instance.
00:18:54.080
A noteworthy side effect is the ability to extend Ruby classes from JavaScript. For instance, if I've embedded the Dog class, I can access its prototype and find its bark and wag functions, allowing a mix of Ruby and JavaScript implementations.
00:19:27.680
We’ve covered how to get references and invoke Ruby functions. You can also access properties similarly. For example, I could define a struct and read its properties using JavaScript.
00:20:01.280
You can see that my properties like 'my name' and 'my city' are accessible from the JavaScript runtime, and you can also set values directly from JavaScript. If the property doesn’t exist or you want to access it dynamically, JavaScript can still retrieve it.
00:20:34.760
The results are indeed seamless. For example, if you have a 'Processes' class that finds and returns processes associated with a username, this is accessible within JavaScript without JavaScript comprehending the background logic.
00:21:01.440
To summarize, we’ve discussed how to access JavaScript from Ruby and vice-versa. This ability enables vast functionality where JavaScript can leverage Ruby capabilities and Ruby can utilize JavaScript logic.
00:21:37.760
Now, let’s discuss some future plans for the project. I think it's crucial to develop a 'safe eval' capability, especially since right now, many individuals might be unaware of the risks involved in executing untrusted JavaScript.
00:22:07.120
It could potentially pose serious risks to their machines, similar to the warnings game from developers in the '90s advising against the use of 'eval' due to its dangers.
00:22:50.080
While this feature exists in the client-side view of JavaScript, we need to work on making it applicable to the server-side. The Ruby Rhino gem uses these concepts effectively, allowing untrusted code to run smoothly.
00:23:20.160
Next, we need to ensure detection of undesirable constructs like malicious while loops and to easily terminate execution if they appear. We also want to manage object allocation effectively to avoid overwhelming the system.
00:23:58.120
Multithreading has posed its challenges. Since the release candidate for Rails 3.1, many issues have been reported, and there’s been considerable hardening on this front. It's crucial to run the Ruby Racer in a multithreaded environment without concern.
00:24:17.760
Another plan is to enhance custom heap snapshots in V8, allowing for better serialization of object structures when starting the interpreter. This speeds up initialization and provides high efficiency.
00:24:42.960
Garbage collection has been a significant challenge. Working with two garbage collectors that don't know about each other and managing memory leaks poses complicated issues. Effectively aligning the various garbage collection approaches of different Ruby implementations has been demanding.
00:25:43.760
My main goal is to provide a seamless interface, helping you forget about these complexities. You should worry about connecting your JavaScript and Ruby worlds without being bogged down by garbage collection.
00:26:11.519
Lastly, I aim to solidify the Ruby Racer to ensure it runs quickly, cross-platform, and is simple to install. The motivation comes from the empowerment it has provided me, helping people to think creatively without getting stuck on technicalities.
00:26:52.280
Throughout this talk, I've met numerous individuals with innovative uses for the Ruby Racer that I couldn't have foreseen. With the ability to execute JavaScript directly in Ruby, this area continues to expand in possibilities, I believe in five years, this will be commonplace.
00:27:53.800
To that end, if anyone wants to help out, please contact me. Currently, it’s mostly just me working on this. While I have grand plans, the pace can be slower than desired, since I work on this project every Thursday at 8 PM Central. You are free to join me.
00:28:34.720
Thank you!
00:29:00.879
Are there any questions? Was garbage collection a challenge?
00:29:12.000
Are your slides available anywhere? I want to reference the code samples.
00:29:51.120
Yes, the slides are available on SlideShare. I’ll update them with the most recent versions, which will include better and simpler samples compared to those that are currently on the Internet.
00:30:13.440
One of your points was about not having to use C because this is just as fast. What measures have you done about its speed compared to native C?
00:30:23.920
I haven’t personally measured that. However, people have posted some of their metrics showing speed improvements. The Ruby Racer relies on V8, which compiles your JavaScript down to native code.
00:30:52.360
We utilize it daily for JavaScript testing. It’s especially useful with Less, as we have a significant investment in it, and when it shifted focus to Node, we adapted via the Ruby Racer to compile Less using JavaScript.
00:31:43.600
There’s also a gem called 'include_js' which allows for easier integration between Ruby and JavaScript, useful for sharing business logic between frameworks. Many use cases arise as more people explore the Ruby Racer.
00:32:06.120
Are there any other questions? No? Alright! Thank you!