Opal 0.7.0: require, kwargs, docs, testing, lots of fixes

It's been almost a year from our 0.6.0 release and has been an awesome time for the Opal community. Today I'm proud to announce we have released v0.7.0, which comes packed with lots of good stuff and uncountable bug fixes.

#require #require_relative and #require_tree

The require system has been completely overhauled in Opal 0.7. The previous version was a rather smart wrapper around sprockets directives but had some limitations, especially when it came to interleaving require calls and code. Some gems couldn't be compiled with Opal just for that reason.

The new require system now relies on a module repository where each "module" actually corresponds to a Ruby file compiled with Opal. This means that #require calls aren't no-ops anymore.

In addition to that #require_relative support has been added and for feature parity with sprockets directives we're also introducing #require_tree. The latter will be particularly useful to require templates.

Keyword Arguments

This has been a super requested feature, and thanks to Adam Beynon they're now a reality. They still have some rough edges (as they did in their first CRuby/MRI incarnation) but the core is there for you all to enjoy.

Documentation

Soooo it's Ruby baby, not Python. We write tests, not docs…

That's true, but as a project grows docs become more and more useful. Luckily most of our codebase mirrors CRuby/MRI thus making superfluous (and unpractical) to re-document every single method, but that is enough just 80% of the time. In fact I found myself asking many times if a specific method was already implemented and whether it had any deviation in semantics or any known bug.

You can now find docs for both corelib and stdlib on the Opal website.

(I'll update docs and CDN links to point to the new stable release v0.7 in the next few days)

Testing

We've added almost 600 new specs (+15%) from RubySpec to our suite, which is great, but the recent "drama" around the project lead me to give another look at tests embedded into our beloved CRuby.

A slightly adapted* version of the latest Minitest is now part of the stdlib, and a super simplified test/unit adapter has been re-implemented. Opal test suite now runs the amazing number of 1 (!) test from the original CRuby suite. As you may have imagined at this point, more will come.

* adaptation was mostly related to thread usage

Hash now calls #hash on keys, but optimizes for String

Full support for #hash has been added to Hash where previously .toString() was called internally by JavaScript. That worked surprisingly well, but there were edge cases (using the number 2 or the string "2" as key would have yielded the same value).

The only exception to the new behavior are Strings (and Symbols) that have a separate hash table (a JavaScript object) and are used as keys directly. This allows for a super-optimized common case while retaining good semantics for edge cases and also allows a to have an internal representation of the Hash that is just a JS object.

objects don't let objects be casted

#method_missing now works for bridged classes too

As you probably already know most core classes in Opal are bridged directly from JavaScript. The list includes Array, Boolean, Numeric, String, Proc, Exception, Regexp and Time. Of course having custom classes inheriting from a native constructor is super easy:

`function MyJSClass() { this.foo = "bar"; this.baz = "qux" }`

class MyClass < `MyJSClass`
  # this.foo in JS equals to @foo in Opal
  attr_reader :foo

  def method_missing name, *a, &b
    `return this[#{name}]`
  end
end

MyClass.new.foo #=> "bar"
MyClass.new.baz #=> "qux"

That's how opal-jquery is implemented. Unfortunately those classes until now were lacking #method_missing support.

How cool it is that Opal has had method_missing for all this time on normal classes? You can still hear people in awe discovering that, what a great pleasure! :)

Node.js builtin support

Node.js is a fantastic platform to have fun of and in the past I had my personal share. But the fact is that stuff like NodeWebkit (now NW.js) or AtomShell are really good if you want to build a desktop app reusing all of you web development super powers. Turns out it's also handy to run Opal specs.

Starting with Opal 0.7 stdlib features platform specific additions for Node.js that perfect support for IO, File, Dir, ENV, and much more, including Kernel#node_require. To run code on Node.js from the command line with these additions you just require 'nodejs' in your code or as a CLI option:

$ opal -rnodejs -e 'p Dir["lib/*"]' # requires npm install -g glob
["lib/mspec", "lib/opal", "lib/opal.rb"]

Improved CLI

The Command Line Interface has been further improved and replicates most options found on the CRuby/MRI CLI plus others that are specific or just useful in out nutty JavaScript land, here's what it looks like now:

$ opal -h
Usage: opal [options] -- [programfile]

    -v                               print version number, then turn on verbose mode
        --verbose                    turn on verbose mode
        --version                    Print the version
    -h, --help                       Show this message

Basic Options:

    -I, --include DIR                Append a load path (may be used more than once)
    -e, --eval SOURCE                One line of script. Several -e's allowed. Omit [programfile]
    -r, --require LIBRARY            Require the library before executing your script
    -s, --stub FILE                  Stubbed files will be compiled as empty files
    -p, --preload FILE               Preloaded files will be prepared for dynamic requires
    -g, --gem GEM_NAME               Adds the specified GEM_NAME to Opal's load path.

Running Options:

        --sexp                       Show Sexps
    -m, --map                        Show sourcemap
    -c, --compile                    Compile to JavaScript
    -R, --runner RUNNER              Choose the runner: nodejs (default), server
        --server-port PORT           Set the port for the server runner (default port: 3000)

Compilation Options:

    -M, --no-method-missing          Enable/Disable method missing
    -O, --no-opal                    Enable/Disable implicit `require "opal"`
    -A, --arity-check                Enable arity check
    -V                               Enable inline Operators
    -D, --dynamic-require LEVEL      Set level of dynamic require severity.
                                     (deafult: error, values: error, warning, ignore)
    -P, --source-map [FILE]          Enable/Disable source map
    -F, --file FILE                  Set filename for compiled code
        --irb                        Enable IRB var mode

Lots of fixes and internal stuff

I can say that working on Opal internals is much better now. Things are still improving towards an even more precise matching of Ruby semantics.

Let me take a moment to thank again and publicly Adam Beynon for creating Opal, meh for being so wonderfully stubborn in respecting Ruby semantics and for his gigantic gargantuan work on the stdlib and–finally–all the other contributors, the users and the library builders.





<3 Hooray for garbage collectors!