ruby2html 1.0.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57887705c24091d9cf76d5f1b7e41da4167077c9c61b5a408756af3804e0df1b
4
- data.tar.gz: e27fa7060204b4da1c95998451a186cf84492e4a896440baa9ce78eceaaf6f9c
3
+ metadata.gz: 8e43d3eb304652b48e727067ce4361d654936cc0258589efd82adf55136a687d
4
+ data.tar.gz: 25900c6ed9f1a441f3f5a2b6d457e13caa5bfca3db99a7573daa89af92351544
5
5
  SHA512:
6
- metadata.gz: 3267b43294cac4d37620a16076fbb97eaaf7d6e8c639b1b83e7b7657e597739617f5ee051c83a2d2394758a9ab19f0d9dd66243ad36eb2c4057308fec64d9ec2
7
- data.tar.gz: 583b0c34f859915a57de3b7c1518ed7df16225eef381203c53b73fdf8664401567f564a5dd5c42b870b9b75d3c4f37f119dc7fae51b7707907ded372d97b52c7
6
+ metadata.gz: 214487bc59cf8ebc35698f7c16ffca8add3d59dfb52142ef1273e5c1dc67d8351fad286f1b417bfd6e9913ae3c06be8e3bba06c81b9670a5a5561cb47e1ba683
7
+ data.tar.gz: 430e623e958a739a33cdfd16d9d0039ff3354f1461cfe8d7db0a2418ce6fbff4a0352e37a67227e36058088c9c26fac0ffd2aaad50c3842a1ea71a536133610f
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ruby2html 🔮✨
2
2
 
3
- Transform your Ruby code into beautiful, structured HTML with ease! 🚀
3
+ Transform your view logic into elegant, semantic HTML with the power of pure Ruby! 🚀✨
4
4
 
5
5
  ## 🌟 What is Ruby2html?
6
6
 
@@ -25,6 +25,32 @@ Or install it yourself as:
25
25
 
26
26
  ## 🎨 Usage
