We have released Opal v1.3.0 a week ago and today we are releasing Opal v1.3.1 containing a few last minute fixes. We plan Opal v1.3 to be supported with a best-effort principle for a longer term, backporting bugfixes.
This is quite a big release with a special focus on developer tools and error reporting improvements.
Preliminary ES Modules support
Although we didn't yet fully integrate with Webpack as originally planned, Jan Biedermann (of the Isomorfeus project) contributed some preliminary ES Modules support.
If you use Isomorfeus you can take advantage of it with its ESBuild integration and the newly added autoload hooks, which finally works without requiring a patched version of Opal.
If you don't use it you can still roll your own integration, making autoload dynamically fetch modules from the server as they're imported.
We are planning to work on a proper import
and npm package support for the next release, along with Ruby 3.1 support.
Newly supported language features
We already support a lot of Ruby 3.0 features. This release fills a few missing gaps, most of which will only be used in very niche usecases.
Refinements
Refinements are now mostly supported. If you don't like monkey patching, this feature can improve your codebase.
binding
Binding allows you to take a peek at local variables in a given scope. You probably most likely know it for binding.irb
or binding.pry
- while we don't support those yet, it's now only a matter of time when it gets implemented - all prerequisites are there already.
autoload
Autoload statements will cause a file to be included inside your Opal bundle, but won't load it instantly. We moved a bigger deal of corelib to benefit from faster load times.
This support can also be used to load files dynamically, like opal-zeitwerk
does in Isomorfeus.
retry
Due to an asynchronous nature of JavaScript, it probably won't have much use here. But still, you can now use this statement to restart the code in a rescue clause.
super
improvements
The following code used to call a super function with (a,b,c)
arguments, now it correctly calls it with (4,b,c)
. Also a few more edge cases were corrected.
def method(a,b,c)
a = 4
super
end
stdin
and gets
support
We have now a larger synchronous IO support. gets
is implemented on a browser via prompt
(aka that annoying alert popup with a text field), on other platforms, including headless chrome, it bridges stdin
properly.
Reminder: for basic support of other platforms than browser, you need to require "opal/platform"
. For extended support of NodeJS, you need to require "nodejs"
.
$ opal -ropal/platform -e 'p gets'
123
"123\n"
Implementing gets
is all that's needed to make a runner able to be ran in the REPL mode
Global variable aliases
Certainly a lesser known feature of Ruby. Did you know it's possible to write this:
alias $PROGRAM_NAME $0
$PROGRAM_NAME = "abc" # This changes $0 as well!
Opal has you covered.
Flip-flop
This Perl-inspired feature was almost removed in Ruby 3.0. But due to some interest, it was actually fixed. Opal now supports this as well! Have you never heard of this feature? That's fair, it mostly isn't present in production code, but can be used for writing unreadable code like this:
a=b=c=(1..100).each do |num|
print num, ?\r,
("Fizz" unless (a = !a) .. (a = !a)),
("Buzz" unless (b = !b) ... !((c = !c) .. (c = !c))),
?\n
end
Toolkit improvements
opal-repl
The opal-repl
command line tool gained a number of improvements:
- Windows support
- Colored output
- Support for printing native JS Objects / null / undefined
- Pry-like
ls
support - Support for everything that opal command line tool supports
- Support for multiple runners: nodejs, chrome, gjs, quickjs, miniracer (used to be miniracer only)
$ opal-repl
>> ls 123
Comparable#methods: between? clamp
Numeric#methods: __coerced__ clone conj conjugate div dup i imag imaginary polar pretty_print pretty_print_cycle real real? rect rectangular step to_c to_json to_n
Number#methods: % & * ** + +@ - -@ / < << <= <=> == === > >= >> [] ^ __id__ abs abs2 allbits? angle anybits? arg bit_length ceil chr coerce denominator digits divmod downto eql? equal? even? fdiv finite? floor gcd gcdlcm infinite? inspect instance_of? integer? is_a? kind_of? lcm magnitude modulo nan? negative? next nobits? nonzero? numerator object_id odd? ord phase positive? pow pred quo rationalize remainder round size succ times to_f to_i to_int to_r to_s truncate upto zero? | ~
>>
$ opal-repl -Rchrome
>> require 'native'
=> true
>> $$[:navigator][:userAgent]
=> "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/95.0.4638.54 Safari/537.36"
>>
New runners
Runners are how you can use different environments to run Opal code on, using our opal
command line tool. We added support for 3 new runners:
gjs
- the GNOME JavaScript engine based on Gecko's JS enginequickjs
- an independently developed JavaScript engine with a good embedding support: https://bellard.org/quickjs/miniracer
- Ruby bindings to pure V8 (used to be supported only for repl)
$ opal -ropal/platform -Rchrome -e 'p OPAL_PLATFORM'
"headless-chrome"
$ opal -ropal/platform -Rnodejs -e 'p OPAL_PLATFORM'
"nodejs"
$ opal -ropal/platform -Rgjs -e 'p OPAL_PLATFORM'
"gjs"
$ opal -ropal/platform -Rquickjs -e 'p OPAL_PLATFORM'
"quickjs"
$ opal -ropal/platform -Rminiracer -e 'p OPAL_PLATFORM'
"opal-miniracer"
While the support for those is preliminary and works only with stdio
, there is a possibility to extend those implementations when needed (patches welcome!). GJS is now one of the primary GNOME development platforms, powering software like Flatseal, allowing you to bridge all GNOME libraries, like Glib or GTK4 (making it a fully featured Node.js alternative, though much more low level and a lot less popular - but that's a digression).
Source map debugger
We have improved the source map support in this release. It should now much more accurately tell you which line/column is being executed. Which means - easier debugging! And that's not all, if source mapping goes wrong, you can always use our new source map debugging tool:
$ opal --debug-source-map -e '[1,2,3,4].each { |i| p i }'
https://sokra.github.io/source-map-visualization/#base64,T3BhbC5xdWV1ZShmdW5jdGlvbihPcGFsKSB7LyogR2VuZXJhdGVkIGJ5IE9wYWwgMS4zLjAgKi8KICB2YXIgJCQxLCBzZWxmID0gT3BhbC50b3AsICRuZXN0aW5nID0gW10sIG5pbCA9IE9wYWwubmlsLCAkJCQgPSBPcGFsLiQkJCwgJCQgPSBPcGFsLiQkLCAkc2VuZCA9IE9wYWwuc2VuZDsKCiAgT3BhbC5hZGRfc3R1YnMoWyckZWFjaCcsICckcCddKTsKICByZXR1cm4gJHNlbmQoWzEsIDIsIDMsIDRdLCAnZWFjaCcsIFtdLCAoJCQxID0gZnVuY3Rpb24oaSl7dmFyIHNlbGYgPSAkJDEuJCRzID09IG51bGwgPyB0aGlzIDogJCQxLiQkczsKCiAgICAKICAgIAogICAgaWYgKGkgPT0gbnVsbCkgewogICAgICBpID0gbmlsOwogICAgfTsKICAgIHJldHVybiBzZWxmLiRwKGkpO30sICQkMS4kJHMgPSBzZWxmLCAkJDEuJCRhcml0eSA9IDEsICQkMSkpCn0pOwo=,eyJ2ZXJzaW9uIjozLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyItZSJdLCJzb3VyY2VzQ29udGVudCI6WyJbMSwyLDMsNF0uZWFjaCB7IHxpfCBwIGkgfSJdLCJuYW1lcyI6WyI8bWFpbj4iLCJlYWNoIiwiMSIsIjIiLCIzIiwiNCIsImJsb2NrIGluIDxtYWluPiIsImkiLCJibG9jayAoMiBsZXZlbHMpIGluIDxtYWluPiIsInNlbGYiLCJwIl0sIm1hcHBpbmdzIjoiQUFBQUEsMkJBQUFBO0VBQUFBOztFQUFBQTtFQUFBQSxPQUFTQyxNQUFULENBQUNDLENBQUQsRUFBR0MsQ0FBSCxFQUFLQyxDQUFMLEVBQU9DLENBQVAsQ0FBU0osUUFBQUEsRUFBQUEsRUFBQUEsRUFBTUssZ0JBQUdDLENBQUhELEVBQUFFOzs7O0lBQUc7SUFBQTtJQUFBO0lBQUdBLE9BQUFDLElBQUFDLEdBQUFBLENBQUVILENBQUZHLEVBQU5KLGtCQUFBQSxpQkFBQUEsS0FBTkw7QUFBVEQ7In0=,WzEsMiwzLDRdLmVhY2ggeyB8aXwgcCBpIH0=
This utility can be also used to help you understand which Ruby code corresponds to which JavaScript output, for educational purposes.
Cache for Opal::Builder
Sprockets doesn't use Opal::Builder
- it has its own cache, though it may need to be configured.
Upgrade to Opal 1.3 and forget the long compilation times when using opal
command line tool or Opal::Builder
thru any other means (like Opal::SimpleServer
). This is implemented by caching the marshaled Compiler objects.
4x improvement for a simple script:
$ time opal _1.2.0_ -e 'p 123'
123
real 0m2.321s
user 0m2.213s
sys 0m0.107s
$ time opal _1.3.0_ -e 'p 123'
123
real 0m0.537s
user 0m0.450s
sys 0m0.090s
8x when including more core, e.g. opal-parser
:
$ time opal _1.2.0_ -ropal-parser -e 'p eval("(0..1)")'
Object freezing is not supported by Opal
0..1
real 0m12.555s
user 0m12.250s
sys 0m0.349s
$ time opal _1.3.0_ -ropal-parser -e 'p eval("(0..1)")'
Object freezing is not supported by Opal
0..1
real 0m1.512s
user 0m1.405s
sys 0m0.249s
Async/await support
This big feature is experimental, and may totally change in future releases of Opal.
This complements the PromiseV2
feature introduced in Opal v1.2 and uses magic comments and a supported library (aptly named async
) to await on JavaScript native promises.
Check it out:
# await: sleep
require "await"
puts "Let's wait 2 seconds..."
sleep 2
puts "Done!"
Please consult documentation of this feature: https://opalrb.com/docs/guides/v1.3.0/async
We are leaving this as experimental, because, as an Opal specific feature. Although it's not supported by Ruby itself the syntax is fully compatible, maybe some unexpected breakthrough will happen, with a port to a gem or to the language itself.
Conclusion
Are you starting to make a new web application and don't want to be forced to write JavaScript on the frontend?
If you are adventurous, why not try Opal? You may be surprised how ready it is for your usecase! :D