ruby2html 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +63 -14
- data/app/controllers/benchmark_controller.rb +48 -0
- data/app/controllers/home_controller.rb +4 -0
- data/app/views/benchmark/normal_html.html.erb +31 -0
- data/app/views/benchmark/ruby_2html.html.erb +35 -0
- data/app/views/home/index.html.erb +2 -1
- data/config/routes.rb +5 -0
- data/lib/gem/ruby2html/render.rb +10 -9
- data/lib/gem/ruby2html/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acd9834eee45bda437a281566e66300e5e5645dddb45850d571d506c27a94501
|
4
|
+
data.tar.gz: ebe9a74c98f952e049335357fda1e9268f7126fbb090cbf7bf4e8b4e1fbedc1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4dc38bb961568de810742b8e8c2a8c9231abcbc0244bf0334350b3949eef3e1d81d2f907ab2565bc1acc6b0b6751ccabdc2c04b9855996e72e0ef0c117eaf8fa
|
7
|
+
data.tar.gz: 5e200ab5fd3f5a0941c7d7a32e5729e5a9f249d01158b1906a2797d0b0e8309bf403e0aaf5bd5a8416ef9cf42a8d56c0150bc2a9d5f0ca63bfe987ba8c777dca
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ruby2html 🔮✨
|
2
2
|
|
3
|
-
Transform your
|
3
|
+
Transform your view logic into elegant, semantic HTML with the power of pure Ruby! 🚀✨
|
4
4
|
|
5
5
|
## 🌟 What is Ruby2html?
|
6
6
|
|
@@ -39,6 +39,30 @@ end
|
|
39
39
|
|
40
40
|
### In your views
|
41
41
|
|
42
|
+
File: `app/views/your_view.html.rb`
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
div class: 'container' do
|
46
|
+
h1 'Welcome to Ruby2html! 🎉', class: 'main-title', 'data-controller': 'welcome'
|
47
|
+
link_to 'Home Sweet Home 🏠', root_path, class: 'btn btn-primary', 'data-turbo': false
|
48
|
+
|
49
|
+
@items.each do |item|
|
50
|
+
h2 class: 'item-title', id: "item-#{item[:id]}" do
|
51
|
+
item[:title]
|
52
|
+
end
|
53
|
+
p class: 'item-description' do
|
54
|
+
item[:description]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
plain '<div>Inline html</div>'.html_safe
|
60
|
+
|
61
|
+
render partial: 'shared/navbar'
|
62
|
+
```
|
63
|
+
|
64
|
+
#### Or use your current .erb views
|
65
|
+
|
42
66
|
File: `app/views/your_view.html.erb`
|
43
67
|
|
44
68
|
Replace your ERB with beautiful Ruby code:
|
@@ -46,27 +70,47 @@ Replace your ERB with beautiful Ruby code:
|
|
46
70
|
```erb
|
47
71
|
<%=
|
48
72
|
html(self) do
|
49
|
-
h1 class: 'main-title', 'data-controller': 'welcome'
|
50
|
-
"Welcome to Ruby2html! 🎉"
|
51
|
-
end
|
73
|
+
h1 "Welcome to Ruby2html! 🎉", class: 'main-title', 'data-controller': 'welcome'
|
52
74
|
div id: 'content', class: 'container' do
|
53
75
|
link_to 'Home Sweet Home 🏠', root_path, class: 'btn btn-primary', 'data-turbo': false
|
54
76
|
end
|
55
77
|
|
56
78
|
@items.each do |item|
|
57
79
|
h2 class: 'item-title', id: "item-#{item[:id]}" do
|
58
|
-
|
80
|
+
item[:title]
|
59
81
|
end
|
60
82
|
p class: 'item-description' do
|
61
|
-
|
83
|
+
item[:description]
|
62
84
|
end
|
63
85
|
end
|
64
86
|
|
87
|
+
plain "<div>Inline html</div>".html_safe
|
88
|
+
|
65
89
|
render partial: 'shared/navbar'
|
66
90
|
end
|
67
91
|
%>
|
68
92
|
```
|
69
93
|
|
94
|
+
### Benchmark
|
95
|
+
|
96
|
+
```bash
|
97
|
+
ruby 3.3.3 (2024-06-12 revision f1c7b6f435) +YJIT [x86_64-linux]
|
98
|
+
Warming up --------------------------------------
|
99
|
+
GET /benchmark/html (ERB)
|
100
|
+
1.000 i/100ms
|
101
|
+
GET /benchmark/ruby (Ruby2html)
|
102
|
+
1.000 i/100ms
|
103
|
+
Calculating -------------------------------------
|
104
|
+
GET /benchmark/html (ERB)
|
105
|
+
20.163 (±19.8%) i/s - 95.000 in 5.062853s
|
106
|
+
GET /benchmark/ruby (Ruby2html)
|
107
|
+
19.362 (±15.5%) i/s - 92.000 in 5.006433s
|
108
|
+
|
109
|
+
Comparison:
|
110
|
+
GET /benchmark/html (ERB): 20.2 i/s
|
111
|
+
GET /benchmark/ruby (Ruby2html): 19.4 i/s - same-ish: difference falls within error
|
112
|
+
```
|
113
|
+
|
70
114
|
### With ViewComponents
|
71
115
|
|
72
116
|
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.
|
@@ -96,10 +140,10 @@ class GreetingComponent < ApplicationComponent
|
|
96
140
|
def call
|
97
141
|
html(self) do
|
98
142
|
h1 class: 'greeting', 'data-user': @name do
|
99
|
-
|
143
|
+
"Hello, #{@name}! 👋"
|
100
144
|
end
|
101
145
|
p class: 'welcome-message' do
|
102
|
-
|
146
|
+
'Welcome to the wonderful world of Ruby2html!'
|
103
147
|
end
|
104
148
|
end
|
105
149
|
end
|
@@ -155,13 +199,13 @@ class FirstComponent < ApplicationComponent
|
|
155
199
|
def call
|
156
200
|
html(self) do
|
157
201
|
h1 id: 'first-component-title' do
|
158
|
-
|
202
|
+
'first component'
|
159
203
|
end
|
160
204
|
div class: 'content-wrapper' do
|
161
205
|
h2 'A subheading'
|
162
206
|
end
|
163
207
|
p class: 'greeting-text', 'data-testid': 'greeting' do
|
164
|
-
|
208
|
+
@item
|
165
209
|
end
|
166
210
|
end
|
167
211
|
end
|
@@ -177,7 +221,7 @@ class SecondComponent < ApplicationComponent
|
|
177
221
|
def call
|
178
222
|
html(self) do
|
179
223
|
h1 class: 'my-class', id: 'second-component-title', 'data-controller': 'second' do
|
180
|
-
|
224
|
+
'second component'
|
181
225
|
end
|
182
226
|
link_to 'Home', root_path, class: 'nav-link', 'data-turbo-frame': false
|
183
227
|
end
|
@@ -187,13 +231,18 @@ end
|
|
187
231
|
|
188
232
|
## Without Rails
|
189
233
|
```ruby
|
190
|
-
|
234
|
+
renderer = Ruby2html::Render.new(nil) do # nil is the context, you can use self or any other object
|
191
235
|
html do
|
192
|
-
|
236
|
+
head do
|
237
|
+
title 'Ruby2html Example'
|
238
|
+
end
|
239
|
+
body do
|
240
|
+
h1 'Hello, World!'
|
241
|
+
end
|
193
242
|
end
|
194
243
|
end
|
195
244
|
|
196
|
-
puts
|
245
|
+
puts renderer.render # => "<html><head><title>Ruby2html Example</title></head><body><h1>Hello, World!</h1></body></html>"
|
197
246
|
```
|
198
247
|
|
199
248
|
## 🐢 Gradual Adoption
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class BenchmarkController < ApplicationController
|
4
|
+
def normal_html
|
5
|
+
@complex_data = generate_complex_data
|
6
|
+
end
|
7
|
+
|
8
|
+
def ruby_2html
|
9
|
+
@complex_data = generate_complex_data
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def generate_complex_data
|
14
|
+
{
|
15
|
+
users: 50.times.map do |i|
|
16
|
+
{
|
17
|
+
id: i + 1,
|
18
|
+
name: Faker::Name.name,
|
19
|
+
email: Faker::Internet.email,
|
20
|
+
address: {
|
21
|
+
street: Faker::Address.street_address,
|
22
|
+
city: Faker::Address.city,
|
23
|
+
country: Faker::Address.country
|
24
|
+
},
|
25
|
+
orders: rand(1..5).times.map do
|
26
|
+
{
|
27
|
+
id: Faker::Alphanumeric.alphanumeric(number: 10),
|
28
|
+
total: Faker::Commerce.price(range: 10..1000.0),
|
29
|
+
items: rand(1..10).times.map do
|
30
|
+
{
|
31
|
+
name: Faker::Commerce.product_name,
|
32
|
+
price: Faker::Commerce.price(range: 5..500.0),
|
33
|
+
quantity: rand(1..5)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
}
|
39
|
+
end,
|
40
|
+
stats: {
|
41
|
+
total_users: 50,
|
42
|
+
average_orders_per_user: rand(1.0..5.0).round(2),
|
43
|
+
most_expensive_item: Faker::Commerce.product_name,
|
44
|
+
most_popular_country: Faker::Address.country
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<h1>Benchmark: Normal HTML (ERB)</h1>
|
2
|
+
|
3
|
+
<h2>User Statistics</h2>
|
4
|
+
<ul>
|
5
|
+
<li>Total Users: <%= @complex_data[:stats][:total_users] %></li>
|
6
|
+
<li>Average Orders per User: <%= @complex_data[:stats][:average_orders_per_user] %></li>
|
7
|
+
<li>Most Expensive Item: <%= @complex_data[:stats][:most_expensive_item] %></li>
|
8
|
+
<li>Most Popular Country: <%= @complex_data[:stats][:most_popular_country] %></li>
|
9
|
+
</ul>
|
10
|
+
|
11
|
+
<h2>User List</h2>
|
12
|
+
<% @complex_data[:users].each do |user| %>
|
13
|
+
<div class="user-card">
|
14
|
+
<h3><%= user[:name] %></h3>
|
15
|
+
<p>Email: <%= user[:email] %></p>
|
16
|
+
<p>Address: <%= "#{user[:address][:street]}, #{user[:address][:city]}, #{user[:address][:country]}" %></p>
|
17
|
+
|
18
|
+
<h4>Orders</h4>
|
19
|
+
<% user[:orders].each do |order| %>
|
20
|
+
<div class="order">
|
21
|
+
<p>Order ID: <%= order[:id] %></p>
|
22
|
+
<p>Total: $<%= order[:total] %></p>
|
23
|
+
<ul>
|
24
|
+
<% order[:items].each do |item| %>
|
25
|
+
<li><%= item[:name] %> - $<%= item[:price] %> (Quantity: <%= item[:quantity] %>)</li>
|
26
|
+
<% end %>
|
27
|
+
</ul>
|
28
|
+
</div>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<%=
|
2
|
+
html(self) do
|
3
|
+
h1 'Benchmark: Ruby2html'
|
4
|
+
|
5
|
+
h2 'User Statistics'
|
6
|
+
ul do
|
7
|
+
li { plain "Total Users: #{@complex_data[:stats][:total_users]}" }
|
8
|
+
li { plain "Average Orders per User: #{@complex_data[:stats][:average_orders_per_user]}" }
|
9
|
+
li { plain "Most Expensive Item: #{@complex_data[:stats][:most_expensive_item]}" }
|
10
|
+
li { plain "Most Popular Country: #{@complex_data[:stats][:most_popular_country]}" }
|
11
|
+
end
|
12
|
+
|
13
|
+
h2 'User List'
|
14
|
+
@complex_data[:users].each do |user|
|
15
|
+
div class: 'user-card' do
|
16
|
+
h3 user[:name]
|
17
|
+
p { plain "Email: #{user[:email]}" }
|
18
|
+
p { plain "Address: #{user[:address][:street]}, #{user[:address][:city]}, #{user[:address][:country]}" }
|
19
|
+
|
20
|
+
h4 'Orders'
|
21
|
+
user[:orders].each do |order|
|
22
|
+
div class: 'order' do
|
23
|
+
p { plain "Order ID: #{order[:id]}" }
|
24
|
+
p { plain "Total: $#{order[:total]}" }
|
25
|
+
ul do
|
26
|
+
order[:items].each do |item|
|
27
|
+
li { plain "#{item[:name]} - $#{item[:price]} (Quantity: #{item[:quantity]})" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
%>
|
data/config/routes.rb
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
Rails.application.routes.draw do
|
4
4
|
root to: 'home#index'
|
5
|
+
get '/benchmark/html', to: 'benchmark#normal_html'
|
6
|
+
get '/benchmark/ruby', to: 'benchmark#ruby_2html'
|
7
|
+
|
8
|
+
get '/rb_files', to: 'home#rb_files'
|
9
|
+
|
5
10
|
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
6
11
|
|
7
12
|
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
|
data/lib/gem/ruby2html/render.rb
CHANGED
@@ -39,14 +39,15 @@ module Ruby2html
|
|
39
39
|
end
|
40
40
|
|
41
41
|
HTML5_TAGS.each do |tag|
|
42
|
-
define_method(tag) do |*args, &block|
|
43
|
-
html!(tag, *args, &block)
|
42
|
+
define_method(tag) do |*args, **options, &block|
|
43
|
+
html!(tag, *args, **options, &block)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def html!(name, *args, &block)
|
48
|
-
|
49
|
-
|
47
|
+
def html!(name, *args, **options, &block)
|
48
|
+
content = args.first.is_a?(String) ? args.shift : nil
|
49
|
+
attributes = options
|
50
|
+
|
50
51
|
tag_content = StringIO.new
|
51
52
|
tag_content << '<'
|
52
53
|
tag_content << name
|
@@ -61,10 +62,10 @@ module Ruby2html
|
|
61
62
|
prev_output = @current_output
|
62
63
|
nested_content = StringIO.new
|
63
64
|
@current_output = nested_content
|
64
|
-
|
65
|
+
block_result = yield
|
65
66
|
@current_output = prev_output
|
66
|
-
tag_content << nested_content.string
|
67
|
-
|
67
|
+
tag_content << (block_result.is_a?(String) ? escape_html(block_result) : nested_content.string)
|
68
|
+
elsif content
|
68
69
|
tag_content << escape_html(content)
|
69
70
|
end
|
70
71
|
|
@@ -105,7 +106,7 @@ module Ruby2html
|
|
105
106
|
define_method(method) do |*args, &block|
|
106
107
|
plain @context.send(method, *args, &block)
|
107
108
|
end
|
108
|
-
end
|
109
|
+
end if defined?(ActionView)
|
109
110
|
|
110
111
|
def attributes_to_s(attributes)
|
111
112
|
return '' if attributes.empty?
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby2html
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sebi
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- app/components/first_component.rb
|
38
38
|
- app/components/second_component.rb
|
39
39
|
- app/controllers/application_controller.rb
|
40
|
+
- app/controllers/benchmark_controller.rb
|
40
41
|
- app/controllers/concerns/.keep
|
41
42
|
- app/controllers/home_controller.rb
|
42
43
|
- app/helpers/application_helper.rb
|
@@ -44,6 +45,8 @@ files:
|
|
44
45
|
- app/mailers/application_mailer.rb
|
45
46
|
- app/models/application_record.rb
|
46
47
|
- app/models/concerns/.keep
|
48
|
+
- app/views/benchmark/normal_html.html.erb
|
49
|
+
- app/views/benchmark/ruby_2html.html.erb
|
47
50
|
- app/views/home/index.html.erb
|
48
51
|
- app/views/layouts/application.html.erb
|
49
52
|
- app/views/layouts/mailer.html.erb
|