Bridgetown2024-01-05T15:16:00-05:00https://www.ruby2js.com/feed.xmlRuby2JSRuby2JS is an extensible Ruby to modern JavaScript transpiler you can use in production today.Ruby2JS 5.1, esbuild, and a Peek at the Future2023-02-20T00:00:00-05:002023-02-20T00:00:00-05:00repo://posts.collection/_posts/2023-02-20-future-of-ruby2js.md<p>At long last, <strong>Ruby2JS 5.1 is here!</strong> It comes packed with several very welcome features:</p>
<ul>
<li>A brand-new Ruby-based configuration DSL</li>
<li>A “preset” option for sane defaults</li>
<li>Magic comment support for sharing portable Ruby2JS code (based on the preset)</li>
</ul>
<p>In addition, the <a href="https://github.com/ruby2js/ruby2js/tree/master/packages/esbuild-plugin">esbuild plugin</a> has reached 1.0 status and has been re-architected to use the Ruby version of the compiler rather than the Node (aka Opal) version. Why? Performance and modern language compatibility.</p>
<p>Let’s dig in a bit on all these new features! And keep reading as well for an update on the status of Ruby2JS and its open source governance.</p>
<h2 id="ruby-based-configuration">Ruby-based Configuration</h2>
<p>Ruby2JS, despite being a language transpiler usually needing some degree of configuration, never had a canonical format for project-based configuration. Until today!</p>
<p>You can add <code class="highlighter-rouge">config/ruby2js.rb</code> to your project and both the Ruby API and the CLI tool will automatically detect its presence and use it for configuration. In addition, you can specify the location of a configuration file manually if you prefer to use a different filename or folder.</p>
<p>The configuration format is very simple and easy to work with, just like other configuration formats such as Puma. You can read all about it in <a href="/docs/options">the documentation here</a>. But that’s not all!</p>
<h2 id="a-preset-configuration-for-sane-defaults">A Preset Configuration for Sane Defaults</h2>
<p>We believe most Ruby2JS code would benefit greatly from transpiling to a modern ECMAScript version (namely ES2021), using a few common filters such as <a href="/docs/filters/functions">Functions</a>, <a href="/docs/filters/esm">ESM</a>, and <a href="/docs/filters/return">Return</a>, using identity-based comparison operators (aka <code class="highlighter-rouge">==</code> becomes <code class="highlighter-rouge">===</code>), and automatically underscoring instance variables (<code class="highlighter-rouge">@x</code> becomes <code class="highlighter-rouge">this._x</code>).</p>
<p>So that’s exactly what we built. By simply adding <code class="highlighter-rouge">preset</code> to a configuration file or passing <code class="highlighter-rouge">preset: true</code> or <code class="highlighter-rouge">--preset</code> to the Ruby API or CLI respectively, you can gain all the benefits of these common options. In addition, by writing your code to use the preset, you can ensure wider compatibility between projects and between tutorials/code samples and production workflows.</p>
<p>Even more to that point, we’ve introduced the idea of a “magic comment”. By adding <code class="highlighter-rouge"># ruby2js: preset</code> to the top of a <code class="highlighter-rouge">.js.rb</code> file, you instruct Ruby2JS to use the preset configuration for that file. You can even add additional filters right in the magic comment, change the ES level, or disable a filter that comes with the preset. <a href="/docs/options">Read the documentation here.</a></p>
<p>We believe all of these features now mean that Ruby2JS code is easier to teach and easier to share. We took your feedback about these issues in the past to heart and are trying to make improvements for better DX.</p>
<h2 id="esbuild-is-faster-using-ruby">esbuild is faster using Ruby?!</h2>
<p><a href="https://esbuild.github.io">esbuild</a> is a modern, fast, and easily-configured frontend bundling tool, and we want to support it as a “first-party” citizen in the Ruby2JS ecosystem.</p>
<p>esbuild is fast because its core code is written in Go, not JavaScript. Along similar lines, we discovered something extraordinary when testing the beta of the esbuild bundling package. When we tried spawning a process to transpile a file using the Ruby version of Ruby2JS, rather than the Opal/Node-powered JavaScript version, we discovered that it was actually faster! And not just a little bit faster…<a href="https://github.com/ruby2js/ruby2js/discussions/170">almost 2x faster!</a></p>
<p>Transpiling using the Ruby version also has the added benefit that the syntax of the code you write on the frontend matches the version of Ruby your project uses overall. Before, you could be using Ruby 3.2 in your overall stack but the “version of Ruby” (in fact the version of Opal) might be older. In fact, there’s actually an outstanding issue that the version of Opal used to generate the JavaScript version of Ruby2JS is locked to an older version of Opal due to bugs introduced when upgrading. More on that below…</p>
<p>So, all in all, it makes sense to standardize around Ruby, even when using esbuild. After all, I would be shocked if anyone had an interest in writing Ruby2JS frontend code and using esbuild as a bundling tool who <em>didn’t</em> actually have Ruby installed for use in a Ruby-based web project. So why rely on Opal/Node if we don’t have to?</p>
<h2 id="the-future-of-ruby2js">The Future of Ruby2JS</h2>
<p>Which brings us to a broader topic: the future of this project.</p>
<p><a href="http://intertwingly.net/blog/">Sam Ruby</a>, a well-known figure in the Ruby community and the brains behind Ruby2JS for many years, stepped down as an active maintainer in 2021. This effectively left me as the sole maintainer of Ruby2JS—and not only the sole maintainer, but by and large the <em>only</em> active contributor to Ruby2JS.</p>
<p>I had started contributing to the project in 2020, and through much trial-and-error and helpful mentorship from Sam, I eventually learned my way around the codebase enough to help usher in a few improvements to the feature set as well as set up this Bridgetown documentation site. It was an amazing experience, and I’d like to thank Sam publicly for his trust in (and patience with!) me.</p>
<p><strong>Here’s the deal:</strong> I love this project and sincerely hope to continue to see it fill an important role in the niche of “Ruby frontend web developers” as <a href="https://www.fullstackruby.dev">I like to consider myself to be</a>.</p>
<p>But the fact of the matter is I have my hands very full with the <a href="https://www.bridgetownrb.com">Bridgetown project</a>, and my ability to devote much attention to Ruby2JS is limited. In addition, what attention I <em>can</em> devote to Ruby2JS is mostly relegated to the use cases for which Ruby2JS is personally useful to me. I’m not saying that’s ideal. It just is what it is.</p>
<p>So because I <em>primarily</em> use Ruby2JS for writing web components (usually using <a href="https://lit.dev">Lit</a>) and bundling using esbuild, that is the principal scope I intend to maintain going forward. I would also argue that it’s a very ergonomic and obvious way to make the most of Ruby2JS as a web developer building projects (as I do) with Rails or Bridgetown.</p>
<p>Thus I have decided to deprecate quite a number of features (“filters” and other integration points) which will be removed officially by the time Ruby2JS 6.0 is released. I don’t have any immediate release date for that, but for the sake of discussion let’s assume it will happen towards the end of this year.</p>
<p>The list of deprecated features is as follows:</p>
<ul>
<li>jQuery filter</li>
<li>JSX filter</li>
<li>matchAll filter (only necessary for ES < 2020)</li>
<li>minitest-jasmine filter</li>
<li>Preact filter</li>
<li>React filter</li>
<li>Require filter</li>
<li>Underscore filter</li>
<li>Vue filter</li>
<li>CGI server integration</li>
<li>ExecJS integration</li>
<li>Haml integration</li>
<li>Rails integration (outside of the new “jsbundling” esbuild pipeline)</li>
<li>Sinatra integration (as a view template type)</li>
<li>Sprockets integration</li>
<li>“use strict” option (all modern ESM code is considered strict anyway)</li>
<li>Webpack loader</li>
</ul>
<p>In addition, I am actively looking for a maintainer to own the <a href="https://github.com/ruby2js/ruby2js/tree/master/packages">Rollup and Vite plugins</a> for transpiling Ruby2JS code via those bundlers. I don’t myself use Rollup/Vite, but I understand they’re quite popular as an alternative to using esbuild directly. They still need to be upgraded to use Ruby rather than Node for the transpilation (like esbuild).</p>
<p>If any of these stand out to you as having a <em>serious impact</em> on current production workflows, let’s talk about possible strategies—either migrations to a better solution, or extracting features out to their own repo. For example, I simply have no interest in maintaining a React filter. I don’t recommend people adopt React in new projects as a general rule, and if they do, then I recommend they use the Next.js framework because it just makes React much, much better. React + Ruby2JS is not a solution I can, in all good conscience, promote.</p>
<p><strong>However</strong>, if someone <em>really</em> needs a React filter long into the future for their projects, I’d be happy to help extract this functionality out to a separate gem with fresh open source governance. Again, that holds true for any of the deprecated features listed above.</p>
<p>The alternative to this approach, <em>and one I strongly considered</em>, would be for me to step down myself as a maintainer of Ruby2JS and seek someone else in the community to come onboard instead. I decided against this move for several reasons:</p>
<ul>
<li>I still really enjoy writing Ruby2JS code and singing the praises of the project.</li>
<li>This is a pretty gnarly codebase to wrap your head around, and I had the benefit of being mentored by Sam Ruby himself. For someone to come in fresh and begin to make sizable contributions, that’s a tall order—especially with the variety of JS packages now in the project as well. There’s also a fair bit of technical debt that has no clear upgrade path at present. For example:</li>
<li>We’re locked into an old version of Opal for compiling the JavaScript version of the Ruby2JS compiler. Over time, this will result in the Node version of Ruby2JS falling farther and farther behind relative to its native Ruby counterpart. It may mean that, at some future date, we sunset this other than for trivial use (such as the online interactive demos)—or we figure out why the transpiler is broken on newer version of Opal which will take a considerable amount of time (I’ve failed after two separate attempts).</li>
<li>And as mentioned above, nobody else <em>has</em> been contributing with any frequency.</li>
</ul>
<p>So ultimately I gladly intend on continuing to act as lead maintainer for Ruby2JS—while significantly reducing the scope of the project down to what (in my opinion) it is best suited for and what I best understand. And beyond that, any additional features are quite welcome to be handled via Ruby2JS “plugins” by others in the community.</p>
<h2 id="the-future-of-ruby-on-the-frontend">The Future of Ruby on the Frontend</h2>
<p>This brings me to my final and most general point regarding where we, the Ruby web developer community, are headed.</p>
<p>I have become fairly convinced with the release of <a href="https://ruby.github.io/ruby.wasm/">Ruby 3.2 and its brand-new WebAssembly support</a> that <strong>the future of Ruby on the frontend is Wasm</strong>. This means <a href="https://www.fullstackruby.dev/podcast/7/">I forsee a day</a> when writing <em>actual Ruby code</em> and directly executing it in the browser will be feasible for a considerable number of ambitious projects.</p>
<p>In effect, this will render both Ruby2JS <em>and</em> Opal obsolete. Why try to fiddle with various compile-to-JavaScript languages and syntaxes when you can simply write <strong>Ruby</strong> and run it?! That’s obviously the ideal, even if today there are significant hurdles to overcome (most notably large Wasm payloads and simplistic Ruby<->JS APIs).</p>
<p>So I look forward to that day, even if it’s still a few years away. In the meantime, I’m thrilled I can continue to write frontend code in a Ruby-like way using Ruby2JS. And I hope you are as well.</p>
<p><strong>Questions? Ideas? Suggestions?</strong> Hop in our <a href="https://github.com/ruby2js/ruby2js/discussions">GitHub Discussions</a> and let us know! And if you find an issue with Ruby2JS 5.1, please file an issue report so we can make Ruby2JS. Better yet, if you’d like to become a contributor yourself to Ruby2JS, we welcome your involvement and support!</p>Jared WhiteRegister2021-03-22T00:00:00-04:002021-03-22T00:00:00-04:00repo://posts.collection/_posts/2021-03-22-register.md<p>Originally, CoffeeScript had a
<a href="https://coffeescript.org/#nodejs-usage">coffeescript/register</a> module, which
would automatically compile CoffeeScript files on the fly.</p>
<p>Originally, Babel later adopted it, producing the
<a href="https://babeljs.io/docs/en/babel-register/">@babel/register</a> module, which
will automatically compile <code class="highlighter-rouge">.es6</code>, <code class="highlighter-rouge">.es</code>, <code class="highlighter-rouge">.jsx</code>, <code class="highlighter-rouge">.mjs</code>, and <code class="highlighter-rouge">.js</code> files the
fly.</p>
<p>Now, Ruby2JS has adopted the idea, and in fact uses the
<a href="https://github.com/ariporad/pirates#readme">same hook</a>, and makes available
the <a href="https://www.npmjs.com/package/@ruby2js/register">@ruby2js/register</a>
module, which will automatically compile <code class="highlighter-rouge">.rb</code> files on the fly.</p>
<h2 id="demonstration">Demonstration</h2>
<p>Let’s start with the simple and somewhat canonical greet function in Ruby:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># greet.rb</span>
<span class="k">def</span> <span class="nf">greet</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"Hello </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">!"</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Now add a main program, in JavaScript, which registers and configures Ruby2JS,
and then calls out to the above script:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.js</span>
<span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">@ruby2js/register</span><span class="dl">'</span><span class="p">)({</span>
<span class="na">options</span><span class="p">:</span> <span class="p">{</span>
<span class="na">eslevel</span><span class="p">:</span> <span class="mi">2021</span><span class="p">,</span>
<span class="na">autoexports</span><span class="p">:</span> <span class="dl">'</span><span class="s1">default</span><span class="dl">'</span><span class="p">,</span>
<span class="na">filters</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">cjs</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">functions</span><span class="dl">'</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">})</span>
<span class="kd">const</span> <span class="nx">greet</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">./greet</span><span class="dl">'</span><span class="p">)</span>
<span class="nx">greet</span><span class="p">(</span><span class="dl">'</span><span class="s1">World</span><span class="dl">'</span><span class="p">)</span>
</code></pre></div></div>
<p>All that’s left is to install the module and go:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yarn add @ruby2js/register
node main.js
</code></pre></div></div>
<p>Enjoy!</p>Sam RubyNew Stimulus Design Pattern?2021-02-24T00:00:00-05:002021-02-24T00:00:00-05:00repo://posts.collection/_posts/2021-02-24-new-stimulus-design-pattern.md<p>As far as I know, this is a new design pattern for Stimulus. At the very
least, it isn’t something I was able to readily find with Google searches.</p>
<p>First, let’s gets some standard stuff out of the way. The
<a href="https://ruby2js.com">Ruby2JS.com</a> is built using the
<a href="https://www.bridgetownrb.com/">Bridgetown</a> static site generator.
<a href="https://opalrb.com/">Opal</a> is used to generate the bulk of the scripts to be
executed. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching">HTTP
caching</a> ensures
that these scripts are only downloaded when they change. <a href="Turbo">Turbo</a>
ensures that these scripts are only loaded once per site visit.
<a href="Stimulus">Stimulus</a> associates HTML elements with controllers.</p>
<p>All standard stuff so far.</p>
<p><a href="https://ruby2js.com">Ruby2JS.com</a> has three controllers on the page. The
first is a Ruby editor. The second is a read-only JavaScript view. And the
third is invisible, but runs the JavaScript which outputs to the console log.
Other pages have these same three controllers with different arrangements
and/or different data, for example the <a href="https://www.ruby2js.com/examples/stimulus/">Stimulus
introduction</a> and the <a href="https://www.ruby2js.com/examples/stimulus/">React Simple
Component</a>. The <a href="https://www.ruby2js.com/demo">Ruby2JS
Demo</a> has a Ruby editor and a JS ouput, but
doesn’t have a results controller and adds an options controller.</p>
<p>The source to the controllers can be found in
<a href="https://github.com/ruby2js/ruby2js/tree/master/demo/controllers">GitHub</a>.
Unsurprisingly given that these controllers support the Ruby2JS site, they are
written in Ruby.</p>
<p>But that’s not the unique design pattern part.</p>
<p>Look at a Ruby editor on any of the pages mentioned. There isn’t really any
<a href="https://stimulus.hotwire.dev/reference/actions">Actions</a>,
<a href="https://stimulus.hotwire.dev/reference/targets">Targets</a>,
<a href="https://stimulus.hotwire.dev/reference/values">Values</a>, or <a href="https://stimulus.hotwire.dev/reference/css-classes">CSS
Classes</a> to speak of.</p>
<p>Instead, updates made in the Ruby editor are sent to <em>other</em> controllers.
A global overview of the design of these pages: the options controller on the
demo page will update the Ruby controller. The Ruby controller will update
both the JavaScript and evaluation results controllers. And there is even a
case where the evaluation results controller will update the Ruby controller,
but we will get to that in a minute.</p>
<p>All of this is accomplished by subclassing a <a href="https://github.com/ruby2js/ruby2js/blob/master/demo/livedemo.js.rb">common base
class</a> and
overridding the <code class="highlighter-rouge">source</code> method with calls to a <code class="highlighter-rouge">findController</code> method. The
<code class="highlighter-rouge">findController</code> method unsurprisingly searches the <code class="highlighter-rouge">application.controllers</code>
array. This base class also takes care of connecting sources with targets
indpendent of the order in which the controllers connect.</p>
<p>Once a source is paired with potentially multiple targets, messages pass via
standard method calls and/or attribute accessors (getters and setters in
JavaScript terms).</p>
<p>As an example,
<a href="https://github.com/ruby2js/ruby2js/blob/master/demo/controllers/ruby_controller.js.rb#L96">here</a>
are the lines of code where <code class="highlighter-rouge">Ruby2JS.convert</code> is called and the resulting JavaScript is sent to each target.</p>
<p>The JSController’s <a href="https://github.com/ruby2js/ruby2js/blob/91f75c3b83026bb0027c6fb390dafdd15a6ab6a9/demo/controllers/js_controller.js.rb#L38">implementation of the <code class="highlighter-rouge">contents=</code>
method</a>
will dispatch the content to the jsEditor.</p>
<p>The EvalController’s <a href="https://github.com/ruby2js/ruby2js/blob/master/demo/controllers/eval_controller.js.rb">implementation of the <code class="highlighter-rouge">contents=</code>
method</a>
will load the script into a <code class="highlighter-rouge">script</code> element and append it to the document.</p>
<p>An interesting detail: if you bring up the <a href="https://www.ruby2js.com/examples/stimulus/">Stimulus
Introduction</a> page and click on the
JavaScript tab you will see different results in Safari than you would in see
in Chrome, Firefox, or Microsoft Edge. Safari doesn’t yet support <a href="https://github.com/tc39/proposal-static-class-features">static
public fields</a>, so an
assignment statement after the class definition is used instead.</p>
<p>The way this works is that the Ruby souce code is initially converted to
JavaScript using the <a href="https://www.ruby2js.com/docs/eslevels#es2022-support">ES2022
option</a>, and the results
are sent to the evaluation controller. The evaluation controller captures the
syntax error and given that this occurred on the very first update it will
update the <code class="highlighter-rouge">options</code> in the Ruby Controller, triggering another conversion, the
results of which are sent to both the JS and Eval controllers.</p>
<p>While this usage is quite different than the traditional application of
Stimulus, the end result is comparable: a site consisting entirely of static
HTML augmented with a small number of <code class="highlighter-rouge">data-</code> attributes that cause the
controllers to activate.</p>
<p>I’m quite curious if others have seen this usage of Stimulus before, if they
find it useful, or have any suggestions.</p>Sam RubyNew live demo2021-01-19T00:00:00-05:002021-01-19T00:00:00-05:00repo://posts.collection/_posts/2021-01-19-new-live-demo.md<p>Check it out <a href="https://www.ruby2js.com/demo?preset=true">here</a>.</p>
<p>Because the conversion from Ruby to JavaScript is fast and runs right in your
browser, this demo will update the converted output keystroke-by-keystroke as
you enter your code. Find and fix your syntax errors, select the right
filters and options, and ensure the JavaScript to be generated is what you
intend before you commit your code.</p>
<p>Or just use it to learn idiomatic equivalents to common Ruby language
patterns. Just be sure to select a modern <code class="highlighter-rouge">eslevel</code>, and liberally add
filters, particularly filter <code class="highlighter-rouge">functions</code>.</p>
<p>Under the covers, it is running the same
<a href="https://github.com/ruby2js/ruby2js">ruby2js</a> code, just converted from Ruby
to JavaScript using the excellent <a href="https://opalrb.com/">Opal</a> converter.</p>
<p>See a conversion that either isn’t quite right or isn’t the best it can be?
<a href="../../docs/community/#ways-to-contribute">Let us know</a>!</p>
<p>Why Opal? Because it is the
<a href="../../docs/choose-your-tool">right tool for this job</a>.</p>
<p><br /></p>
<p><a href="/docs" class="button is-primary is-info">Ready to Give Ruby2JS a Try?</a></p>Sam RubyReact filter documentation has landed!2021-01-06T00:00:00-05:002021-01-06T00:00:00-05:00repo://posts.collection/_posts/2021-01-06-react-filter-documentation-has-landed.md<p>While filters can do one-for-one transformations, they can also do much more.
The <a href="/docs/filters/react">react</a> filter will, for example, do the following:</p>
<ul>
<li>
<p>Support three different syntaxes for HTML, one based on
<a href="https://reactjs.org/docs/introducing-jsx.html">JSX</a>, one involving
direct calls to
<a href="https://reactjs.org/docs/react-api.html#createelement">React.createElement</a>,
and finally one based on
<a href="https://github.com/rubys/wunderbar#readme">Wunderbar</a></p>
</li>
<li>
<p>Unifying all of the mechanisms to initialize, reference, and update state
with assignments and references to instance variables.</p>
</li>
<li>
<p>Automatically
<a href="https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/function/bind">bind</a>
all event handlers.</p>
</li>
<li>
<p>Eliminate the need to create event handlers for
<a href="https://reactjs.org/docs/forms.html#controlled-components">controlled components</a>.</p>
</li>
</ul>
<p>In addition to the <a href="/docs/filters/react">documentation</a>, a
<a href="https://github.com/ruby2js/ruby2js/tree/master/demo/reactjs.org#readme">downloadable</a>
version of the demos is provided, enabling you to be up and running in
seconds!</p>Sam RubyWelcome to the New Ruby2JS Website2020-12-29T00:00:00-05:002020-12-29T00:00:00-05:00repo://posts.collection/_posts/2020-12-29-welcome-to-the-new-ruby2js-website.md<p>There’s only so much you can do with a README on GitHub and then it’s time for an upgrade. 😎</p>
<p>So here it is! The brand new <strong>Ruby2JS</strong> documentation site. Now that we have a spiffy new home on the interwebs, we’ll be polishing up and adding additional documentation and examples, as well as posting news updates as features and improvements get added to the project.</p>
<p>Many thanks to <a href="https://github.com/rubys">Sam Ruby</a> for giving me (Jared) so much runway to transition everything over to the new site (and a <a href="https://github.com/ruby2js">new org on GitHub!</a>). Lots more exciting news in store for 2021!</p>
<p><br /></p>
<p><a href="/docs" class="button is-primary is-info">Ready to Give Ruby2JS a Try?</a></p>Jared White