ruby2html 1.6.6 โ†’ 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,159 +1,120 @@
1
- # Ruby2html ๐Ÿ”ฎโœจ
1
+ # Ruby2html
2
2
 
3
- Transform your view logic into elegant, semantic HTML with the power of pure Ruby! ๐Ÿš€โœจ
3
+ [![CI](https://github.com/sebyx07/ruby2html/actions/workflows/ci.yml/badge.svg)](https://github.com/sebyx07/ruby2html/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/ruby2html)](https://rubygems.org/gems/ruby2html)
5
+ [![Maintained by developerz.ai](https://img.shields.io/badge/maintained%20by-developerz.ai-6f42c1)](https://developerz.ai)
4
6
 
5
- ## ๐ŸŒŸ What is Ruby2html?
7
+ Write views in pure Ruby; get escaped, compact HTML. A C extension handles escaping, attribute serialization and tag rendering. Works standalone, as Rails `.html.rb` templates, inside ERB, and in ViewComponent.
6
8
 
7
- Ruby2html is a magical gem that allows you to write your views in pure Ruby and automatically converts them into clean, well-formatted HTML. Say goodbye to messy ERB templates and hello to the full power of Ruby in your views! ๐ŸŽ‰
9
+ ```ruby
10
+ div class: 'card', 'data-controller': 'card' do
11
+ h2 @product.title
12
+ p @product.description
13
+ a 'Details', href: product_path(@product), class: 'btn'
14
+ end
15
+ ```
16
+
17
+ ```html
18
+ <div class="card" data-controller="card"><h2>Lamp</h2><p>Warm &amp; bright</p><a href="/products/1" class="btn">Details</a></div>
19
+ ```
8
20
 
9
- ## ๐Ÿš€ Installation
21
+ ## Why ruby2html
10
22
 
11
- Add this line to your application's Gemfile:
23
+ - **Fastest Ruby HTML renderer we've measured.** The page benchmark renders 1.35x faster than Erubi (Rails' ERB engine) and 2.4โ€“16x faster than Phlex 2, with 40โ€“90% fewer allocations; in a full Rails request it leads ERB, Slim and Phlex ([numbers](#performance)).
24
+ - **Light.** No runtime dependencies, a 32 KB gem, and `require 'ruby2html'` costs about 0.4 MB and 19 ms. Prism, the Rails glue and the optional beautifier load only when used, or at boot in eager-loading production apps.
25
+ - **Plain Ruby views.** `.html.rb` templates, ERB interop, ViewComponent, or the built-in `Ruby2html::Component`. Escaped by default.
26
+ - **Runs everywhere.** CRuby โ‰ฅ 3.1 with a C extension (SIMD escaping on x86_64 and arm64), and JRuby/TruffleRuby through a byte-identical pure-Ruby implementation. Rails 7.2+.
27
+ - **AI-ready.** One-file reference for coding agents: [`llms.txt`](llms.txt).
12
28
 
29
+ ## Installation
30
+
31
+ Supports all maintained CRuby versions (>= 3.1), JRuby (via the pure-Ruby implementation), TruffleRuby, and Rails 7.2+; the CI matrix is generated automatically.
13
32
 
14
33
  ```ruby
34
+ # Gemfile
15
35
  gem 'ruby2html'
16
36
  ```
17
37
 
18
- And then execute:
38
+ ```bash
39
+ bundle install # compiles the native extension (needs a C compiler); pure-Ruby fallback otherwise
40
+ ```
41
+
42
+ ## AI agents
43
+
44
+ **This project is maintained by [developerz.ai](https://developerz.ai).** Its maintainer agent triages issues, reviews every pull request, merges every pull request once its author marks it ready and CI passes, watches project health daily, and opens release PRs. The policy is in [`.dz/maintainer/maintainer.yml`](.dz/maintainer/maintainer.yml). Breaking changes and major releases stay with the owner.
19
45
 
20
- $ bundle install
46
+ - **Using ruby2html in your app?** Point your agent at [`llms.txt`](llms.txt), the complete reference in one file (DSL rules, escaping, Rails, components, forms, pitfalls). Paste this into your app's `CLAUDE.md` / `AGENTS.md`:
47
+ ```markdown
48
+ ## ruby2html views
49
+ `*.html.rb` templates and `html do ... end` blocks use the ruby2html gem. Before editing them read
50
+ https://github.com/sebyx07/ruby2html/blob/main/llms.txt
51
+ ```
52
+ - **Working on this repo?** [`AGENTS.md`](AGENTS.md) is the only agent-instructions file (`CLAUDE.md` is a symlink to it): commands, code map, invariants, how to verify a change, CI/release automation.
53
+ - **Claude Code**: project skills in `.claude/skills/` (`release`, `benchmark`, `add-html-tag`, `review`, `debug-render`, `upgrade-deps`), read-only reviewer subagents in `.claude/agents/` (`perf-reviewer`, `xss-auditor`), and `.claude/settings.json` with a safe permission allowlist and a non-blocking recompile-reminder hook.
21
54
 
22
- Or install it yourself as:
55
+ ## Usage
23
56
 
24
- $ gem install ruby2html
57
+ ### DSL basics
25
58
 
26
- ## ๐ŸŽจ Usage
59
+ - Every HTML5 tag is a method (`div`, `p`, `ul`, `li`, `img`, `input`, ...), plus `turbo_frame` / `turbo_stream`.
60
+ - `tag content, **attrs` or `tag(**attrs) { nested tags }`. Text and attribute values are HTML-escaped; numbers and symbols are converted (`td 42`).
61
+ - Raw HTML only from `html_safe` strings (Rails helpers, `'<hr>'.html_safe`), as content or via `plain`. `plain 'text'` escapes.
62
+ - Attributes: `'data-id': 1` or `data: { id: 1 }`, `class: ['btn', active && 'on']`, `disabled: true` โ†’ bare `disabled`, `nil`/`false` omitted.
27
63
 
28
- ### In your views
64
+ Full rules and pitfalls: [`llms.txt`](llms.txt).
29
65
 
30
- File: `app/views/your_view.html.rb`
66
+ ### Rails views (`.html.rb`)
67
+
68
+ File: `app/views/products/index.html.rb`
31
69
 
32
70
  ```ruby
33
71
  div class: 'container' do
34
- h1 'Welcome to Ruby2html! ๐ŸŽ‰', class: 'main-title', 'data-controller': 'welcome'
35
- link_to 'Home Sweet Home ๐Ÿ ', root_path, class: 'btn btn-primary', 'data-turbo': false
72
+ h1 'Products', class: 'main-title'
73
+ link_to 'Home', root_path, class: 'btn btn-primary', 'data-turbo': false
36
74
 
37
75
  @products.each do |product|
38
- h2 class: 'item-title', id: "product-#{product[:id]}" do
39
- product.title
40
- end
41
- p class: 'item-description' do
42
- product.description
43
- end
76
+ h2 product.title, class: 'item-title', id: "product-#{product.id}"
77
+ p product.description, class: 'item-description'
44
78
  end
45
79
  end
46
80
 
47
81
  plain '<div>Inline html</div>'.html_safe
48
-
49
82
  render partial: 'shared/navbar'
50
83
  ```
51
84
 
52
- ### (Optional) Nicely Format the HTML for source inspection
53
-
54
- File: `config/environments/development.rb` or `config/environments/test.rb`
55
- ```ruby
56
- config.middleware.use Ruby2html::HtmlBeautifierMiddleware
57
- ```
58
-
59
- #### Or use your current .erb views
85
+ Controller instance variables are available. Partials can be `.html.rb` too. `link_to`, `image_tag`, `button_to`, `form_with` and `render` write their output directly; other helpers return a value you must output (`h1 t('.title')`, `plain some_safe_html_helper`).
60
86
 
61
- ### In your ApplicationController
62
-
63
- File: `app/controllers/application_controller.rb`
87
+ ### Inside ERB
64
88
 
65
89
  ```ruby
66
- # frozen_string_literal: true
67
-
68
90
  class ApplicationController < ActionController::Base
69
- include Ruby2html::RailsHelper # to access the <%= html %> helper
91
+ include Ruby2html::RailsHelper # exposes the html(context) helper to views
70
92
  end
71
93
  ```
72
94
 
73
- File: `app/views/your_view.html.erb`
74
-
75
- Replace your ERB with beautiful Ruby code:
76
-
77
95
  ```erb
78
- <%=
79
- html(self) do
80
- h1 "Welcome to Ruby2html! ๐ŸŽ‰", class: 'main-title', 'data-controller': 'welcome'
81
- div id: 'content', class: 'container' do
82
- link_to 'Home Sweet Home ๐Ÿ ', root_path, class: 'btn btn-primary', 'data-turbo': false
83
- end
84
-
85
- @items.each do |item|
86
- h2 class: 'item-title', id: "item-#{item[:id]}" do
87
- item.title
88
- end
89
- p class: 'item-description' do
90
- item.description
91
- end
92
- end
93
-
94
- plain "<div>Inline html</div>".html_safe
95
-
96
- render partial: 'shared/navbar'
96
+ <h1>Legacy ERB header</h1>
97
+ <%= html(self) do
98
+ div class: 'ruby2html-section' do
99
+ h2 'This section is powered by Ruby2html'
100
+ @items.each { |item| p item[:title] }
97
101
  end
98
- %>
102
+ end %>
99
103
  ```
100
104
 
101
- ### Benchmark
105
+ ERB, Slim and Ruby2html views, partials and components can be mixed freely, so adoption can be gradual.
102
106
 
103
- ```bash
104
- ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [x86_64-linux]
105
- Warming up --------------------------------------
106
- GET /benchmark/html (ERB)
107
- 40.000 i/100ms
108
- GET /benchmark/ruby (Ruby2html templates .html.rb)
109
- 12.000 i/100ms
110
- GET /benchmark/ruby (Ruby2html + view components)
111
- 12.000 i/100ms
112
- GET /benchmark/slim (Slim)
113
- 46.000 i/100ms
114
- GET /benchmark/phlex (Phlex)
115
- 34.000 i/100ms
116
- Calculating -------------------------------------
117
- GET /benchmark/html (ERB)
118
- 414.030 (ยฑ 2.4%) i/s - 24.840k in 60.032818s
119
- GET /benchmark/ruby (Ruby2html templates .html.rb)
120
- 124.973 (ยฑ 3.2%) i/s - 7.500k in 60.071485s
121
- GET /benchmark/ruby (Ruby2html + view components)
122
- 123.211 (ยฑ 4.1%) i/s - 7.380k in 60.000731s
123
- GET /benchmark/slim (Slim)
124
- 431.525 (ยฑ 9.0%) i/s - 25.668k in 60.103492s
125
- GET /benchmark/phlex (Phlex)
126
- 328.925 (ยฑ 7.0%) i/s - 19.618k in 60.019961s
127
-
128
- Comparison:
129
- GET /benchmark/slim (Slim): 431.5 i/s
130
- GET /benchmark/html (ERB): 414.0 i/s - same-ish: difference falls within error
131
- GET /benchmark/phlex (Phlex): 328.9 i/s - 1.31x slower
132
- GET /benchmark/ruby (Ruby2html templates .html.rb): 125.0 i/s - 3.45x slower
133
- GET /benchmark/ruby (Ruby2html + view components): 123.2 i/s - 3.50x slower
134
- ```
135
-
136
- ### With ViewComponents
137
-
138
- Ruby2html seamlessly integrates with ViewComponents, offering flexibility in how you define your component's HTML structure. You can use the `call` method with Ruby2html syntax, or stick with traditional `.erb` template files.
139
-
140
- File: `app/components/application_component.rb`
107
+ ### ViewComponent
141
108
 
142
109
  ```ruby
143
- # frozen_string_literal: true
144
-
145
110
  class ApplicationComponent < ViewComponent::Base
146
111
  include Ruby2html::ComponentHelper
147
112
  end
148
113
  ```
149
114
 
150
- #### Option 1: Using `call` method with Ruby2html
151
-
152
- File: `app/components/greeting_component.rb`
115
+ Inline `call`:
153
116
 
154
117
  ```ruby
155
- # frozen_string_literal: true
156
-
157
118
  class GreetingComponent < ApplicationComponent
158
119
  def initialize(name)
159
120
  @name = name
@@ -161,173 +122,142 @@ class GreetingComponent < ApplicationComponent
161
122
 
162
123
  def call
163
124
  html do
164
- h1 class: 'greeting', 'data-user': @name do
165
- "Hello, #{@name}! ๐Ÿ‘‹"
166
- end
167
- p class: 'welcome-message' do
168
- 'Welcome to the wonderful world of Ruby2html!'
169
- end
125
+ h1 "Hello, #{@name}!", class: 'greeting', 'data-user': @name
126
+ p 'Welcome to Ruby2html!', class: 'welcome-message'
170
127
  end
171
128
  end
172
129
  end
173
130
  ```
174
131
 
175
- #### Option 2: Using traditional ERB template
176
-
177
- File: `app/components/farewell_component.rb`
132
+ Or a sidecar template, `app/components/farewell_component.html.rb`:
178
133
 
179
134
  ```ruby
180
- # frozen_string_literal: true
181
-
182
- class FarewellComponent < ApplicationComponent
183
- def initialize(name)
184
- @name = name
185
- end
186
- end
187
- ```
188
-
189
- File: `app/components/farewell_component.html.rb`
190
-
191
- ```rb
192
135
  div class: 'farewell' do
193
- h1 class: 'farewell-message' do
194
- "Goodbye, #{@name}! ๐Ÿ‘‹"
195
- end
196
- p class: 'farewell-text' do
197
- 'We hope to see you again soon!'
198
- end
136
+ h1 "Goodbye, #{@name}!", class: 'farewell-message'
199
137
  end
200
138
  ```
201
139
 
202
- This flexibility allows you to:
203
- - Use Ruby2html syntax for new components or when refactoring existing ones
204
- - Keep using familiar ERB templates where preferred
205
- - Mix and match approaches within your application as needed
206
-
207
- ### More Component Examples
140
+ Public component methods can be called from the template and may emit tags themselves (see `app/components/first_component.rb`).
208
141
 
209
- File: `app/components/first_component.rb`
142
+ ### `Ruby2html::Component` (no ViewComponent required)
210
143
 
211
144
  ```ruby
212
- # frozen_string_literal: true
213
-
214
- class FirstComponent < ApplicationComponent
215
- def initialize
216
- @item = 'Hello, World!'
145
+ class CardComponent < Ruby2html::Component
146
+ def initialize(title:)
147
+ @title = title
217
148
  end
218
149
 
219
- def call
220
- html do
221
- h1 id: 'first-component-title' do
222
- 'first component'
223
- end
224
- div class: 'content-wrapper' do
225
- h2 'A subheading'
226
- end
227
- p class: 'greeting-text', 'data-testid': 'greeting' do
228
- @item
229
- end
150
+ def view_template
151
+ div class: 'card' do
152
+ h2 @title
153
+ content # block passed to render
154
+ link_to 'More', helpers.root_path
230
155
  end
231
156
  end
232
157
  end
158
+
159
+ # any view or controller
160
+ render CardComponent.new(title: 'Hi')
233
161
  ```
234
162
 
235
- File: `app/components/second_component.rb`
163
+ Tag methods run on the component itself (no instance-variable copying); nested ruby2html components write into the parent's buffer.
236
164
 
237
- ```ruby
238
- # frozen_string_literal: true
165
+ ### Forms
239
166
 
240
- class SecondComponent < ApplicationComponent
241
- def call
242
- html do
243
- h1 class: 'my-class', id: 'second-component-title', 'data-controller': 'second' do
244
- 'second component'
245
- end
246
- link_to 'Home', root_path, class: 'nav-link', 'data-turbo-frame': false
247
- end
248
- end
167
+ ```ruby
168
+ form_with model: @user do |f|
169
+ f.label :email
170
+ f.text_field :email
171
+ f.submit 'Save'
249
172
  end
250
173
  ```
251
174
 
252
- ## Without Rails
175
+ ### Without Rails
176
+
253
177
  ```ruby
254
- renderer = Ruby2html::Render.new(nil) do # context by default is nil, you can use self or any other object
178
+ require 'ruby2html'
179
+
180
+ Ruby2html::Render.new do # optional context object: its public methods and ivars become available
255
181
  html do
256
- head do
257
- title 'Ruby2html Example'
258
- end
259
- body do
260
- h1 'Hello, World!'
261
- end
182
+ head { title 'Ruby2html Example' }
183
+ body { h1 'Hello, World!' }
262
184
  end
263
- end
264
-
265
- puts renderer.render # => "<html><head><title>Ruby2html Example</title></head><body><h1>Hello, World!</h1></body></html>"
185
+ end.render
186
+ # => "<html><head><title>Ruby2html Example</title></head><body><h1>Hello, World!</h1></body></html>"
266
187
  ```
267
188
 
268
- ## ๐Ÿข Gradual Adoption
269
-
270
- One of the best features of Ruby2html is that you don't need to rewrite all your views at once! You can adopt it gradually, mixing Ruby2html with your existing ERB templates. This allows for a smooth transition at your own pace.
271
-
272
- ### Mixed usage example
189
+ ### Pretty HTML in development
273
190
 
274
- File: `app/views/your_mixed_view.html.erb`
191
+ ```ruby
192
+ # Gemfile (development group): gem 'htmlbeautifier'
193
+ # config/environments/development.rb
194
+ config.middleware.use Ruby2html::HtmlBeautifierMiddleware
195
+ ```
275
196
 
276
- ```erb
277
- <h1>Welcome to our gradually evolving page!</h1>
197
+ ## Performance
278
198
 
279
- <%= render partial: 'legacy_erb_partial' %>
199
+ Benchmarks run in CI on every push to `main` and every pull request ([`.github/workflows/benchmark.yml`](.github/workflows/benchmark.yml)): the latest run's summary page has the current table, and PRs get a sticky comment with the delta against `main`. Run them locally with `bundle exec rake bench` (standalone scripts in `bench/`) or `bundle exec rspec spec/benchmarks/` (request-level through the demo app, vs ERB, Slim, Phlex and ViewComponent).
280
200
 
281
- <%=
282
- html(self) do
283
- div class: 'ruby2html-section' do
284
- h2 "This section is powered by Ruby2html!"
285
- p "Isn't it beautiful? ๐Ÿ˜"
286
- end
287
- end
288
- %>
201
+ ### Snapshot at 2.0.0 release (2026-10-31)
289
202
 
290
- <%= render ModernComponent.new %>
203
+ This is a dated snapshot. CI has the live numbers. Measured on Ruby 3.4.7 x86_64 on a busy machine (load average about 7), so read the ratios rather than the absolute figures.
291
204
 
292
- <footer>
293
- <!-- More legacy ERB code -->
294
- </footer>
295
- ```
205
+ **Page render** (`bench/render_bench.rb`, no Rails): a header with nav, 100 list items with links, images, attributes and escaped text, about 34 KB of HTML.
296
206
 
297
- In this example, you can see how Ruby2html seamlessly integrates with existing ERB code. This approach allows you to:
207
+ | Engine | Renders/s | Time per render | Allocations per render |
208
+ |---|---|---|---|
209
+ | **ruby2html 2.0 (C extension)** | **2,676** | **0.37 ms** | **419** |
210
+ | Erubi 1.x (Rails' ERB engine) | 1,988 (1.35x slower) | 0.50 ms | 713 |
211
+ | ruby2html 2.0 (pure Ruby: JRuby, no compiler) | ~820 | ~1.2 ms | 1,029 |
212
+ | Phlex 2.4, enlarged attribute cacheยน | ~1,250 (โ‰ˆ2.4x slower) | ~0.8 ms | 831 |
213
+ | Phlex 2.4, defaults | 164 (16x slower) | 6.1 ms | 6,410 |
214
+ | ruby2html 1.6.6 | ~620 | ~1.6 ms | 3,531 |
298
215
 
299
- - Keep your existing ERB templates and partials
300
- - Gradually introduce Ruby2html in specific sections
301
- - Use Ruby2html in new components while maintaining older ones
302
- - Refactor your views at your own pace
216
+ ยน Phlex 2's default attribute cache (2 KB, FIFO) misses on per-row values such as ids and hrefs. `Phlex::ATTRIBUTE_CACHE.expand(1_000_000)` is its best case. Phlex 2 also checks URLs for `javascript:`; ruby2html leaves URL validation to you.
303
217
 
304
- Remember, there's no rush! You can keep your `.erb` files and Ruby2html code side by side until you're ready to fully transition. This flexibility ensures that adopting Ruby2html won't disrupt your existing workflow or require a massive rewrite of your application. ๐ŸŒˆ
218
+ **Full Rails request** (`spec/benchmarks/requests_spec.rb`; Rails 8.1, a 7 ms request where most of the time is Rails itself; differences are within ยฑ15% error):
305
219
 
306
- ## ๐Ÿ›  Development
220
+ | Engine | Requests/s |
221
+ |---|---|
222
+ | **`Ruby2html::Component`** | **144.5** |
223
+ | **ViewComponent 4 + ruby2html** | **142.2** |
224
+ | **`.html.rb` template** | **139.6** |
225
+ | Slim | 128.3 |
226
+ | ERB | 122.6 |
227
+ | Phlex 2.4 | 116.6 |
307
228
 
308
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
229
+ **HTML escaping** (SIMD vs scalar): up to 5.9x faster on 10 KB clean text, about 2x on 200-byte strings; short strings take the scalar path.
309
230
 
310
- ## ๐Ÿค Contributing
231
+ **Footprint:**
311
232
 
312
- Bug reports and pull requests are welcome on GitHub at https://github.com/sebyx07/ruby2html. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/sebyx07/ruby2html/blob/master/CODE_OF_CONDUCT.md).
233
+ | | Ruby/C lines | Runtime dependencies | `require` cost |
234
+ |---|---|---|---|
235
+ | **ruby2html** | 1.1k Ruby + 0.8k C | none | ~0.4 MB, ~19 ms |
236
+ | phlex + phlex-rails | 6.2k | refract, zeitwerk, railties | ~1.4 MB, ~30 ms (phlex alone) |
237
+ | view_component | 5.3k | actionview, activesupport, prism | |
238
+ | slim | 7.3k | temple, tilt | |
313
239
 
314
- ## ๐Ÿ“œ License
240
+ How it is fast:
315
241
 
316
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
242
+ - **Tag methods in C**: each tag writes open tag, content/block output and close tag straight into one shared buffer (no nested buffers, no `*args`/`**kwargs` allocation without keywords).
243
+ - **HTML escaping**: 16-byte SSE2/NEON scan for `& < > " '`; clean strings are one `memcpy`, dirty strings get their exact size computed once.
244
+ - **Attributes**: written directly into the buffer, no intermediate strings, no cache to thrash or leak.
245
+ - **Lazy loading**: `require 'ruby2html'` loads only the renderer. Production Rails apps load everything at boot, so forked workers share it copy-on-write.
246
+ - **Templates and components**: `.html.rb` templates are rewritten at compile time (Prism) to run in the view itself; `Ruby2html::Component` runs tag methods on the component. No instance-variable copying or `method_missing` per render.
247
+ - A pure-Ruby implementation with identical output is used when the extension is unavailable.
317
248
 
318
- ## ๐ŸŒˆ Code of Conduct
249
+ ## Development
319
250
 
320
- Everyone interacting in the Ruby2html project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/sebyx07/ruby2html/blob/master/CODE_OF_CONDUCT.md).
251
+ ```bash
252
+ bundle install
253
+ bundle exec rake compile # build the C extension
254
+ CI=1 bundle exec rspec # specs (CI=1 skips the slow benchmark spec)
255
+ bundle exec rubocop
256
+ bin/rails server # demo app
257
+ ```
321
258
 
322
- ## ๐ŸŒŸ Features
259
+ The repository is both the gem (`lib/gem/`, `ext/ruby2html/`) and a Rails demo app (`app/`, `config/`). See [CONTRIBUTING.md](CONTRIBUTING.md), [docs/architecture.md](docs/architecture.md), [docs/release.md](docs/release.md) and [CHANGELOG.md](CHANGELOG.md).
323
260
 
324
- - Write views in pure Ruby ๐Ÿ’Ž
325
- - Seamless Rails integration ๐Ÿ›ค๏ธ
326
- - ViewComponent support with flexible template options ๐Ÿงฉ
327
- - Automatic HTML beautification ๐Ÿ’…
328
- - Easy addition of custom attributes and data attributes ๐Ÿท๏ธ
329
- - Gradual adoption - mix with existing ERB templates ๐Ÿข
330
- - Improved readability and maintainability ๐Ÿ“š
331
- - Full access to Ruby's power in your views ๐Ÿ’ช
261
+ ## License
332
262
 
333
- Start writing your views in Ruby today and experience the magic of Ruby2html! โœจ๐Ÿ”ฎ
263
+ MIT. See [LICENSE](LICENSE).