Skip to content

Releases: sebyx07/ruby2html

Release list

v2.1.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 22:59
ff5ca08

Added

  • JRuby now ships a small Java extension (Ruby2html::JavaNative) that accelerates HTML escaping — the renderer's hot path — over the pure-Ruby implementation. About 1.9x faster end-to-end rendering on JRuby; output is byte-identical. Distributed as a ruby2html-<version>-java platform gem with the jar prebuilt (no JDK needed to install); everything else stays pure Ruby.

Changed

  • Ruby2html::Component#render_in accepts Rails' keyword options (locals:), as Rails main (8.2) passes them and deprecates render_in without options.
  • TruffleRuby now uses the pure-Ruby implementation instead of the C extension. TruffleRuby runs C extensions through Sulong but JITs pure Ruby with Graal, which here renders ~28x faster; Ruby2html.native? is now false on TruffleRuby.
  • On CRuby, a C extension that fails to load now prints one warning with the reason (ruby2html: C extension not loaded (...); using the pure-Ruby renderer) instead of silently rendering about 3x slower. JRuby/TruffleRuby and RUBY2HTML_DISABLE_EXT=1 stay silent.
  • Development and single-version CI jobs now run on the latest stable Ruby (4.0). The CRuby test matrix already tracks the official maintained-versions list automatically (currently 3.3, 3.4, 4.0).

v2.0.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 18:01
1821b1d

A rewrite of the renderer for speed, memory and correctness, plus a new component class, Rails 8.1 / ViewComponent 4 support, JRuby/TruffleRuby support and full CI/release automation. Contains breaking changes: read "Upgrading to 2.0" below.

Performance

Measured on Ruby 3.4.7 x86_64 (bench/render_bench.rb, a 100-item page with attributes and escaped text):

  • About 4.7x faster than 1.6.6 (≈620 → ≈2,950 renders/s) with about 8x fewer allocations per render (3,531 → 419).
  • Faster than Erubi (Rails' ERB engine) and Phlex on the same page; see the README snapshot and the CI benchmark workflow for current numbers.
  • HTML escaping uses SSE2 (x86_64) / NEON (arm64): up to 5.9x faster on large clean text, about 2x on typical strings.
  • require 'ruby2html' costs about 0.4 MB and 19 ms (was about 9.5 MB and 116 ms): Prism, the Rails glue and htmlbeautifier load only when used; in eager-loading (production) Rails apps they load at boot instead.

Added

  • Ruby2html::Component: a lightweight component base class (no ViewComponent needed) with view_template/call, render_in, helpers and content; nested components write straight into the parent's buffer.
  • Pure-Ruby implementation with byte-identical output (Ruby2html::PureRuby), used on JRuby, when the extension can't be built, or with RUBY2HTML_DISABLE_EXT=1. Ruby2html.native? and Ruby2html::IMPLEMENTATION tell which one is loaded.
  • Ruby2html.escape_html(obj).
  • Attributes: true renders a bare attribute, false omits it; nested data:/aria: Hashes expand to data-user-id="1"; Arrays render as space-separated tokens (class: ['btn', active && 'on']).
  • .html.rb templates are compiled with Prism to run directly in the view/component (no instance-variable copying, no method_missing).
  • capture, content_for and provide with blocks of ruby2html tags work in .html.rb templates.
  • Support for Rails 8.1, ViewComponent 4, JRuby and TruffleRuby; fiber-safe rendering (no thread/fiber-global state).
  • llms.txt: complete reference for AI coding tools; AGENTS.md (with CLAUDE.md symlinked to it) and Claude Code skills/agents for maintainers.
  • CI: generated CRuby matrix (all maintained versions), Rails matrix, Linux x86_64/arm64 and macOS builds, -Werror gcc/clang builds, sanitizers, JRuby/TruffleRuby, gem install smoke test, benchmarks in every PR; Dependabot; releases published automatically when a version bump merges (RubyGems trusted publishing).
  • Maintained by developerz.ai: its maintainer agent triages issues, reviews PRs, merges on green, runs daily health and release checkups, and opens release PRs (.dz/).

Fixed

  • Attribute cache keyed by Object#hash: a hash collision could render another element's attributes; the cache also grew without bound. Removed.
  • link_to rendered a button_to form.
  • form_with emitted its fields as escaped text, ignored patch/delete (_method), crashed on submit without a label, and ignored Rails' CSRF/data-remote settings. It now delegates to Rails' own form_with and FormBuilder.
  • Tags inside link_to/button_to blocks were emitted before the link instead of inside it.
  • html_safe content was escaped twice; non-String content (p 42) was silently dropped.
  • A block's String return value replaced markup the block had already written.
  • The <output> tag was unusable (shadowed by Render#output).
  • Template backtraces were off by 3 lines; the renderer wasn't restored when a template raised; template locals could clash with internal variables or be rewritten into tag calls (h2 title in a partial with a title local).
  • A rescued exception while writing attributes left a half-written start tag in the output.
  • HtmlBeautifierMiddleware beautified each streamed chunk separately, didn't close the body, and touched gzip/204/304 responses.
  • The packaged gem shipped a locally compiled .so; rake build didn't build a gem.

Upgrading to 2.0

Templates and components:

  1. self in .html.rb templates is now the view (or component), not a Ruby2html::Render. Replace self.div / self.plain with div / plain. Without Prism (Ruby < 3.3 and no prism gem) templates still use the 1.x path.
  2. Thread.current[:__ruby2html_renderer__] is gone. The active renderer lives on the component/view; use component.__ruby2html_renderer__.
  3. form_with output now matches Rails (_method, accept-charset, no forced data-remote, no utf8 field); its builder is a proxy around Rails' FormBuilder.
  4. Ruby2html::ComponentHelper#method_missing is now private.

HTML output:
5. Non-String content renders: p 42 → <p>42</p> (1.x: <p></p>); nil renders nothing.
6. html_safe strings passed as content or returned from a block are inserted raw (1.x escaped them).
7. A block's return value is used only if the block wrote nothing, and only for String/Symbol/Numeric (1.x: a returned String replaced everything the block wrote).
8. Attributes: true → bare attribute and false omitted (except data-*, aria-*, draggable, spellcheck, contenteditable, which keep "true"/"false"); Hash values expand; Arrays join with spaces; invalid attribute names raise ArgumentError; Hashes/Arrays nested deeper than 32 levels raise ArgumentError.
9. Tag methods accept at most one positional argument (extra arguments raise ArgumentError).
10. The output buffer is UTF-8; strings in other encodings are transcoded (raising EncodingError if impossible).

Removed / packaging:
11. Ruby2html::ATTRIBUTE_CACHE, Render::METHOD_DEFINITIONS, Render#output and the Render#fast_escape_html / #fast_attributes_to_s / #fast_buffer_append / #fast_render_tag methods are removed. Use Ruby2html.escape_html, the value returned by render, or current_output.
12. Ruby >= 3.1 and (for Rails integration) Rails >= 7.2 are required.
13. htmlbeautifier is no longer a dependency: add gem 'htmlbeautifier' if you use Ruby2html::HtmlBeautifierMiddleware.
14. The gem's require path is lib/gem only.