27
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
+ @items.each do |item|
38
+ h2 class: 'item-title', id: "item-#{item[:id]}" do
39
+ item[:title]
40
+ end
41
+ p class: 'item-description' do
42
+ item[: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
+ #### Or use your current .erb views
53
+
28
54
  ### In your ApplicationController
29
55
 
30
56
  File: `app/controllers/application_controller.rb`
@@ -37,8 +63,6 @@ class ApplicationController < ActionController::Base
37
63
  end
38
64
  ```
39
65
 
40
- ### In your views
41
-
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' do
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
- plain item[:title]
80
+ item[:title]
59
81
  end
60
82
  p class: 'item-description' do
61
- plain item[:description]
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
+ 2.000 i/100ms
101
+ GET /benchmark/ruby (Ruby2html)
102
+ 1.000 i/100ms
103
+ Calculating -------------------------------------
104
+ GET /benchmark/html (ERB)
105
+ 20.989 (±19.1%) i/s - 102.000 in 5.077353s
106
+ GET /benchmark/ruby (Ruby2html)
107
+ 20.438 (±19.6%) i/s - 97.000 in 5.010249s
108
+
109
+ Comparison:
110
+ GET /benchmark/html (ERB): 21.0 i/s
111
+ GET /benchmark/ruby (Ruby2html): 20.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
- plain "Hello, #{@name}! 👋"
143
+ "Hello, #{@name}! 👋"
100
144
  end
101
145
  p class: 'welcome-message' do
102
- plain 'Welcome to the wonderful world of Ruby2html!'
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
- plain 'first component'
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
- plain @item
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
- plain 'second component'
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
- html = Ruby2html::Render.new(nil) do # nil is the context, you can use self or any other object
234
+ renderer = Ruby2html::Render.new(nil) do # nil is the context, you can use self or any other object
191
235
  html do
192
- h1 'Hello, World!'
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 html.render
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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ h1 'first component'
4
+
5
+ div do
6
+ h2 'A subheading'
7
+ end
8
+
9
+ p @item
@@ -2,7 +2,7 @@
2
2
 
3
3
  class SecondComponent < ApplicationComponent
4
4
  def call
5
- html(self) do
5
+ html do
6
6
  h1 class: 'my-class' do
7
7
  plain 'Second Component'
8
8
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ h1 'Third component'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ThirdComponent < ApplicationComponent
4
+ def initialize
5
+ @item = 'Hello, World!'
6
+ end
7
+ end
@@ -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
@@ -9,4 +9,8 @@ class HomeController < ApplicationController
9
9
  }
10
10
  ]
11
11
  end
12
+
13
+ def rb_files
14
+ @value = 'value'
15
+ end
12
16
  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,33 @@
1
+ # frozen_string_literal: true
2
+
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
@@ -2,10 +2,12 @@
2
2
 
3
3
  <%= render FirstComponent.new %>
4
4
  <%= render SecondComponent.new %>
5
+ <%= render ThirdComponent.new %>
5
6
 
6
7
  <%=
7
8
  html(self) do
8
- h1(id: 4) { plain "ok" }
9
+ h1(id: 4) { "Inside" }
10
+ h2 "Inside 2", id: '44'
9
11
  h1 "ok"
10
12
  h1 "ok"
11
13
  h1 "ok"
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ h1 'RbFiles'
4
+ h1 'ok'
5
+ h1 'ok'
6
+
7
+ div @value
8
+
9
+ link_to 'Home', root_url
10
+ render partial: 'shared/navbar'
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.
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Ruby2html
4
4
  module ComponentHelper
5
- def html(context, &block)
6
- Ruby2html::Render.new(context, &block).render.html_safe
5
+ def html(&block)
6
+ Ruby2html::Render.new(self, &block).render.html_safe
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruby2html
4
+ class TemplateHandler
5
+ class_attribute :default_format
6
+ self.default_format = :html
7
+
8
+ def self.call(template, source)
9
+ new.call(template, source)
10
+ end
11
+
12
+ def call(_template, source)
13
+ <<-RUBY
14
+ Ruby2html::Render.new(self) do
15
+ #{source}
16
+ end.render
17
+ RUBY
18
+ end
19
+ end
20
+
21
+ class Railtie < Rails::Railtie
22
+ initializer 'ruby2html.initializer' do
23
+ Rails.autoloaders.main.ignore(
24
+ Rails.root.join('app/views/**/*.html.rb'),
25
+ Rails.root.join('app/components/**/*.html.rb')
26
+ )
27
+ end
28
+ end
29
+ end
30
+
31
+ ActionView::Template.register_template_handler :rb, Ruby2html::TemplateHandler if defined? ActionView::Template
@@ -35,18 +35,24 @@ module Ruby2html
35
35
  return plain @context.render(*args, **options, &block)
36
36
  end
37
37
  instance_exec(&@root)
38
- @output.string
38
+ result = @output.string
39
+ if defined?(ActiveSupport)
40
+ result = ActiveSupport::SafeBuffer.new(result)
41
+ end
42
+
43
+ result
39
44
  end
40
45
 
41
46
  HTML5_TAGS.each do |tag|
42
- define_method(tag) do |*args, &block|
43
- html!(tag, *args, &block)
47
+ define_method(tag) do |*args, **options, &block|
48
+ html!(tag, *args, **options, &block)
44
49
  end
45
50
  end
46
51
 
47
- def html!(name, *args, &block)
48
- attributes = args.first.is_a?(Hash) ? args.shift : {}
49
- content = args.first.to_s
52
+ def html!(name, *args, **options, &block)
53
+ content = args.first.is_a?(String) ? args.shift : nil
54
+ attributes = options
55
+
50
56
  tag_content = StringIO.new
51
57
  tag_content << '<'
52
58
  tag_content << name
@@ -61,10 +67,10 @@ module Ruby2html
61
67
  prev_output = @current_output
62
68
  nested_content = StringIO.new
63
69
  @current_output = nested_content
64
- instance_exec(&block)
70
+ block_result = yield
65
71
  @current_output = prev_output
66
- tag_content << nested_content.string
67
- else
72
+ tag_content << (block_result.is_a?(String) ? escape_html(block_result) : nested_content.string)
73
+ elsif content
68
74
  tag_content << escape_html(content)
69
75
  end
70
76
 
@@ -105,7 +111,7 @@ module Ruby2html
105
111
  define_method(method) do |*args, &block|
106
112
  plain @context.send(method, *args, &block)
107
113
  end
108
- end
114
+ end if defined?(ActionView)
109
115
 
110
116
  def attributes_to_s(attributes)
111
117
  return '' if attributes.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ruby2html
4
- VERSION = '1.0.0'
4
+ VERSION = '1.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby2html
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sebi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-08 00:00:00.000000000 Z
11
+ date: 2024-08-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby2HTML empowers developers to write view logic in pure Ruby, seamlessly
14
14
  converting it into clean, well-formatted HTML. Enhance your templating workflow,
@@ -33,10 +33,13 @@ files:
33
33
  - app/channels/application_cable/channel.rb
34
34
  - app/channels/application_cable/connection.rb
35
35
  - app/components/application_component.rb
36
- - app/components/first_component.html.erb
36
+ - app/components/first_component.html.rb
37
37
  - app/components/first_component.rb
38
38
  - app/components/second_component.rb
39
+ - app/components/third_component.rb
40
+ - app/components/third_component/third_component.html.rb
39
41
  - app/controllers/application_controller.rb
42
+ - app/controllers/benchmark_controller.rb
40
43
  - app/controllers/concerns/.keep
41
44
  - app/controllers/home_controller.rb
42
45
  - app/helpers/application_helper.rb
@@ -44,7 +47,10 @@ files:
44
47
  - app/mailers/application_mailer.rb
45
48
  - app/models/application_record.rb
46
49
  - app/models/concerns/.keep
50
+ - app/views/benchmark/normal_html.html.erb
51
+ - app/views/benchmark/ruby_2html.html.rb
47
52
  - app/views/home/index.html.erb
53
+ - app/views/home/rb_files.html.rb
48
54
  - app/views/layouts/application.html.erb
49
55
  - app/views/layouts/mailer.html.erb
50
56
  - app/views/layouts/mailer.text.erb
@@ -76,6 +82,7 @@ files:
76
82
  - lib/gem/ruby2html.rb
77
83
  - lib/gem/ruby2html/component_helper.rb
78
84
  - lib/gem/ruby2html/rails_helper.rb
85
+ - lib/gem/ruby2html/railtie.rb
79
86
  - lib/gem/ruby2html/render.rb
80
87
  - lib/gem/ruby2html/version.rb
81
88
  - lib/tasks/.keep
@@ -1,9 +0,0 @@
1
- <%= html(self) do
2
- h1 "first component"
3
-
4
- div do
5
- h2 "A subheading"
6
- end
7
-
8
- p @item
9
- end %>