ruby2html 1.5.7 → 1.6.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.
- checksums.yaml +4 -4
- data/README.md +333 -0
- data/ext/ruby2html/extconf.rb +16 -0
- data/ext/ruby2html/ruby2html.c +108 -0
- data/lib/gem/ruby2html/render.rb +65 -59
- data/lib/gem/ruby2html/version.rb +1 -1
- data/lib/ruby2html/ruby2html.so +0 -0
- data/ruby2html.gemspec +17 -7
- metadata +69 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fe6d6acf3bfc4153070aed751386fd1c3ae93b1fa0eb7f852a3678fd17e2826
|
4
|
+
data.tar.gz: ad9fcb5b1b938959002439da8a82d613590068e2cad943f69810678e641dc99d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a68922e47e1b82cb5eec9d37eeaa31a5ecbf3b1627dcbdd9af355516c3b5b6838b1cc7ef7e7df2c5d5935a4e8f075f73eef13ced4a696c9ab14cfe8c3e6c71b7
|
7
|
+
data.tar.gz: 6d4b7b4fe8438989ee4a501e5181bcaba9f86614ba66ef7a905414239bb311764b21a88c1690adee7164c357354ecdc1354ad1128ae427d4f4c601fa5786b978
|
data/README.md
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
# Ruby2html 🔮✨
|
2
|
+
|
3
|
+
Transform your view logic into elegant, semantic HTML with the power of pure Ruby! 🚀✨
|
4
|
+
|
5
|
+
## 🌟 What is Ruby2html?
|
6
|
+
|
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! 🎉
|
8
|
+
|
9
|
+
## 🚀 Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'ruby2html'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle install
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install ruby2html
|
25
|
+
|
26
|
+
## 🎨 Usage
|
27
|
+
|
28
|
+
### In your views
|
29
|
+
|
30
|
+
File: `app/views/your_view.html.rb`
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
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
|
36
|
+
|
37
|
+
@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
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
plain '<div>Inline html</div>'.html_safe
|
48
|
+
|
49
|
+
render partial: 'shared/navbar'
|
50
|
+
```
|
51
|
+
|
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
|
60
|
+
|
61
|
+
### In your ApplicationController
|
62
|
+
|
63
|
+
File: `app/controllers/application_controller.rb`
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# frozen_string_literal: true
|
67
|
+
|
68
|
+
class ApplicationController < ActionController::Base
|
69
|
+
include Ruby2html::RailsHelper # to access the <%= html %> helper
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
File: `app/views/your_view.html.erb`
|
74
|
+
|
75
|
+
Replace your ERB with beautiful Ruby code:
|
76
|
+
|
77
|
+
```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'
|
97
|
+
end
|
98
|
+
%>
|
99
|
+
```
|
100
|
+
|
101
|
+
### Benchmark
|
102
|
+
|
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`
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# frozen_string_literal: true
|
144
|
+
|
145
|
+
class ApplicationComponent < ViewComponent::Base
|
146
|
+
include Ruby2html::ComponentHelper
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
#### Option 1: Using `call` method with Ruby2html
|
151
|
+
|
152
|
+
File: `app/components/greeting_component.rb`
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# frozen_string_literal: true
|
156
|
+
|
157
|
+
class GreetingComponent < ApplicationComponent
|
158
|
+
def initialize(name)
|
159
|
+
@name = name
|
160
|
+
end
|
161
|
+
|
162
|
+
def call
|
163
|
+
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
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
#### Option 2: Using traditional ERB template
|
176
|
+
|
177
|
+
File: `app/components/farewell_component.rb`
|
178
|
+
|
179
|
+
```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
|
+
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
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
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
|
208
|
+
|
209
|
+
File: `app/components/first_component.rb`
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
# frozen_string_literal: true
|
213
|
+
|
214
|
+
class FirstComponent < ApplicationComponent
|
215
|
+
def initialize
|
216
|
+
@item = 'Hello, World!'
|
217
|
+
end
|
218
|
+
|
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
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
```
|
234
|
+
|
235
|
+
File: `app/components/second_component.rb`
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
# frozen_string_literal: true
|
239
|
+
|
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
|
249
|
+
end
|
250
|
+
```
|
251
|
+
|
252
|
+
## Without Rails
|
253
|
+
```ruby
|
254
|
+
renderer = Ruby2html::Render.new(nil) do # context by default is nil, you can use self or any other object
|
255
|
+
html do
|
256
|
+
head do
|
257
|
+
title 'Ruby2html Example'
|
258
|
+
end
|
259
|
+
body do
|
260
|
+
h1 'Hello, World!'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
puts renderer.render # => "<html><head><title>Ruby2html Example</title></head><body><h1>Hello, World!</h1></body></html>"
|
266
|
+
```
|
267
|
+
|
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
|
273
|
+
|
274
|
+
File: `app/views/your_mixed_view.html.erb`
|
275
|
+
|
276
|
+
```erb
|
277
|
+
<h1>Welcome to our gradually evolving page!</h1>
|
278
|
+
|
279
|
+
<%= render partial: 'legacy_erb_partial' %>
|
280
|
+
|
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
|
+
%>
|
289
|
+
|
290
|
+
<%= render ModernComponent.new %>
|
291
|
+
|
292
|
+
<footer>
|
293
|
+
<!-- More legacy ERB code -->
|
294
|
+
</footer>
|
295
|
+
```
|
296
|
+
|
297
|
+
In this example, you can see how Ruby2html seamlessly integrates with existing ERB code. This approach allows you to:
|
298
|
+
|
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
|
303
|
+
|
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. 🌈
|
305
|
+
|
306
|
+
## 🛠 Development
|
307
|
+
|
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.
|
309
|
+
|
310
|
+
## 🤝 Contributing
|
311
|
+
|
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).
|
313
|
+
|
314
|
+
## 📜 License
|
315
|
+
|
316
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
317
|
+
|
318
|
+
## 🌈 Code of Conduct
|
319
|
+
|
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).
|
321
|
+
|
322
|
+
## 🌟 Features
|
323
|
+
|
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 💪
|
332
|
+
|
333
|
+
Start writing your views in Ruby today and experience the magic of Ruby2html! ✨🔮
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# ext/ruby2html/extconf.rb
|
4
|
+
require 'mkmf'
|
5
|
+
|
6
|
+
# Add optimization flags
|
7
|
+
$CFLAGS << ' -O3 -Wall -Wextra'
|
8
|
+
|
9
|
+
# Check for required headers
|
10
|
+
have_header('ruby.h')
|
11
|
+
have_header('ruby/encoding.h')
|
12
|
+
|
13
|
+
extension_name = 'ruby2html/ruby2html'
|
14
|
+
dir_config(extension_name)
|
15
|
+
|
16
|
+
create_makefile(extension_name)
|
@@ -0,0 +1,108 @@
|
|
1
|
+
// ext/ruby2html/ruby2html.c
|
2
|
+
#include <ruby.h>
|
3
|
+
#include <ruby/encoding.h>
|
4
|
+
#include <ctype.h>
|
5
|
+
|
6
|
+
static VALUE rb_mRuby2html;
|
7
|
+
static VALUE rb_cRenderer;
|
8
|
+
|
9
|
+
// Fast HTML escaping
|
10
|
+
static VALUE fast_escape_html(VALUE self, VALUE str) {
|
11
|
+
if (NIL_P(str)) return Qnil;
|
12
|
+
|
13
|
+
str = rb_String(str);
|
14
|
+
long len = RSTRING_LEN(str);
|
15
|
+
const char *ptr = RSTRING_PTR(str);
|
16
|
+
|
17
|
+
// First pass: calculate required buffer size
|
18
|
+
long new_len = len;
|
19
|
+
for (long i = 0; i < len; i++) {
|
20
|
+
switch (ptr[i]) {
|
21
|
+
case '&': new_len += 4; break; // &
|
22
|
+
case '<': new_len += 3; break; // <
|
23
|
+
case '>': new_len += 3; break; // >
|
24
|
+
case '"': new_len += 5; break; // "
|
25
|
+
case '\'': new_len += 5; break; // '
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
if (new_len == len) return str;
|
30
|
+
|
31
|
+
VALUE result = rb_str_new(NULL, new_len);
|
32
|
+
char *out = RSTRING_PTR(result);
|
33
|
+
long pos = 0;
|
34
|
+
|
35
|
+
// Second pass: actual escaping
|
36
|
+
for (long i = 0; i < len; i++) {
|
37
|
+
switch (ptr[i]) {
|
38
|
+
case '&':
|
39
|
+
memcpy(out + pos, "&", 5);
|
40
|
+
pos += 5;
|
41
|
+
break;
|
42
|
+
case '<':
|
43
|
+
memcpy(out + pos, "<", 4);
|
44
|
+
pos += 4;
|
45
|
+
break;
|
46
|
+
case '>':
|
47
|
+
memcpy(out + pos, ">", 4);
|
48
|
+
pos += 4;
|
49
|
+
break;
|
50
|
+
case '"':
|
51
|
+
memcpy(out + pos, """, 6);
|
52
|
+
pos += 6;
|
53
|
+
break;
|
54
|
+
case '\'':
|
55
|
+
memcpy(out + pos, "'", 5);
|
56
|
+
pos += 5;
|
57
|
+
break;
|
58
|
+
default:
|
59
|
+
out[pos++] = ptr[i];
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
rb_str_set_len(result, pos);
|
64
|
+
rb_enc_associate(result, rb_enc_get(str));
|
65
|
+
return result;
|
66
|
+
}
|
67
|
+
|
68
|
+
// Fast attribute string builder
|
69
|
+
static VALUE fast_attributes_to_s(VALUE self, VALUE hash) {
|
70
|
+
if (NIL_P(hash) || RHASH_EMPTY_P(hash)) return rb_str_new2("");
|
71
|
+
|
72
|
+
VALUE result = rb_str_buf_new(64); // Pre-allocate with reasonable size
|
73
|
+
VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
|
74
|
+
long len = RARRAY_LEN(keys);
|
75
|
+
|
76
|
+
for (long i = 0; i < len; i++) {
|
77
|
+
VALUE key = rb_ary_entry(keys, i);
|
78
|
+
VALUE value = rb_hash_aref(hash, key);
|
79
|
+
|
80
|
+
if (!NIL_P(value)) {
|
81
|
+
rb_str_cat2(result, " ");
|
82
|
+
rb_str_append(result, rb_String(key));
|
83
|
+
rb_str_cat2(result, "=\"");
|
84
|
+
rb_str_append(result, fast_escape_html(self, rb_String(value)));
|
85
|
+
rb_str_cat2(result, "\"");
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
return result;
|
90
|
+
}
|
91
|
+
|
92
|
+
// Fast string buffer concatenation
|
93
|
+
static VALUE fast_buffer_append(VALUE self, VALUE buffer, VALUE str) {
|
94
|
+
if (!NIL_P(str)) {
|
95
|
+
rb_funcall(buffer, rb_intern("<<"), 1, rb_String(str));
|
96
|
+
}
|
97
|
+
return Qnil;
|
98
|
+
}
|
99
|
+
|
100
|
+
void Init_ruby2html(void) {
|
101
|
+
rb_mRuby2html = rb_define_module("Ruby2html");
|
102
|
+
|
103
|
+
rb_cRenderer = rb_define_class_under(rb_mRuby2html, "Render", rb_cObject);
|
104
|
+
|
105
|
+
rb_define_method(rb_cRenderer, "fast_escape_html", fast_escape_html, 1);
|
106
|
+
rb_define_method(rb_cRenderer, "fast_attributes_to_s", fast_attributes_to_s, 1);
|
107
|
+
rb_define_method(rb_cRenderer, "fast_buffer_append", fast_buffer_append, 2);
|
108
|
+
}
|
data/lib/gem/ruby2html/render.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
begin
|
4
|
+
require 'ruby2html/ruby2html'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'ruby2html not installed'
|
7
|
+
end
|
8
|
+
|
3
9
|
module Ruby2html
|
4
10
|
class Render
|
5
11
|
HTML5_TAGS = %w[
|
@@ -13,16 +19,58 @@ module Ruby2html
|
|
13
19
|
].freeze
|
14
20
|
|
15
21
|
VOID_ELEMENTS = %w[area base br col embed hr img input link meta param source track wbr].freeze
|
16
|
-
|
17
22
|
COMMON_RAILS_METHOD_HELPERS = %w[link_to image_tag form_with button_to].freeze
|
18
23
|
|
24
|
+
# Pre-generate all HTML tag methods as a single string
|
25
|
+
METHOD_DEFINITIONS = HTML5_TAGS.map do |tag|
|
26
|
+
method_name = tag.tr('-', '_')
|
27
|
+
is_void = VOID_ELEMENTS.include?(tag)
|
28
|
+
<<-RUBY
|
29
|
+
def #{method_name}(*args, **options, &block)
|
30
|
+
content = args.first.is_a?(String) ? args.shift : nil
|
31
|
+
estimated_size = #{tag.length * 2 + 5}
|
32
|
+
estimated_size += 32 if options.any?
|
33
|
+
estimated_size += content.length if content
|
34
|
+
tag_content = String.new(capacity: estimated_size)
|
35
|
+
tag_content << '<#{tag}'
|
36
|
+
fast_buffer_append(tag_content, fast_attributes_to_s(options))
|
37
|
+
#{if is_void
|
38
|
+
'tag_content << \' />\''
|
39
|
+
else
|
40
|
+
<<-TAG_LOGIC
|
41
|
+
tag_content << '>'
|
42
|
+
if block
|
43
|
+
prev_output = @current_output
|
44
|
+
nested_content = String.new(capacity: 1024)
|
45
|
+
@current_output = nested_content
|
46
|
+
block_result = block.call
|
47
|
+
@current_output = prev_output
|
48
|
+
if block_result.is_a?(String)
|
49
|
+
fast_buffer_append(tag_content, fast_escape_html(block_result))
|
50
|
+
else
|
51
|
+
fast_buffer_append(tag_content, nested_content)
|
52
|
+
end
|
53
|
+
elsif content
|
54
|
+
fast_buffer_append(tag_content, fast_escape_html(content))
|
55
|
+
end
|
56
|
+
tag_content << '</#{tag}>'
|
57
|
+
TAG_LOGIC
|
58
|
+
end}
|
59
|
+
fast_buffer_append(@current_output, tag_content)
|
60
|
+
end
|
61
|
+
RUBY
|
62
|
+
end.join("\n")
|
63
|
+
|
64
|
+
# Evaluate all method definitions at once
|
65
|
+
class_eval(METHOD_DEFINITIONS, __FILE__, __LINE__ + 1)
|
66
|
+
|
19
67
|
attr_reader :output
|
20
68
|
attr_accessor :current_output
|
21
69
|
|
22
70
|
def initialize(context = nil, &root)
|
23
71
|
@context = context
|
24
72
|
@root = root
|
25
|
-
@output =
|
73
|
+
@output = String.new(capacity: 4096)
|
26
74
|
@current_output = @output
|
27
75
|
end
|
28
76
|
|
@@ -31,73 +79,41 @@ module Ruby2html
|
|
31
79
|
return result unless annotate_rendered_view_with_filenames?
|
32
80
|
|
33
81
|
template_path = template_path.sub("#{Rails.root}/", '')
|
34
|
-
|
35
|
-
|
82
|
+
comment_start = "<!-- BEGIN #{template_path} -->"
|
83
|
+
comment_end = "<!-- END #{template_path} -->"
|
84
|
+
|
85
|
+
final_result = String.new(capacity: result.length + comment_start.length + comment_end.length)
|
86
|
+
final_result << comment_start
|
87
|
+
final_result << result
|
88
|
+
final_result << comment_end
|
89
|
+
final_result.html_safe
|
36
90
|
end if defined?(ActionView)
|
37
91
|
|
38
92
|
def render(*args, **options, &block)
|
39
93
|
set_instance_variables
|
40
94
|
|
41
|
-
return plain @context.render(*args, **options, &block)
|
95
|
+
return plain @context.render(*args, **options, &block) if !args.empty? || !options.empty? || block_given?
|
42
96
|
|
43
97
|
instance_exec(&@root)
|
44
|
-
result = @output
|
45
|
-
|
98
|
+
result = @output
|
46
99
|
result = ActiveSupport::SafeBuffer.new(result) if defined?(ActiveSupport)
|
47
|
-
|
48
100
|
result
|
49
101
|
end
|
50
102
|
|
51
|
-
HTML5_TAGS.each do |tag|
|
52
|
-
define_method(tag.tr('-', '_')) do |*args, **options, &block|
|
53
|
-
html!(tag, *args, **options, &block)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
103
|
def respond_to?(method_name, include_private = false)
|
58
|
-
HTML5_TAGS.include?(method_name) || super
|
59
|
-
end
|
60
|
-
|
61
|
-
def html!(name, *args, **options)
|
62
|
-
content = args.first.is_a?(String) ? args.shift : nil
|
63
|
-
attributes = options
|
64
|
-
|
65
|
-
tag_content = StringIO.new
|
66
|
-
tag_content << "<#{name}"
|
67
|
-
tag_content << attributes_to_s(attributes)
|
68
|
-
|
69
|
-
if VOID_ELEMENTS.include?(name)
|
70
|
-
tag_content << ' />'
|
71
|
-
else
|
72
|
-
tag_content << '>'
|
73
|
-
|
74
|
-
if block_given?
|
75
|
-
prev_output = @current_output
|
76
|
-
nested_content = StringIO.new
|
77
|
-
@current_output = nested_content
|
78
|
-
block_result = yield
|
79
|
-
@current_output = prev_output
|
80
|
-
tag_content << (block_result.is_a?(String) ? escape_html(block_result) : nested_content.string)
|
81
|
-
elsif content
|
82
|
-
tag_content << escape_html(content)
|
83
|
-
end
|
84
|
-
|
85
|
-
tag_content << "</#{name}>"
|
86
|
-
end
|
87
|
-
|
88
|
-
@current_output << tag_content.string
|
104
|
+
HTML5_TAGS.include?(method_name.to_s.tr('_', '-')) || super
|
89
105
|
end
|
90
106
|
|
91
107
|
def plain(text)
|
92
108
|
if defined?(ActiveSupport) && (text.is_a?(ActiveSupport::SafeBuffer) || text.html_safe?)
|
93
|
-
@current_output
|
109
|
+
fast_buffer_append(@current_output, text)
|
94
110
|
else
|
95
|
-
@current_output
|
111
|
+
fast_buffer_append(@current_output, fast_escape_html(text.to_s))
|
96
112
|
end
|
97
113
|
end
|
98
114
|
|
99
115
|
def component(component_output)
|
100
|
-
@current_output
|
116
|
+
fast_buffer_append(@current_output, component_output)
|
101
117
|
end
|
102
118
|
|
103
119
|
private
|
@@ -120,18 +136,8 @@ module Ruby2html
|
|
120
136
|
end
|
121
137
|
end if defined?(ActionView)
|
122
138
|
|
123
|
-
def attributes_to_s(attributes)
|
124
|
-
return '' if attributes.empty?
|
125
|
-
|
126
|
-
result = StringIO.new
|
127
|
-
attributes.compact.each do |k, v|
|
128
|
-
result << " #{k}=\"#{escape_html(v)}\""
|
129
|
-
end
|
130
|
-
result.string
|
131
|
-
end
|
132
|
-
|
133
139
|
def escape_html(text)
|
134
|
-
|
140
|
+
fast_escape_html(text.to_s)
|
135
141
|
end
|
136
142
|
|
137
143
|
def annotate_rendered_view_with_filenames?
|
Binary file
|
data/ruby2html.gemspec
CHANGED
@@ -8,13 +8,13 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ['sebi']
|
9
9
|
spec.email = ['gore.sebyx@yahoo.com']
|
10
10
|
|
11
|
-
spec.summary = 'Transform Ruby code into beautiful, structured HTML'
|
11
|
+
spec.summary = 'Transform Ruby code into beautiful, structured HTML with C-optimized performance'
|
12
12
|
spec.description = 'Ruby2HTML empowers developers to write view logic in pure Ruby, ' \
|
13
13
|
'seamlessly converting it into clean, well-formatted HTML. ' \
|
14
14
|
'Enhance your templating workflow, improve readability, and ' \
|
15
15
|
'leverage the full power of Ruby in your views. ' \
|
16
16
|
'Features include Rails integration, custom component support, ' \
|
17
|
-
'
|
17
|
+
'automatic HTML beautification, and C-optimized rendering performance.'
|
18
18
|
spec.homepage = 'https://github.com/sebyx07/ruby2html'
|
19
19
|
spec.license = 'MIT'
|
20
20
|
spec.required_ruby_version = '>= 3.0.0'
|
@@ -22,12 +22,22 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.metadata['homepage_uri'] = spec.homepage
|
23
23
|
spec.metadata['source_code_uri'] = spec.homepage
|
24
24
|
|
25
|
-
#
|
26
|
-
|
27
|
-
gemspec = File.basename(__FILE__)
|
25
|
+
# Include C extension
|
26
|
+
spec.extensions = ['ext/ruby2html/extconf.rb']
|
28
27
|
|
29
|
-
|
30
|
-
spec.
|
28
|
+
# Include both lib and ext directories
|
29
|
+
spec.files = Dir.glob('{lib,ext}/{**/*,*}') +
|
30
|
+
['README.md', File.basename(__FILE__)]
|
31
31
|
|
32
|
+
# Set require paths for both the gem and extension
|
33
|
+
spec.require_paths = %w[lib/gem lib]
|
34
|
+
|
35
|
+
# Runtime dependencies
|
32
36
|
spec.add_dependency 'htmlbeautifier', '>= 1.4'
|
37
|
+
|
38
|
+
# Development dependencies
|
39
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
40
|
+
spec.add_development_dependency 'rake-compiler', '~> 1.2'
|
41
|
+
spec.add_development_dependency 'minitest', '~> 5.14'
|
42
|
+
spec.add_development_dependency 'benchmark-ips', '~> 2.10'
|
33
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby2html
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sebi
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-09 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: htmlbeautifier
|
@@ -24,16 +23,77 @@ dependencies:
|
|
24
23
|
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '1.4'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '13.0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '13.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rake-compiler
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.2'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.2'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: minitest
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '5.14'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.14'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: benchmark-ips
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.10'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '2.10'
|
27
82
|
description: Ruby2HTML empowers developers to write view logic in pure Ruby, seamlessly
|
28
83
|
converting it into clean, well-formatted HTML. Enhance your templating workflow,
|
29
84
|
improve readability, and leverage the full power of Ruby in your views. Features
|
30
|
-
include Rails integration, custom component support,
|
85
|
+
include Rails integration, custom component support, automatic HTML beautification,
|
86
|
+
and C-optimized rendering performance.
|
31
87
|
email:
|
32
88
|
- gore.sebyx@yahoo.com
|
33
89
|
executables: []
|
34
|
-
extensions:
|
90
|
+
extensions:
|
91
|
+
- ext/ruby2html/extconf.rb
|
35
92
|
extra_rdoc_files: []
|
36
93
|
files:
|
94
|
+
- README.md
|
95
|
+
- ext/ruby2html/extconf.rb
|
96
|
+
- ext/ruby2html/ruby2html.c
|
37
97
|
- lib/gem/ruby2html.rb
|
38
98
|
- lib/gem/ruby2html/component_helper.rb
|
39
99
|
- lib/gem/ruby2html/html_beautifier_middleware.rb
|
@@ -46,6 +106,7 @@ files:
|
|
46
106
|
- lib/gem/ruby2html/railtie.rb
|
47
107
|
- lib/gem/ruby2html/render.rb
|
48
108
|
- lib/gem/ruby2html/version.rb
|
109
|
+
- lib/ruby2html/ruby2html.so
|
49
110
|
- ruby2html.gemspec
|
50
111
|
homepage: https://github.com/sebyx07/ruby2html
|
51
112
|
licenses:
|
@@ -53,10 +114,10 @@ licenses:
|
|
53
114
|
metadata:
|
54
115
|
homepage_uri: https://github.com/sebyx07/ruby2html
|
55
116
|
source_code_uri: https://github.com/sebyx07/ruby2html
|
56
|
-
post_install_message:
|
57
117
|
rdoc_options: []
|
58
118
|
require_paths:
|
59
119
|
- lib/gem
|
120
|
+
- lib
|
60
121
|
required_ruby_version: !ruby/object:Gem::Requirement
|
61
122
|
requirements:
|
62
123
|
- - ">="
|
@@ -68,8 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
129
|
- !ruby/object:Gem::Version
|
69
130
|
version: '0'
|
70
131
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
72
|
-
signing_key:
|
132
|
+
rubygems_version: 3.6.3
|
73
133
|
specification_version: 4
|
74
|
-
summary: Transform Ruby code into beautiful, structured HTML
|
134
|
+
summary: Transform Ruby code into beautiful, structured HTML with C-optimized performance
|
75
135
|
test_files: []
|