phlex 0.3.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of phlex might be problematic. Click here for more details.

Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +8 -0
  3. data/.rubocop.yml +21 -5
  4. data/Gemfile +26 -12
  5. data/Procfile.dev +3 -0
  6. data/README.md +1 -1
  7. data/Rakefile +3 -5
  8. data/SECURITY.md +1 -1
  9. data/bench.rb +7 -0
  10. data/config/sus.rb +15 -0
  11. data/docs/assets/application.css +6 -0
  12. data/docs/build.rb +17 -10
  13. data/docs/components/callout.rb +1 -1
  14. data/docs/components/code_block.rb +2 -2
  15. data/docs/components/code_span.rb +9 -0
  16. data/docs/components/example.rb +5 -5
  17. data/docs/components/heading.rb +2 -2
  18. data/docs/components/layout.rb +62 -17
  19. data/docs/components/markdown.rb +14 -15
  20. data/docs/components/nav/item.rb +33 -0
  21. data/docs/components/nav.rb +6 -0
  22. data/docs/components/tabs/tab.rb +4 -2
  23. data/docs/components/tabs.rb +1 -1
  24. data/docs/components/title.rb +2 -2
  25. data/docs/page_builder.rb +3 -0
  26. data/docs/pages/application_page.rb +1 -1
  27. data/docs/pages/helpers.rb +97 -0
  28. data/docs/pages/index.rb +6 -17
  29. data/docs/pages/library/collections.rb +83 -0
  30. data/docs/pages/rails/getting_started.rb +53 -0
  31. data/docs/pages/rails/helpers.rb +55 -0
  32. data/docs/pages/rails/layouts.rb +61 -0
  33. data/docs/pages/rails/migrating.rb +37 -0
  34. data/docs/pages/rails/rendering_views.rb +35 -0
  35. data/docs/pages/templates.rb +53 -151
  36. data/docs/pages/testing/capybara.rb +48 -0
  37. data/docs/pages/testing/getting_started.rb +44 -0
  38. data/docs/pages/testing/nokogiri.rb +83 -0
  39. data/docs/pages/testing/rails.rb +17 -0
  40. data/docs/pages/translations.rb +81 -0
  41. data/docs/pages/views.rb +87 -78
  42. data/fixtures/compiler_test_helpers.rb +19 -0
  43. data/fixtures/content.rb +60 -0
  44. data/fixtures/dummy/app/components/comment_component.html.erb +14 -0
  45. data/fixtures/dummy/app/components/comment_component.rb +8 -0
  46. data/fixtures/dummy/app/components/reaction_component.html.erb +3 -0
  47. data/fixtures/dummy/app/components/reaction_component.rb +7 -0
  48. data/fixtures/dummy/app/controllers/comments_controller.rb +4 -0
  49. data/fixtures/dummy/app/views/application_view.rb +8 -0
  50. data/fixtures/dummy/app/views/articles/form.rb +3 -1
  51. data/fixtures/dummy/app/views/card.rb +4 -2
  52. data/fixtures/dummy/app/views/comments/comment.rb +25 -0
  53. data/fixtures/dummy/app/views/comments/index.html.erb +3 -0
  54. data/fixtures/dummy/app/views/comments/reaction.rb +17 -0
  55. data/fixtures/dummy/app/views/comments/show.html.erb +3 -0
  56. data/fixtures/dummy/app/views/heading.rb +1 -1
  57. data/fixtures/layout.rb +5 -5
  58. data/fixtures/page.rb +18 -24
  59. data/fixtures/{test_helper.rb → rails_helper.rb} +3 -7
  60. data/fixtures/standard_element.rb +87 -0
  61. data/fixtures/view_helper.rb +1 -1
  62. data/fixtures/void_element.rb +31 -0
  63. data/lib/generators/phlex/collection/USAGE +8 -0
  64. data/lib/generators/phlex/collection/collection_generator.rb +13 -0
  65. data/lib/generators/phlex/collection/templates/collection.rb.erb +16 -0
  66. data/lib/generators/phlex/controller/USAGE +10 -0
  67. data/lib/generators/phlex/controller/controller_generator.rb +54 -0
  68. data/lib/generators/phlex/controller/templates/controller.rb.erb +10 -0
  69. data/lib/generators/phlex/controller/templates/view.rb.erb +14 -0
  70. data/lib/generators/phlex/layout/USAGE +8 -0
  71. data/lib/generators/phlex/layout/layout_generator.rb +13 -0
  72. data/lib/generators/phlex/layout/templates/layout.rb.erb +31 -0
  73. data/lib/generators/phlex/page/USAGE +8 -0
  74. data/lib/generators/phlex/page/page_generator.rb +13 -0
  75. data/lib/generators/phlex/page/templates/page.rb.erb +13 -0
  76. data/lib/generators/phlex/table/USAGE +8 -0
  77. data/lib/generators/phlex/table/table_generator.rb +14 -0
  78. data/lib/generators/phlex/table/templates/table.rb.erb +11 -0
  79. data/lib/generators/phlex/view/templates/view.rb.erb +7 -1
  80. data/lib/generators/phlex/view/view_generator.rb +9 -1
  81. data/lib/install/phlex.rb +10 -1
  82. data/lib/phlex/block.rb +2 -4
  83. data/lib/phlex/buffered.rb +6 -8
  84. data/lib/phlex/callable.rb +9 -0
  85. data/lib/phlex/collection.rb +33 -0
  86. data/lib/phlex/compiler/elements.rb +49 -0
  87. data/lib/phlex/compiler/generators/content.rb +103 -0
  88. data/lib/phlex/compiler/generators/element.rb +61 -0
  89. data/lib/phlex/compiler/nodes/base.rb +19 -0
  90. data/lib/phlex/compiler/nodes/call.rb +9 -0
  91. data/lib/phlex/compiler/nodes/command.rb +13 -0
  92. data/lib/phlex/compiler/nodes/fcall.rb +18 -0
  93. data/lib/phlex/compiler/nodes/method_add_block.rb +33 -0
  94. data/lib/phlex/compiler/nodes/vcall.rb +9 -0
  95. data/lib/phlex/compiler/optimizer.rb +66 -0
  96. data/lib/phlex/compiler/visitors/base.rb +15 -0
  97. data/lib/phlex/compiler/visitors/file.rb +23 -11
  98. data/lib/phlex/compiler/visitors/stable_scope.rb +28 -0
  99. data/lib/phlex/compiler/visitors/statements.rb +36 -0
  100. data/lib/phlex/compiler/visitors/view.rb +19 -0
  101. data/lib/phlex/compiler/visitors/view_method.rb +59 -0
  102. data/lib/phlex/compiler.rb +23 -3
  103. data/lib/phlex/elements.rb +57 -0
  104. data/lib/phlex/engine.rb +0 -3
  105. data/lib/phlex/helpers.rb +59 -0
  106. data/lib/phlex/html/callbacks.rb +11 -0
  107. data/lib/phlex/html.rb +209 -54
  108. data/lib/phlex/markdown.rb +76 -0
  109. data/lib/phlex/rails/form.rb +67 -0
  110. data/lib/phlex/rails/helpers.rb +118 -0
  111. data/lib/phlex/rails/layout.rb +15 -0
  112. data/lib/phlex/rails.rb +10 -0
  113. data/lib/phlex/renderable.rb +9 -3
  114. data/lib/phlex/table.rb +104 -0
  115. data/lib/phlex/testing/capybara.rb +25 -0
  116. data/lib/phlex/testing/nokogiri.rb +24 -0
  117. data/lib/phlex/testing/rails.rb +19 -0
  118. data/lib/phlex/testing/view_helper.rb +15 -0
  119. data/lib/phlex/translation.rb +23 -0
  120. data/lib/phlex/turbo/frame.rb +21 -0
  121. data/lib/phlex/turbo/stream.rb +18 -0
  122. data/lib/phlex/version.rb +1 -1
  123. data/lib/phlex.rb +22 -24
  124. metadata +112 -15
  125. data/.rspec +0 -1
  126. data/fixtures/compilation/vcall.rb +0 -38
  127. data/lib/phlex/compiler/generators/standard_element.rb +0 -30
  128. data/lib/phlex/compiler/generators/void_element.rb +0 -29
  129. data/lib/phlex/compiler/optimizers/base_optimizer.rb +0 -34
  130. data/lib/phlex/compiler/optimizers/vcall.rb +0 -29
  131. data/lib/phlex/compiler/visitors/base_visitor.rb +0 -19
  132. data/lib/phlex/compiler/visitors/component.rb +0 -28
  133. data/lib/phlex/compiler/visitors/component_method.rb +0 -28
  134. data/lib/phlex/rails/tag_helpers.rb +0 -29
  135. data/lib/phlex/view.rb +0 -223
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "minitest"
4
+
5
+ module Pages
6
+ module Testing
7
+ class Capybara < ApplicationPage
8
+ def template
9
+ render Layout.new(title: "Testing with Capybara") do
10
+ render Markdown.new(<<~MD)
11
+ # Testing with Capybara
12
+
13
+ Require `phlex/testing/capybara` and include `Phlex::Testing::Capybara::ViewHelper` to use [Capybara](http://teamcapybara.github.io/capybara/) matchers.
14
+
15
+ The `render` method will return a `Capybara::Node::Simple` and set the `page` attribute to the result.
16
+ MD
17
+
18
+ render Example.new do |e|
19
+ e.tab "test.rb", <<~RUBY
20
+ require "phlex/testing/capybara"
21
+
22
+ class TestExample < Minitest::Test
23
+ include Phlex::Testing::Capybara::ViewHelper
24
+
25
+ def test_example
26
+ render Example.new("Joel")
27
+ assert_select "h1", text: "Hello Joel"
28
+ end
29
+ end
30
+ RUBY
31
+
32
+ e.tab "hello.rb", <<~RUBY
33
+ class Hello < Phlex::HTML
34
+ def initialize(name)
35
+ @name = name
36
+ end
37
+
38
+ def template
39
+ h1 { "Hello \#{@name}" }
40
+ end
41
+ end
42
+ RUBY
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pages
4
+ module Testing
5
+ class GettingStarted < ApplicationPage
6
+ def template
7
+ render Layout.new(title: "Getting Started Testing Phlex Views") do
8
+ render Markdown.new(<<~MD)
9
+ # Getting Started
10
+
11
+ The `Phlex::Testing::ViewHelper` module defines `render` allowing you to render Phlex views directly in your tests and make assertions against the output.
12
+ MD
13
+
14
+ render Example.new do |e|
15
+ e.tab "test/test_hello.rb", <<~RUBY
16
+ require "phlex/testing/view_helper"
17
+
18
+ class TestHello < Minitest::Test
19
+ include Phlex::Testing::ViewHelper
20
+
21
+ def test_hello_output_includes_name
22
+ output = render Hello.new("Joel")
23
+ assert_equal "<h1>Hello Joel</h1>", output
24
+ end
25
+ end
26
+ RUBY
27
+
28
+ e.tab "hello.rb", <<~RUBY
29
+ class Hello < Phlex::HTML
30
+ def initialize(name)
31
+ @name = name
32
+ end
33
+
34
+ def template
35
+ h1 { "Hello \#{@name}" }
36
+ end
37
+ end
38
+ RUBY
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pages
4
+ module Testing
5
+ class Nokogiri < ApplicationPage
6
+ def template
7
+ render Layout.new(title: "Testing with Nokogiri") do
8
+ render Markdown.new(<<~MD)
9
+ # Testing with Nokogiri
10
+
11
+ Phlex includes test helpers for working with rendered views as [Nokogiri](https://nokogiri.org) documents and fragments.
12
+
13
+ Nokogiri is not a dependency of Phlex, so you’ll need to install that separately to use this helper.
14
+
15
+ ## Documents
16
+
17
+ If your view represents a whole HTML document, you can require `phlex/testing/nokogiri` and include the `Phlex::Testing::Nokogiri::DocumentHelper` module to render your view as `Nokogiri::Document` using the `render` method.
18
+ MD
19
+
20
+ render Example.new do |e|
21
+ e.tab "test.rb", <<~RUBY
22
+ require "phlex/testing/nokogiri"
23
+
24
+ class TestExample < Minitest::Test
25
+ include Phlex::Testing::Nokogiri::DocumentHelper
26
+
27
+ def test_example
28
+ output = render Example.new
29
+ assert_equal "Hello Joel", output.css("h1").text
30
+ end
31
+ end
32
+ RUBY
33
+
34
+ e.tab "hello.rb", <<~RUBY
35
+ class Hello < Phlex::HTML
36
+ def initialize(name)
37
+ @name = name
38
+ end
39
+
40
+ def template
41
+ h1 { "Hello \#{@name}" }
42
+ end
43
+ end
44
+ RUBY
45
+ end
46
+
47
+ render Markdown.new(<<~MD)
48
+ ## Fragments
49
+
50
+ If your view represents a fragment (partial), you can require `phlex/testing/nokogiri` and include the `Phlex::Testing::Nokogiri::FragmentHelper` module to render your view as `Nokogiri::Fragment` with the `render` method.
51
+ MD
52
+
53
+ render Example.new do |e|
54
+ e.tab "test.rb", <<~RUBY
55
+ require "phlex/testing/nokogiri"
56
+
57
+ class TestExample < Minitest::Test
58
+ include Phlex::Testing::Nokogiri::FragmentHelper
59
+
60
+ def test_example
61
+ output = render Example.new("Joel")
62
+ assert_equal "Hello Joel", output.css("h1").text
63
+ end
64
+ end
65
+ RUBY
66
+
67
+ e.tab "hello.rb", <<~RUBY
68
+ class Hello < Phlex::HTML
69
+ def initialize(name)
70
+ @name = name
71
+ end
72
+
73
+ def template
74
+ h1 { "Hello \#{@name}" }
75
+ end
76
+ end
77
+ RUBY
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pages
4
+ module Testing
5
+ class Rails < ApplicationPage
6
+ def template
7
+ render Layout.new(title: "Testing Phlex Views in Rails") do
8
+ render Markdown.new(<<~MD)
9
+ # Testing Phlex views in Rails
10
+
11
+ When you include `Phlex::Testing::Rails::ViewHelper`, views rendered in the test will have a view context, so they can use Rails helpers.
12
+ MD
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pages
4
+ class Translations < ApplicationPage
5
+ def initialize
6
+ I18n.backend.store_translations(
7
+ "pt-BR", {
8
+ hello: "Olá",
9
+ views: { feedback: { welcome_message: { hello: "Olá" } } }
10
+ }
11
+ )
12
+ I18n.locale = "pt-BR"
13
+ end
14
+
15
+ def template
16
+ render Layout.new(title: "Translations") do
17
+ render Markdown.new(<<~MD)
18
+ # Translations
19
+
20
+ Phlex has built-in support for translations with the **[I18n Gem](https://github.com/ruby-i18n/i18n)**.
21
+
22
+ Just include `Phlex::Translation` in your view and use the `translate` method to access a translation.
23
+ MD
24
+
25
+ render Example.new do |e|
26
+ e.tab "welcome_message.rb", <<~RUBY
27
+ class WelcomeMessage < Phlex::HTML
28
+ include Phlex::Translation
29
+
30
+ def template
31
+ h1 { translate("hello") }
32
+ end
33
+ end
34
+ RUBY
35
+
36
+ e.tab "pt-PR.yml", <<~YAML, syntax: :yaml
37
+ pt-BR:
38
+ hello: "Olá"
39
+ YAML
40
+
41
+ e.execute "WelcomeMessage.new.call"
42
+ end
43
+
44
+ render Markdown.new(<<~MD)
45
+ ## Implicit scoopes
46
+
47
+ Start your translate key with a `.` to use the name of the view as an implicit scope.
48
+ MD
49
+
50
+ render Example.new do |e|
51
+ e.tab "welcome_message.rb", <<~RUBY
52
+ module Views
53
+ module Feedback
54
+ class WelcomeMessage < Phlex::HTML
55
+ include Phlex::Translation
56
+
57
+ def template
58
+ h1 { translate(".hello") }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ RUBY
64
+
65
+ e.tab "pt-BR.yml", <<~YAML, syntax: :yaml
66
+ pt-BR:
67
+ views:
68
+ feedback:
69
+ welcome_message:
70
+ hello: Olá
71
+ YAML
72
+
73
+ e.execute <<~RUBY
74
+ Views::Feedback::WelcomeMessage.translation_path = 'views.feedback.welcome_message'
75
+ Views::Feedback::WelcomeMessage.new.call
76
+ RUBY
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
data/docs/pages/views.rb CHANGED
@@ -3,69 +3,77 @@
3
3
  module Pages
4
4
  class Views < ApplicationPage
5
5
  def template
6
- render Layout.new(title: "Components in Phlex") do
6
+ render Layout.new(title: "Phlex Views") do
7
7
  render Markdown.new(<<~MD)
8
8
  # Views
9
9
 
10
- ## Yielding content
10
+ Phlex Views are Ruby objects that represent your app's user interface — from pages and layouts and nav-bars, to headings and buttons and links.
11
11
 
12
- Your views can accept content as a block passed to the template method. You can capture the content block and pass it to the `content` method to yield it.
12
+ You can create a view class by subclassing `Phlex::HTML` and defining a `template` instance method.
13
13
  MD
14
14
 
15
15
  render Example.new do |e|
16
- e.tab "card.rb", <<~RUBY
17
- class Card < Phlex::View
18
- def template(&)
19
- article(class: "drop-shadow rounded p-5") {
20
- h1 "Amazing content!"
21
- yield_content(&)
22
- }
16
+ e.tab "hello.rb", <<~RUBY
17
+ class Hello < Phlex::HTML
18
+ def template
19
+ h1 { "👋 Hello World!" }
23
20
  end
24
21
  end
25
22
  RUBY
26
23
 
27
- e.execute "Card.new.call { 'Your content here.\n' }"
24
+ e.execute "Hello.new.call"
28
25
  end
29
26
 
30
27
  render Markdown.new(<<~MD)
31
- ## Delegating content
28
+ The `template` method determines what your view will output when its rendered. The above example will output an `<h1>` tag with the content `👋 Hello world!`. Click on the "Output" tab above to see for yourself.
29
+
30
+ ## Accepting arguments
32
31
 
33
- Alternatively, you can pass the content down as an argument to another view or tag.
32
+ You can define an initializer for your views just like any other Ruby class. Let's make our `Hello` view take a `name` as a keyword argument, save it in an instance variable and render that variable in the template.
33
+
34
+ We'll render this view with the arguments `name: "Joel"` and see what it produces.
34
35
  MD
35
36
 
36
37
  render Example.new do |e|
37
- e.tab "card.rb", <<~RUBY
38
- class Card < Phlex::View
39
- def template(&)
40
- article(class: "drop-shadow rounded p-5", &)
38
+ e.tab "hello.rb", <<~RUBY
39
+ class Hello < Phlex::HTML
40
+ def initialize(name:)
41
+ @name = name
42
+ end
43
+
44
+ def template
45
+ h1 { "👋 Hello \#{@name}!" }
41
46
  end
42
47
  end
43
48
  RUBY
44
49
 
45
- e.execute "Card.new.call { 'Your content here.' }"
50
+ e.execute "Hello.new(name: 'Joel').call"
46
51
  end
47
52
 
48
53
  render Markdown.new(<<~MD)
49
- ## Nested views
54
+ ## Rendering views
50
55
 
51
- Components can render other views and optionally pass them content as a block.
56
+ Views can render other views in their templates using the `render` method. Let's try rendering a couple of instances of this `Hello` view from a new `Example` view and look at the output of the `Example` view.
52
57
  MD
53
58
 
54
59
  render Example.new do |e|
55
60
  e.tab "example.rb", <<~RUBY
56
- class Example < Phlex::View
61
+ class Example < Phlex::HTML
57
62
  def template
58
- render Card.new do
59
- h1 "Hello"
60
- end
63
+ render Hello.new(name: "Joel")
64
+ render Hello.new(name: "Alexandre")
61
65
  end
62
66
  end
63
67
  RUBY
64
68
 
65
- e.tab "card.rb", <<~RUBY
66
- class Card < Phlex::View
67
- def template(&)
68
- article(class: "drop-shadow rounded p-5", &)
69
+ e.tab "hello.rb", <<~RUBY
70
+ class Hello < Phlex::HTML
71
+ def initialize(name:)
72
+ @name = name
73
+ end
74
+
75
+ def template
76
+ h1 { "👋 Hello \#{@name}!" }
69
77
  end
70
78
  end
71
79
  RUBY
@@ -74,22 +82,28 @@ module Pages
74
82
  end
75
83
 
76
84
  render Markdown.new(<<~MD)
77
- If the block just wraps a string, the string is treated as _text content_.
85
+ ## Passing content blocks
86
+
87
+ Views can also yield content blocks, which can be passed in when rendering. Let's make a `Card` component that yields content in an `<article>` element with a `drop-shadow` class on it.
78
88
  MD
79
89
 
80
90
  render Example.new do |e|
81
- e.tab "example.rb", <<~RUBY
82
- class Example < Phlex::View
83
- def template
84
- render(Card.new) { "Hi" }
91
+ e.tab "card.rb", <<~RUBY
92
+ class Card < Phlex::HTML
93
+ def template(&content)
94
+ article(class: "drop-shadow") do
95
+ yield_content(&content)
96
+ end
85
97
  end
86
98
  end
87
99
  RUBY
88
100
 
89
- e.tab "card.rb", <<~RUBY
90
- class Card < Phlex::View
91
- def template(&)
92
- article(class: "drop-shadow rounded p-5", &)
101
+ e.tab "example.rb", <<~RUBY
102
+ class Example < Phlex::HTML
103
+ def template
104
+ render Card.new do
105
+ h1 { "👋 Hello!" }
106
+ end
93
107
  end
94
108
  end
95
109
  RUBY
@@ -98,28 +112,38 @@ module Pages
98
112
  end
99
113
 
100
114
  render Markdown.new(<<~MD)
101
- ## Component attributes
115
+ The template in the `Card` view accepts a block argument `&content` and uses the `yield_content` method to yield it in an `<article>` element.
102
116
 
103
- Besides content, views can define attributes in an initializer, which can then be rendered in the template.
104
- MD
117
+ The `Example` view renders a `Card` and passes it a block with an `<h1>` element.
105
118
 
106
- render Example.new do |e|
107
- e.tab "hello.rb", <<~RUBY
108
- class Hello < Phlex::View
109
- def initialize(name:)
110
- @name = name
111
- end
119
+ Looking at the output of the `Example` view, we can see the `<h1>` element was rendered inside the `<article>` element from the `Card` view.
112
120
 
113
- def template
114
- h1 "Hello \#{@name}!"
115
- end
121
+ ## Delegating content blocks
122
+
123
+ Since the block of content was the only thing we need in the `<article>` element, we could have just passed the content block to the element instead.
124
+
125
+ ```ruby
126
+ class Card < Phlex::HTML
127
+ def template(&content)
128
+ article(class: "drop-shadow", &content)
116
129
  end
117
- RUBY
130
+ end
131
+ ```
132
+ MD
133
+
134
+ render Markdown.new(<<~MD)
135
+ ## Registering custom elements
118
136
 
137
+ You can register custom elements with the `register_element` macro. The custom element will only be available in the view where it is registered and subclasses of that view.
138
+ MD
139
+
140
+ render Example.new do |e|
119
141
  e.tab "example.rb", <<~RUBY
120
- class Example < Phlex::View
142
+ class Example < Phlex::HTML
143
+ register_element :trix_editor
144
+
121
145
  def template
122
- render Hello.new(name: "Joel")
146
+ trix_editor input: "content", autofocus: true
123
147
  end
124
148
  end
125
149
  RUBY
@@ -128,41 +152,26 @@ module Pages
128
152
  end
129
153
 
130
154
  render Markdown.new(<<~MD)
131
- It’s usually a good idea to use instance variables directly rather than creating accessor methods for them. Otherwise it’s easy to run into naming conflicts. For example, your layout view might have the attribute `title`, to render into a `<title>` element in the document head. If you define `attr_accessor :title`, that would overwrite the `title` method for creating `<title>` elements.
155
+ ## Callbacks
132
156
 
133
- ## Calculations with methods
134
-
135
- Views are just Ruby classes, so you can perform calculations on view attributes by defining your own methods.
157
+ Prepend the `Phlex::HTML::Callbacks` module, and if you define `#before_rendering_template` and/or `#after_rendering_template` method in your view, they will be called immediately before and after your template is rendered.
136
158
  MD
137
159
 
138
160
  render Example.new do |e|
139
- e.tab "status.rb", <<~RUBY
140
- class Status < Phlex::View
141
- def initialize(status:)
142
- @status = status
143
- end
161
+ e.tab "example.rb", <<~RUBY
162
+ class Example < Phlex::HTML
163
+ prepend Phlex::HTML::Callbacks
144
164
 
145
- def template
146
- span status_emoji
165
+ def before_rendering_template
166
+ h1 { "Hello" }
147
167
  end
148
168
 
149
- private
150
-
151
- def status_emoji
152
- case @status
153
- when :success
154
- "✅"
155
- when :failure
156
- "❌"
157
- end
169
+ def template
170
+ h2 { "World" }
158
171
  end
159
- end
160
- RUBY
161
172
 
162
- e.tab "example.rb", <<~RUBY
163
- class Example < Phlex::View
164
- def template
165
- render Status.new(status: :success)
173
+ def after_rendering_template
174
+ h3 { "Bye" }
166
175
  end
167
176
  end
168
177
  RUBY
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CompilerTestHelpers
4
+ # @return Array
5
+ def compile(view)
6
+ @compiler = Phlex::Compiler.new(view)
7
+ output = []
8
+
9
+ mock(@compiler) do |m|
10
+ m.before(:redefine) { output << _1 }
11
+ end
12
+
13
+ @compiler.call
14
+
15
+ output.map! do |method|
16
+ Phlex::Compiler::Formatter.format("", SyntaxTree.parse(method))
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fixtures
4
+ module Content
5
+ class BareString < Phlex::HTML
6
+ def template
7
+ h1 { "Hello" }
8
+ end
9
+ end
10
+
11
+ class Symbol < Phlex::HTML
12
+ def template
13
+ h1 { :hello }
14
+ end
15
+ end
16
+
17
+ class Float < Phlex::HTML
18
+ def template
19
+ h1 { 1.2 }
20
+ end
21
+ end
22
+
23
+ class Integer < Phlex::HTML
24
+ def template
25
+ h1 { 1 }
26
+ end
27
+ end
28
+
29
+ class Variable < Phlex::HTML
30
+ def template
31
+ greeting = "Hello"
32
+ h1 { greeting }
33
+ end
34
+ end
35
+
36
+ class InstanceVariable < Phlex::HTML
37
+ def template
38
+ h1 { @hello }
39
+ end
40
+ end
41
+
42
+ class NestedTags < Phlex::HTML
43
+ def template
44
+ article {
45
+ h1 { "Inside" }
46
+ }
47
+ end
48
+ end
49
+
50
+ class NonMutatingNestedContent < Phlex::HTML
51
+ def template
52
+ div { say_hello }
53
+ end
54
+
55
+ def say_hello
56
+ "Hello"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,14 @@
1
+ <div>
2
+ <span>
3
+ <%= @name %>
4
+ </span>
5
+ <span>
6
+ <%= @body %>
7
+ </span>
8
+
9
+ <%= content %>
10
+
11
+ <%= render Views::Comments::Reaction.new(emoji: 'hamburger') do |reaction| %>
12
+ <p>Emoji reaction for a comment from <%= @name %> with body <%= @body %></p>
13
+ <% end %>
14
+ </div>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CommentComponent < ViewComponent::Base
4
+ def initialize(name:, body:)
5
+ @name = name
6
+ @body = body
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ <p><%= @emoji %></p>
2
+
3
+ <%= content %>
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ReactionComponent < ViewComponent::Base
4
+ def initialize(emoji:)
5
+ @emoji = emoji
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CommentsController < ActionController::Base
4
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Views
4
+ class ApplicationView < Phlex::HTML
5
+ include Rails.application.routes.url_helpers
6
+ include Phlex::Translation
7
+ end
8
+ end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Views
4
4
  module Articles
5
- class Form < Phlex::View
5
+ class Form < ApplicationView
6
+ include Phlex::Rails::Helpers::FormWith
7
+
6
8
  def template
7
9
  form_with url: "test" do |f|
8
10
  f.text_field :name