phlex 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/.editorconfig +5 -0
- data/.rubocop.yml +6 -0
- data/CONTRIBUTING.md +23 -0
- data/Rakefile +2 -2
- data/bench.rb +1 -1
- data/docs/assets/application.css +2 -0
- data/docs/build.rb +4 -4
- data/docs/components/callout.rb +5 -5
- data/docs/components/code_block.rb +18 -18
- data/docs/components/example.rb +23 -23
- data/docs/components/heading.rb +5 -5
- data/docs/components/layout.rb +42 -41
- data/docs/components/markdown.rb +19 -19
- data/docs/components/tabs/tab.rb +18 -18
- data/docs/components/tabs.rb +21 -21
- data/docs/components/title.rb +5 -5
- data/docs/page_builder.rb +32 -32
- data/docs/pages/application_page.rb +3 -3
- data/docs/pages/index.rb +23 -25
- data/docs/pages/rails_integration.rb +58 -0
- data/docs/pages/templates.rb +238 -238
- data/docs/pages/views.rb +175 -0
- data/fixtures/compilation/vcall.rb +38 -0
- data/fixtures/dummy/app/views/articles/form.rb +9 -9
- data/fixtures/dummy/app/views/card.rb +8 -8
- data/fixtures/dummy/app/views/heading.rb +5 -5
- data/fixtures/dummy/config/routes.rb +1 -1
- data/fixtures/dummy/db/schema.rb +2 -2
- data/fixtures/layout.rb +24 -24
- data/fixtures/page.rb +34 -34
- data/fixtures/test_helper.rb +2 -2
- data/fixtures/view_helper.rb +16 -0
- data/lib/generators/phlex/component/USAGE +1 -1
- data/lib/generators/phlex/component/component_generator.rb +8 -8
- data/lib/generators/phlex/component/templates/{component.rb.erb → view.rb.erb} +1 -1
- data/lib/install/phlex.rb +18 -0
- data/lib/overrides/symbol/name.rb +1 -1
- data/lib/phlex/block.rb +12 -12
- data/lib/phlex/buffered.rb +13 -13
- data/lib/phlex/compiler/formatter.rb +91 -0
- data/lib/phlex/compiler/generators/standard_element.rb +30 -0
- data/lib/phlex/compiler/generators/void_element.rb +29 -0
- data/lib/phlex/compiler/optimizers/base_optimizer.rb +34 -0
- data/lib/phlex/compiler/optimizers/vcall.rb +29 -0
- data/lib/phlex/compiler/visitors/base_visitor.rb +19 -0
- data/lib/phlex/compiler/visitors/component.rb +28 -0
- data/lib/phlex/compiler/visitors/component_method.rb +28 -0
- data/lib/phlex/compiler/visitors/file.rb +17 -0
- data/lib/phlex/compiler.rb +50 -0
- data/lib/phlex/configuration.rb +3 -3
- data/lib/phlex/engine.rb +11 -0
- data/lib/phlex/html.rb +128 -17
- data/lib/phlex/rails/tag_helpers.rb +23 -23
- data/lib/phlex/renderable.rb +32 -32
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex/view.rb +223 -0
- data/lib/phlex.rb +27 -12
- data/lib/tasks/phlex_tasks.rake +10 -0
- metadata +39 -10
- data/CHANGELOG.md +0 -5
- data/docs/pages/components.rb +0 -175
- data/fixtures/component_helper.rb +0 -16
- data/lib/phlex/component.rb +0 -196
- data/lib/phlex/rails.rb +0 -8
data/lib/phlex/view.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
|
4
|
+
using Overrides::Symbol::Name
|
5
|
+
end
|
6
|
+
|
7
|
+
module Phlex
|
8
|
+
class View
|
9
|
+
extend HTML
|
10
|
+
include Renderable
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :rendered_at_least_once
|
14
|
+
|
15
|
+
def compile
|
16
|
+
return if @compiled
|
17
|
+
return unless name
|
18
|
+
return if name.start_with? "#"
|
19
|
+
|
20
|
+
Compiler.new(self).call
|
21
|
+
|
22
|
+
@compiled = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def compiled?
|
26
|
+
!!@compiled
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(buffer = +"", view_context: nil, parent: nil, &block)
|
31
|
+
raise "The same view instance shouldn't be rendered twice" if rendered?
|
32
|
+
|
33
|
+
@_rendered = true
|
34
|
+
@_target = buffer
|
35
|
+
@_view_context = view_context
|
36
|
+
@_parent = parent
|
37
|
+
@output_buffer = self
|
38
|
+
|
39
|
+
template(&block)
|
40
|
+
|
41
|
+
self.class.rendered_at_least_once ||= true
|
42
|
+
|
43
|
+
buffer
|
44
|
+
end
|
45
|
+
|
46
|
+
def rendered?
|
47
|
+
@_rendered ||= false
|
48
|
+
end
|
49
|
+
|
50
|
+
HTML::STANDARD_ELEMENTS.each do |method_name, tag|
|
51
|
+
register_element(method_name, tag: tag)
|
52
|
+
end
|
53
|
+
|
54
|
+
HTML::VOID_ELEMENTS.each do |method_name, tag|
|
55
|
+
register_void_element(method_name, tag: tag)
|
56
|
+
end
|
57
|
+
|
58
|
+
def yield_content(&block)
|
59
|
+
return unless block_given?
|
60
|
+
|
61
|
+
original_length = @_target.length
|
62
|
+
output = yield(self)
|
63
|
+
unchanged = (original_length == @_target.length)
|
64
|
+
|
65
|
+
if unchanged
|
66
|
+
case output
|
67
|
+
when String, Symbol, Integer, Float
|
68
|
+
text(output)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def text(content)
|
76
|
+
@_target << case content
|
77
|
+
when String then CGI.escape_html(content)
|
78
|
+
when Symbol then CGI.escape_html(content.name)
|
79
|
+
else CGI.escape_html(content.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def whitespace
|
86
|
+
@_target << " "
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def comment(content = "")
|
91
|
+
@_target << "<!-- " << CGI.escape_html(content.to_s) << " -->"
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def doctype
|
96
|
+
@_target << HTML::DOCTYPE
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def raw(content)
|
101
|
+
@_target << content
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def html_safe?
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def safe_append=(value)
|
110
|
+
return unless value
|
111
|
+
|
112
|
+
@_target << case value
|
113
|
+
when String then value
|
114
|
+
when Symbol then value.name
|
115
|
+
else value.to_s
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def append=(value)
|
120
|
+
return unless value
|
121
|
+
|
122
|
+
if value.html_safe?
|
123
|
+
self.safe_append = value
|
124
|
+
else
|
125
|
+
@_target << case value
|
126
|
+
when String then CGI.escape_html(value)
|
127
|
+
when Symbol then CGI.escape_html(value.name)
|
128
|
+
else CGI.escape_html(value.to_s)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def capture(&block)
|
134
|
+
return unless block_given?
|
135
|
+
|
136
|
+
original_buffer = @_target
|
137
|
+
new_buffer = +""
|
138
|
+
@_target = new_buffer
|
139
|
+
|
140
|
+
yield
|
141
|
+
|
142
|
+
@_target = original_buffer
|
143
|
+
new_buffer.html_safe
|
144
|
+
end
|
145
|
+
|
146
|
+
def classes(*tokens, **conditional_tokens)
|
147
|
+
{ class: self.tokens(*tokens, **conditional_tokens) }
|
148
|
+
end
|
149
|
+
|
150
|
+
def tokens(*tokens, **conditional_tokens)
|
151
|
+
conditional_tokens.each do |condition, token|
|
152
|
+
case condition
|
153
|
+
when Symbol then next unless send(condition)
|
154
|
+
when Proc then next unless condition.call
|
155
|
+
else raise ArgumentError,
|
156
|
+
"The class condition must be a Symbol or a Proc."
|
157
|
+
end
|
158
|
+
|
159
|
+
case token
|
160
|
+
when Symbol then tokens << token.name
|
161
|
+
when String then tokens << token
|
162
|
+
when Array then tokens.concat(t)
|
163
|
+
else raise ArgumentError,
|
164
|
+
"Conditional classes must be Symbols, Strings, or Arrays of Symbols or Strings."
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
tokens.compact.join(" ")
|
169
|
+
end
|
170
|
+
|
171
|
+
def helpers
|
172
|
+
@_view_context
|
173
|
+
end
|
174
|
+
|
175
|
+
def _attributes(attributes, buffer: +"")
|
176
|
+
if attributes[:href]&.start_with?(/\s*javascript/)
|
177
|
+
attributes[:href] = attributes[:href].sub(/^\s*(javascript:)+/, "")
|
178
|
+
end
|
179
|
+
|
180
|
+
_build_attributes(attributes, buffer: buffer)
|
181
|
+
|
182
|
+
unless self.class.rendered_at_least_once
|
183
|
+
Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
|
184
|
+
end
|
185
|
+
|
186
|
+
buffer
|
187
|
+
end
|
188
|
+
|
189
|
+
def _build_attributes(attributes, buffer:)
|
190
|
+
attributes.each do |k, v|
|
191
|
+
next unless v
|
192
|
+
|
193
|
+
name = case k
|
194
|
+
when String
|
195
|
+
k
|
196
|
+
when Symbol
|
197
|
+
k.name.tr("_", "-")
|
198
|
+
else
|
199
|
+
k.to_s
|
200
|
+
end
|
201
|
+
|
202
|
+
if HTML::EVENT_ATTRIBUTES[name] || name.match?(/[<>&"']/)
|
203
|
+
raise ArgumentError, "Unsafe attribute name detected: #{k}."
|
204
|
+
end
|
205
|
+
|
206
|
+
case v
|
207
|
+
when true
|
208
|
+
buffer << " " << name
|
209
|
+
when String
|
210
|
+
buffer << " " << name << '="' << CGI.escape_html(v) << '"'
|
211
|
+
when Symbol
|
212
|
+
buffer << " " << name << '="' << CGI.escape_html(v.name) << '"'
|
213
|
+
when Hash
|
214
|
+
_build_attributes(v.transform_keys { "#{k}-#{_1.name.tr('_', '-')}" }, buffer: buffer)
|
215
|
+
else
|
216
|
+
buffer << " " << name << '="' << CGI.escape_html(v.to_s) << '"'
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
buffer
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/lib/phlex.rb
CHANGED
@@ -2,29 +2,44 @@
|
|
2
2
|
|
3
3
|
require "cgi"
|
4
4
|
require "zeitwerk"
|
5
|
+
require "syntax_tree"
|
5
6
|
|
6
7
|
loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
7
8
|
loader.ignore("#{__dir__}/generators")
|
8
9
|
loader.inflector.inflect("html" => "HTML")
|
10
|
+
loader.inflector.inflect("vcall" => "VCall")
|
11
|
+
loader.inflector.inflect("fcall" => "FCall")
|
9
12
|
loader.setup
|
10
13
|
|
11
14
|
module Phlex
|
12
|
-
|
13
|
-
|
15
|
+
Error = Module.new
|
16
|
+
ArgumentError = Class.new(ArgumentError) { include Error }
|
17
|
+
NameError = Class.new(NameError) { include Error }
|
14
18
|
|
15
|
-
|
19
|
+
extend self
|
16
20
|
|
17
|
-
|
21
|
+
ATTRIBUTE_CACHE = {}
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
def const_missing(name)
|
24
|
+
if name == :Component
|
25
|
+
raise NameError, "👋 Phlex::Component is now Phlex::View"
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
31
|
+
def configuration
|
32
|
+
@configuration ||= Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def configure
|
36
|
+
yield configuration
|
37
|
+
end
|
26
38
|
end
|
27
39
|
|
28
|
-
|
29
|
-
|
40
|
+
begin
|
41
|
+
require "rails"
|
42
|
+
require "phlex/engine"
|
43
|
+
rescue LoadError
|
44
|
+
# Rails isn't in this env, don't load the engine.
|
30
45
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :phlex do
|
4
|
+
desc "Install Phlex in the Rails application"
|
5
|
+
task :install do
|
6
|
+
install_file_path = File.expand_path("../install/phlex.rb", __dir__)
|
7
|
+
|
8
|
+
system "#{RbConfig.ruby} bin/rails app:template LOCATION=#{install_file_path}"
|
9
|
+
end
|
10
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phlex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Drapper
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: syntax_tree
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.6'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: zeitwerk
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,17 +38,18 @@ dependencies:
|
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '2.6'
|
27
|
-
description: A high-performance view
|
41
|
+
description: A high-performance view framework optimised for developer happiness.
|
28
42
|
email:
|
29
43
|
- joel@drapper.me
|
30
44
|
executables: []
|
31
45
|
extensions: []
|
32
46
|
extra_rdoc_files: []
|
33
47
|
files:
|
48
|
+
- ".editorconfig"
|
34
49
|
- ".rspec"
|
35
50
|
- ".rubocop.yml"
|
36
|
-
- CHANGELOG.md
|
37
51
|
- CODE_OF_CONDUCT.md
|
52
|
+
- CONTRIBUTING.md
|
38
53
|
- Gemfile
|
39
54
|
- LICENSE.txt
|
40
55
|
- README.md
|
@@ -56,10 +71,11 @@ files:
|
|
56
71
|
- docs/components/title.rb
|
57
72
|
- docs/page_builder.rb
|
58
73
|
- docs/pages/application_page.rb
|
59
|
-
- docs/pages/components.rb
|
60
74
|
- docs/pages/index.rb
|
75
|
+
- docs/pages/rails_integration.rb
|
61
76
|
- docs/pages/templates.rb
|
62
|
-
-
|
77
|
+
- docs/pages/views.rb
|
78
|
+
- fixtures/compilation/vcall.rb
|
63
79
|
- fixtures/dummy/app/assets/config/manifest.js
|
64
80
|
- fixtures/dummy/app/controllers/articles_controller.rb
|
65
81
|
- fixtures/dummy/app/views/articles/form.rb
|
@@ -76,20 +92,33 @@ files:
|
|
76
92
|
- fixtures/layout.rb
|
77
93
|
- fixtures/page.rb
|
78
94
|
- fixtures/test_helper.rb
|
95
|
+
- fixtures/view_helper.rb
|
79
96
|
- lib/generators/phlex/component/USAGE
|
80
97
|
- lib/generators/phlex/component/component_generator.rb
|
81
|
-
- lib/generators/phlex/component/templates/
|
98
|
+
- lib/generators/phlex/component/templates/view.rb.erb
|
99
|
+
- lib/install/phlex.rb
|
82
100
|
- lib/overrides/symbol/name.rb
|
83
101
|
- lib/phlex.rb
|
84
102
|
- lib/phlex/block.rb
|
85
103
|
- lib/phlex/buffered.rb
|
86
|
-
- lib/phlex/
|
104
|
+
- lib/phlex/compiler.rb
|
105
|
+
- lib/phlex/compiler/formatter.rb
|
106
|
+
- lib/phlex/compiler/generators/standard_element.rb
|
107
|
+
- lib/phlex/compiler/generators/void_element.rb
|
108
|
+
- lib/phlex/compiler/optimizers/base_optimizer.rb
|
109
|
+
- lib/phlex/compiler/optimizers/vcall.rb
|
110
|
+
- lib/phlex/compiler/visitors/base_visitor.rb
|
111
|
+
- lib/phlex/compiler/visitors/component.rb
|
112
|
+
- lib/phlex/compiler/visitors/component_method.rb
|
113
|
+
- lib/phlex/compiler/visitors/file.rb
|
87
114
|
- lib/phlex/configuration.rb
|
115
|
+
- lib/phlex/engine.rb
|
88
116
|
- lib/phlex/html.rb
|
89
|
-
- lib/phlex/rails.rb
|
90
117
|
- lib/phlex/rails/tag_helpers.rb
|
91
118
|
- lib/phlex/renderable.rb
|
92
119
|
- lib/phlex/version.rb
|
120
|
+
- lib/phlex/view.rb
|
121
|
+
- lib/tasks/phlex_tasks.rake
|
93
122
|
- package-lock.json
|
94
123
|
- package.json
|
95
124
|
- phlex_logo.png
|
@@ -122,5 +151,5 @@ requirements: []
|
|
122
151
|
rubygems_version: 3.3.7
|
123
152
|
signing_key:
|
124
153
|
specification_version: 4
|
125
|
-
summary: A framework for building
|
154
|
+
summary: A framework for building views with a Ruby DSL.
|
126
155
|
test_files: []
|
data/CHANGELOG.md
DELETED
data/docs/pages/components.rb
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pages
|
4
|
-
class Components < ApplicationPage
|
5
|
-
def template
|
6
|
-
render Layout.new(title: "Components in Phlex") do
|
7
|
-
render Markdown.new(<<~MD)
|
8
|
-
# Components
|
9
|
-
|
10
|
-
## Yielding content
|
11
|
-
|
12
|
-
Your components 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.
|
13
|
-
MD
|
14
|
-
|
15
|
-
render Example.new do |e|
|
16
|
-
e.tab "card.rb", <<~RUBY
|
17
|
-
class Card < Phlex::Component
|
18
|
-
def template(&)
|
19
|
-
article(class: "drop-shadow rounded p-5") {
|
20
|
-
h1 "Amazing content!"
|
21
|
-
content(&)
|
22
|
-
}
|
23
|
-
end
|
24
|
-
end
|
25
|
-
RUBY
|
26
|
-
|
27
|
-
e.execute "Card.new.call { 'Your content here.\n' }"
|
28
|
-
end
|
29
|
-
|
30
|
-
render Markdown.new(<<~MD)
|
31
|
-
## Delegating content
|
32
|
-
|
33
|
-
Alternatively, you can pass the content down as an argument to another component or tag.
|
34
|
-
MD
|
35
|
-
|
36
|
-
render Example.new do |e|
|
37
|
-
e.tab "card.rb", <<~RUBY
|
38
|
-
class Card < Phlex::Component
|
39
|
-
def template(&)
|
40
|
-
article(class: "drop-shadow rounded p-5", &)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
RUBY
|
44
|
-
|
45
|
-
e.execute "Card.new.call { 'Your content here.' }"
|
46
|
-
end
|
47
|
-
|
48
|
-
render Markdown.new(<<~MD)
|
49
|
-
## Nested components
|
50
|
-
|
51
|
-
Components can render other components and optionally pass them content as a block.
|
52
|
-
MD
|
53
|
-
|
54
|
-
render Example.new do |e|
|
55
|
-
e.tab "example.rb", <<~RUBY
|
56
|
-
class Example < Phlex::Component
|
57
|
-
def template
|
58
|
-
render Card.new do
|
59
|
-
h1 "Hello"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
RUBY
|
64
|
-
|
65
|
-
e.tab "card.rb", <<~RUBY
|
66
|
-
class Card < Phlex::Component
|
67
|
-
def template(&)
|
68
|
-
article(class: "drop-shadow rounded p-5", &)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
RUBY
|
72
|
-
|
73
|
-
e.execute "Example.new.call"
|
74
|
-
end
|
75
|
-
|
76
|
-
render Markdown.new(<<~MD)
|
77
|
-
If the block just wraps a string, the string is treated as _text content_.
|
78
|
-
MD
|
79
|
-
|
80
|
-
render Example.new do |e|
|
81
|
-
e.tab "example.rb", <<~RUBY
|
82
|
-
class Example < Phlex::Component
|
83
|
-
def template
|
84
|
-
render(Card.new) { "Hi" }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
RUBY
|
88
|
-
|
89
|
-
e.tab "card.rb", <<~RUBY
|
90
|
-
class Card < Phlex::Component
|
91
|
-
def template(&)
|
92
|
-
article(class: "drop-shadow rounded p-5", &)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
RUBY
|
96
|
-
|
97
|
-
e.execute "Example.new.call"
|
98
|
-
end
|
99
|
-
|
100
|
-
render Markdown.new(<<~MD)
|
101
|
-
## Component attributes
|
102
|
-
|
103
|
-
Besides content, components can define attributes in an initializer, which can then be rendered in the template.
|
104
|
-
MD
|
105
|
-
|
106
|
-
render Example.new do |e|
|
107
|
-
e.tab "hello.rb", <<~RUBY
|
108
|
-
class Hello < Phlex::Component
|
109
|
-
def initialize(name:)
|
110
|
-
@name = name
|
111
|
-
end
|
112
|
-
|
113
|
-
def template
|
114
|
-
h1 "Hello \#{@name}!"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
RUBY
|
118
|
-
|
119
|
-
e.tab "example.rb", <<~RUBY
|
120
|
-
class Example < Phlex::Component
|
121
|
-
def template
|
122
|
-
render Hello.new(name: "Joel")
|
123
|
-
end
|
124
|
-
end
|
125
|
-
RUBY
|
126
|
-
|
127
|
-
e.execute "Example.new.call"
|
128
|
-
end
|
129
|
-
|
130
|
-
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 component 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.
|
132
|
-
|
133
|
-
## Calculations with methods
|
134
|
-
|
135
|
-
Components are just Ruby classes, so you can perform calculations on component attributes by defining your own methods.
|
136
|
-
MD
|
137
|
-
|
138
|
-
render Example.new do |e|
|
139
|
-
e.tab "status.rb", <<~RUBY
|
140
|
-
class Status < Phlex::Component
|
141
|
-
def initialize(status:)
|
142
|
-
@status = status
|
143
|
-
end
|
144
|
-
|
145
|
-
def template
|
146
|
-
span status_emoji
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
def status_emoji
|
152
|
-
case @status
|
153
|
-
when :success
|
154
|
-
"✅"
|
155
|
-
when :failure
|
156
|
-
"❌"
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
RUBY
|
161
|
-
|
162
|
-
e.tab "example.rb", <<~RUBY
|
163
|
-
class Example < Phlex::Component
|
164
|
-
def template
|
165
|
-
render Status.new(status: :success)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
RUBY
|
169
|
-
|
170
|
-
e.execute "Example.new.call"
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ComponentHelper
|
4
|
-
def self.extended(parent)
|
5
|
-
parent.instance_exec do
|
6
|
-
let(:output) { example.call }
|
7
|
-
let(:example) { component.new }
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def component(&block)
|
12
|
-
let :component do
|
13
|
-
Class.new(Phlex::Component, &block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|