phlex 0.4.0 → 0.5.1
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/.rubocop.yml +21 -1
- data/Gemfile +26 -13
- data/README.md +1 -1
- data/SECURITY.md +1 -1
- data/bench.rb +7 -0
- data/config/sus.rb +15 -0
- data/docs/assets/application.css +6 -0
- data/docs/build.rb +2 -0
- data/docs/components/callout.rb +1 -1
- data/docs/components/code_block.rb +2 -2
- data/docs/components/code_span.rb +1 -1
- data/docs/components/example.rb +4 -4
- data/docs/components/heading.rb +2 -2
- data/docs/components/layout.rb +55 -32
- data/docs/components/markdown.rb +13 -28
- data/docs/components/nav/item.rb +1 -1
- data/docs/components/nav.rb +1 -1
- data/docs/components/tabs/tab.rb +1 -1
- data/docs/components/tabs.rb +1 -1
- data/docs/components/title.rb +2 -2
- data/docs/pages/application_page.rb +1 -1
- data/docs/pages/helpers.rb +5 -5
- data/docs/pages/library/collections.rb +4 -22
- data/docs/pages/rails/getting_started.rb +7 -3
- data/docs/pages/rails/helpers.rb +3 -1
- data/docs/pages/rails/layouts.rb +2 -2
- data/docs/pages/rails/rendering_views.rb +1 -1
- data/docs/pages/templates.rb +6 -6
- data/docs/pages/testing/capybara.rb +48 -0
- data/docs/pages/testing/getting_started.rb +44 -0
- data/docs/pages/testing/nokogiri.rb +83 -0
- data/docs/pages/testing/rails.rb +17 -0
- data/docs/pages/translations.rb +81 -0
- data/docs/pages/views.rb +56 -8
- data/fixtures/compiler_test_helpers.rb +19 -0
- data/fixtures/content.rb +60 -0
- data/fixtures/dummy/app/views/application_view.rb +8 -0
- data/fixtures/dummy/app/views/articles/form.rb +1 -1
- data/fixtures/dummy/app/views/card.rb +1 -1
- data/fixtures/dummy/app/views/comments/comment.rb +1 -1
- data/fixtures/dummy/app/views/comments/reaction.rb +1 -1
- data/fixtures/dummy/app/views/heading.rb +1 -1
- data/fixtures/layout.rb +5 -5
- data/fixtures/page.rb +18 -24
- data/fixtures/{test_helper.rb → rails_helper.rb} +3 -8
- data/fixtures/standard_element.rb +87 -0
- data/fixtures/view_helper.rb +1 -1
- data/fixtures/void_element.rb +31 -0
- data/lib/generators/phlex/collection/templates/collection.rb.erb +2 -1
- data/lib/generators/phlex/controller/USAGE +10 -0
- data/lib/generators/phlex/controller/controller_generator.rb +54 -0
- data/lib/generators/phlex/controller/templates/controller.rb.erb +10 -0
- data/lib/generators/phlex/controller/templates/view.rb.erb +14 -0
- data/lib/generators/phlex/layout/templates/layout.rb.erb +2 -1
- data/lib/generators/phlex/page/templates/page.rb.erb +3 -1
- data/lib/generators/phlex/table/templates/table.rb.erb +3 -1
- data/lib/generators/phlex/view/templates/view.rb.erb +7 -1
- data/lib/generators/phlex/view/view_generator.rb +9 -1
- data/lib/install/phlex.rb +10 -1
- data/lib/phlex/block.rb +2 -4
- data/lib/phlex/buffered.rb +6 -8
- data/lib/phlex/callable.rb +9 -0
- data/lib/phlex/collection.rb +2 -27
- data/lib/phlex/compiler/elements.rb +49 -0
- data/lib/phlex/compiler/generators/content.rb +103 -0
- data/lib/phlex/compiler/generators/element.rb +61 -0
- data/lib/phlex/compiler/nodes/base.rb +19 -0
- data/lib/phlex/compiler/nodes/call.rb +9 -0
- data/lib/phlex/compiler/nodes/command.rb +13 -0
- data/lib/phlex/compiler/nodes/fcall.rb +18 -0
- data/lib/phlex/compiler/nodes/method_add_block.rb +33 -0
- data/lib/phlex/compiler/nodes/vcall.rb +9 -0
- data/lib/phlex/compiler/optimizer.rb +66 -0
- data/lib/phlex/compiler/visitors/base.rb +15 -0
- data/lib/phlex/compiler/visitors/file.rb +23 -11
- data/lib/phlex/compiler/visitors/stable_scope.rb +28 -0
- data/lib/phlex/compiler/visitors/statements.rb +36 -0
- data/lib/phlex/compiler/visitors/view.rb +19 -0
- data/lib/phlex/compiler/visitors/view_method.rb +59 -0
- data/lib/phlex/compiler.rb +23 -3
- data/lib/phlex/elements.rb +57 -0
- data/lib/phlex/helpers.rb +59 -0
- data/lib/phlex/html/callbacks.rb +11 -0
- data/lib/phlex/html.rb +208 -47
- data/lib/phlex/markdown.rb +76 -0
- data/lib/phlex/rails/form.rb +67 -0
- data/lib/phlex/rails/helpers.rb +39 -2
- data/lib/phlex/rails.rb +10 -0
- data/lib/phlex/renderable.rb +9 -3
- data/lib/phlex/testing/capybara.rb +25 -0
- data/lib/phlex/testing/nokogiri.rb +24 -0
- data/lib/phlex/testing/rails.rb +19 -0
- data/lib/phlex/testing/view_helper.rb +15 -0
- data/lib/phlex/translation.rb +23 -0
- data/lib/phlex/turbo/frame.rb +21 -0
- data/lib/phlex/turbo/stream.rb +18 -0
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex.rb +23 -24
- metadata +62 -14
- data/.rspec +0 -1
- data/fixtures/compilation/vcall.rb +0 -38
- data/lib/phlex/compiler/generators/standard_element.rb +0 -30
- data/lib/phlex/compiler/generators/void_element.rb +0 -29
- data/lib/phlex/compiler/optimizers/base_optimizer.rb +0 -34
- data/lib/phlex/compiler/optimizers/vcall.rb +0 -29
- data/lib/phlex/compiler/visitors/base_visitor.rb +0 -19
- data/lib/phlex/compiler/visitors/component.rb +0 -28
- data/lib/phlex/compiler/visitors/component_method.rb +0 -28
- data/lib/phlex/view.rb +0 -229
data/lib/phlex/html.rb
CHANGED
@@ -5,7 +5,7 @@ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
|
|
5
5
|
end
|
6
6
|
|
7
7
|
module Phlex
|
8
|
-
|
8
|
+
class HTML
|
9
9
|
DOCTYPE = "<!DOCTYPE html>"
|
10
10
|
|
11
11
|
STANDARD_ELEMENTS = {
|
@@ -126,52 +126,213 @@ module Phlex
|
|
126
126
|
|
127
127
|
EVENT_ATTRIBUTES = %w[onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncopy oncuechange oncut ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onerror onfocus onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmessage onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onoffline ononline onpagehide onpageshow onpaste onpause onplay onplaying onpopstate onprogress onratechange onreset onresize onscroll onsearch onseeked onseeking onselect onstalled onstorage onsubmit onsuspend ontimeupdate ontoggle onunload onvolumechange onwaiting onwheel].to_h { [_1, true] }.freeze
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
129
|
+
extend Elements
|
130
|
+
include Helpers
|
131
|
+
include Callable
|
132
|
+
include Renderable
|
133
|
+
|
134
|
+
class << self
|
135
|
+
attr_accessor :rendered_at_least_once
|
136
|
+
|
137
|
+
# def compile
|
138
|
+
# return if @compiled
|
139
|
+
# return unless name
|
140
|
+
# return if name.start_with? "#"
|
141
|
+
#
|
142
|
+
# Compiler.new(self).call
|
143
|
+
#
|
144
|
+
# @compiled = true
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# def compiled?
|
148
|
+
# !!@compiled
|
149
|
+
# end
|
150
|
+
end
|
151
|
+
|
152
|
+
def call(buffer = +"", view_context: nil, parent: nil, &block)
|
153
|
+
return buffer unless render?
|
154
|
+
|
155
|
+
raise "The same view instance shouldn't be rendered twice" if rendered?
|
156
|
+
|
157
|
+
@_rendered = true
|
158
|
+
@_target = buffer
|
159
|
+
@_view_context = view_context
|
160
|
+
@_parent = parent
|
161
|
+
@output_buffer = self
|
162
|
+
|
163
|
+
template(&block)
|
164
|
+
|
165
|
+
self.class.rendered_at_least_once ||= true
|
166
|
+
|
167
|
+
buffer
|
168
|
+
end
|
169
|
+
|
170
|
+
def rendered?
|
171
|
+
@_rendered ||= false
|
172
|
+
end
|
173
|
+
|
174
|
+
def render?
|
175
|
+
true
|
176
|
+
end
|
177
|
+
|
178
|
+
STANDARD_ELEMENTS.each do |method_name, tag|
|
179
|
+
register_element(method_name, tag: tag)
|
180
|
+
end
|
181
|
+
|
182
|
+
VOID_ELEMENTS.each do |method_name, tag|
|
183
|
+
register_void_element(method_name, tag: tag)
|
184
|
+
end
|
185
|
+
|
186
|
+
def yield_content(&block)
|
187
|
+
return unless block_given?
|
188
|
+
|
189
|
+
original_length = @_target.length
|
190
|
+
output = yield(self)
|
191
|
+
unchanged = (original_length == @_target.length)
|
192
|
+
|
193
|
+
if unchanged
|
194
|
+
case output
|
195
|
+
when String, Symbol, Integer, Float
|
196
|
+
text(output)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
|
203
|
+
def text(content)
|
204
|
+
@_target << _output(content)
|
205
|
+
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
|
209
|
+
def _output(content)
|
210
|
+
case content
|
211
|
+
when String then Hescape.escape_html(content)
|
212
|
+
when Symbol then Hescape.escape_html(content.name)
|
213
|
+
else Hescape.escape_html(content.to_s)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def whitespace
|
218
|
+
@_target << " "
|
219
|
+
|
220
|
+
if block_given?
|
221
|
+
yield
|
222
|
+
@_target << " "
|
223
|
+
end
|
224
|
+
|
225
|
+
nil
|
226
|
+
end
|
227
|
+
|
228
|
+
def comment(content = "")
|
229
|
+
@_target << "<!-- " << Hescape.escape_html(content.to_s) << " -->"
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
def doctype
|
234
|
+
@_target << DOCTYPE
|
235
|
+
nil
|
236
|
+
end
|
237
|
+
|
238
|
+
def unsafe_raw(content = nil, &block)
|
239
|
+
@_target << (content || instance_exec(&block))
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
|
243
|
+
def html_safe?
|
244
|
+
true
|
245
|
+
end
|
246
|
+
|
247
|
+
def safe_append=(value)
|
248
|
+
return unless value
|
249
|
+
|
250
|
+
@_target << case value
|
251
|
+
when String then value
|
252
|
+
when Symbol then value.name
|
253
|
+
else value.to_s
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def append=(value)
|
258
|
+
return unless value
|
259
|
+
|
260
|
+
if value.html_safe?
|
261
|
+
self.safe_append = value
|
262
|
+
else
|
263
|
+
@_target << case value
|
264
|
+
when String then Hescape.escape_html(value)
|
265
|
+
when Symbol then Hescape.escape_html(value.name)
|
266
|
+
else Hescape.escape_html(value.to_s)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def capture(&block)
|
272
|
+
return unless block_given?
|
273
|
+
|
274
|
+
original_buffer = @_target
|
275
|
+
new_buffer = +""
|
276
|
+
@_target = new_buffer
|
277
|
+
|
278
|
+
yield
|
279
|
+
|
280
|
+
@_target = original_buffer
|
281
|
+
|
282
|
+
new_buffer.html_safe
|
283
|
+
end
|
284
|
+
|
285
|
+
def helpers
|
286
|
+
@_view_context
|
287
|
+
end
|
288
|
+
|
289
|
+
def _attributes(**attributes)
|
290
|
+
if attributes[:href]&.start_with?(/\s*javascript/)
|
291
|
+
attributes[:href] = attributes[:href].sub(/^\s*(javascript:)+/, "")
|
292
|
+
end
|
293
|
+
|
294
|
+
buffer = +""
|
295
|
+
_build_attributes(attributes, buffer: buffer)
|
296
|
+
|
297
|
+
unless self.class.rendered_at_least_once
|
298
|
+
Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
|
299
|
+
end
|
300
|
+
|
301
|
+
buffer
|
302
|
+
end
|
303
|
+
|
304
|
+
def _build_attributes(attributes, buffer:)
|
305
|
+
attributes.each do |k, v|
|
306
|
+
next unless v
|
307
|
+
|
308
|
+
name = case k
|
309
|
+
when String
|
310
|
+
k
|
311
|
+
when Symbol
|
312
|
+
k.name.tr("_", "-")
|
313
|
+
else
|
314
|
+
k.to_s
|
315
|
+
end
|
316
|
+
|
317
|
+
if HTML::EVENT_ATTRIBUTES[name] || name.match?(/[<>&"']/)
|
318
|
+
raise ArgumentError, "Unsafe attribute name detected: #{k}."
|
319
|
+
end
|
320
|
+
|
321
|
+
case v
|
322
|
+
when true
|
323
|
+
buffer << " " << name
|
324
|
+
when String
|
325
|
+
buffer << " " << name << '="' << Hescape.escape_html(v) << '"'
|
326
|
+
when Symbol
|
327
|
+
buffer << " " << name << '="' << Hescape.escape_html(v.name) << '"'
|
328
|
+
when Hash
|
329
|
+
_build_attributes(v.transform_keys { "#{k}-#{_1.name.tr('_', '-')}" }, buffer: buffer)
|
330
|
+
else
|
331
|
+
buffer << " " << name << '="' << Hescape.escape_html(v.to_s) << '"'
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
buffer
|
175
336
|
end
|
176
337
|
end
|
177
338
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "markly"
|
4
|
+
|
5
|
+
module Phlex
|
6
|
+
class Markdown < Phlex::HTML
|
7
|
+
def initialize(content)
|
8
|
+
@content = content
|
9
|
+
end
|
10
|
+
|
11
|
+
def template
|
12
|
+
visit(doc)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def doc
|
18
|
+
Markly.parse(@content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit(node)
|
22
|
+
return if node.nil?
|
23
|
+
|
24
|
+
case node.type
|
25
|
+
in :document | :softbreak
|
26
|
+
visit_children(node)
|
27
|
+
in :text
|
28
|
+
text(node.string_content)
|
29
|
+
in :header
|
30
|
+
case node.header_level
|
31
|
+
in 1 then h1 { visit_children(node) }
|
32
|
+
in 2 then h2 { visit_children(node) }
|
33
|
+
in 3 then h3 { visit_children(node) }
|
34
|
+
in 4 then h4 { visit_children(node) }
|
35
|
+
in 5 then h5 { visit_children(node) }
|
36
|
+
in 6 then h6 { visit_children(node) }
|
37
|
+
end
|
38
|
+
in :paragraph
|
39
|
+
grandparent = node.parent&.parent
|
40
|
+
|
41
|
+
if grandparent&.type == :list && grandparent&.list_tight
|
42
|
+
visit_children(node)
|
43
|
+
else
|
44
|
+
p { visit_children(node) }
|
45
|
+
end
|
46
|
+
in :link
|
47
|
+
a(href: node.url) { visit_children(node) }
|
48
|
+
in :emph
|
49
|
+
em { visit_children(node) }
|
50
|
+
in :strong
|
51
|
+
strong { visit_children(node) }
|
52
|
+
in :list
|
53
|
+
case node.list_type
|
54
|
+
in :ordered_list then ol { visit_children(node) }
|
55
|
+
in :bullet_list then ul { visit_children(node) }
|
56
|
+
end
|
57
|
+
in :list_item
|
58
|
+
li { visit_children(node) }
|
59
|
+
in :code
|
60
|
+
whitespace { code { text(node.string_content) } }
|
61
|
+
in :code_block
|
62
|
+
code_block(node.string_content, language: node.fence_info) do |**attributes|
|
63
|
+
pre(**attributes) { text(node.string_content) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def code_block(code, language:)
|
69
|
+
yield
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_children(node)
|
73
|
+
node.each { |c| visit(c) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex
|
4
|
+
module Rails
|
5
|
+
class Form < Phlex::HTML
|
6
|
+
def initialize(model)
|
7
|
+
@model = model
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.input_field(method_name, type:)
|
11
|
+
define_method method_name do |field, value: @model.attributes[field.to_s], **attributes|
|
12
|
+
input(
|
13
|
+
name: field_name(field),
|
14
|
+
type: type,
|
15
|
+
value: value,
|
16
|
+
**attributes
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def template(&block)
|
22
|
+
form action: @url, method: @method do
|
23
|
+
authenticity_token_field
|
24
|
+
yield_content(&block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def authenticity_token_field
|
29
|
+
input(
|
30
|
+
name: "authenticity_token",
|
31
|
+
type: "hidden",
|
32
|
+
value: @_view_context.form_authenticity_token
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def submit(value)
|
37
|
+
input(
|
38
|
+
name: "commit",
|
39
|
+
type: "submit",
|
40
|
+
value: value
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def url
|
45
|
+
@_view_context.url_for(@model)
|
46
|
+
end
|
47
|
+
|
48
|
+
def field_name(*field)
|
49
|
+
@_view_context.field_name(ActiveModel::Naming.param_key(@model.class), *field)
|
50
|
+
end
|
51
|
+
|
52
|
+
input_field :url_field, type: "url"
|
53
|
+
input_field :text_field, type: "text"
|
54
|
+
input_field :date_field, type: "date"
|
55
|
+
input_field :time_field, type: "time"
|
56
|
+
input_field :week_field, type: "week"
|
57
|
+
input_field :month_field, type: "month"
|
58
|
+
input_field :email_field, type: "email"
|
59
|
+
input_field :color_field, type: "color"
|
60
|
+
input_field :hidden_field, type: "hidden"
|
61
|
+
input_field :search_field, type: "search"
|
62
|
+
input_field :password_field, type: "password"
|
63
|
+
input_field :telephone_field, type: "tel"
|
64
|
+
input_field :datetime_local_field, type: "datetime-local"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/phlex/rails/helpers.rb
CHANGED
@@ -28,11 +28,48 @@ module Phlex
|
|
28
28
|
end
|
29
29
|
|
30
30
|
module FormWith
|
31
|
+
class BufferedFormWith < Phlex::Buffered
|
32
|
+
alias_method :check_box, :__output_method__
|
33
|
+
alias_method :collection_check_boxes, :__output_method__
|
34
|
+
alias_method :collection_radio_buttons, :__output_method__
|
35
|
+
alias_method :collection_select, :__output_method__
|
36
|
+
alias_method :color_field, :__output_method__
|
37
|
+
alias_method :date_field, :__output_method__
|
38
|
+
alias_method :date_select, :__output_method__
|
39
|
+
alias_method :datetime_field, :__output_method__
|
40
|
+
alias_method :datetime_local_field, :__output_method__
|
41
|
+
alias_method :datetime_select, :__output_method__
|
42
|
+
alias_method :email_field, :__output_method__
|
43
|
+
alias_method :file_field, :__output_method__
|
44
|
+
alias_method :grouped_collection_select, :__output_method__
|
45
|
+
alias_method :hidden_field, :__output_method__
|
46
|
+
alias_method :label, :__output_method__
|
47
|
+
alias_method :month_field, :__output_method__
|
48
|
+
alias_method :number_field, :__output_method__
|
49
|
+
alias_method :password_field, :__output_method__
|
50
|
+
alias_method :phone_field, :__output_method__
|
51
|
+
alias_method :radio_button, :__output_method__
|
52
|
+
alias_method :range_field, :__output_method__
|
53
|
+
alias_method :search_field, :__output_method__
|
54
|
+
alias_method :select, :__output_method__
|
55
|
+
alias_method :submit, :__output_method__
|
56
|
+
alias_method :telephone_field, :__output_method__
|
57
|
+
alias_method :text_area, :__output_method__
|
58
|
+
alias_method :text_field, :__output_method__
|
59
|
+
alias_method :time_field, :__output_method__
|
60
|
+
alias_method :time_select, :__output_method__
|
61
|
+
alias_method :time_zone_select, :__output_method__
|
62
|
+
alias_method :url_field, :__output_method__
|
63
|
+
alias_method :week_field, :__output_method__
|
64
|
+
alias_method :weekday_select, :__output_method__
|
65
|
+
alias_method :button, :__output_method__
|
66
|
+
end
|
67
|
+
|
31
68
|
def form_with(*args, **kwargs, &block)
|
32
|
-
|
69
|
+
@_target << @_view_context.form_with(*args, **kwargs) { |form|
|
33
70
|
capture do
|
34
71
|
yield(
|
35
|
-
|
72
|
+
BufferedFormWith.new(form, buffer: @_target)
|
36
73
|
)
|
37
74
|
end
|
38
75
|
}
|
data/lib/phlex/rails.rb
ADDED
data/lib/phlex/renderable.rb
CHANGED
@@ -3,13 +3,13 @@
|
|
3
3
|
module Phlex
|
4
4
|
module Renderable
|
5
5
|
def render(renderable, *args, **kwargs, &block)
|
6
|
-
if renderable.is_a?(
|
6
|
+
if renderable.is_a?(HTML)
|
7
7
|
if block_given? && !block.binding.receiver.is_a?(Phlex::Block)
|
8
8
|
block = Phlex::Block.new(self, &block)
|
9
9
|
end
|
10
10
|
|
11
11
|
renderable.call(@_target, view_context: @_view_context, parent: self, &block)
|
12
|
-
elsif renderable.is_a?(Class) && renderable <
|
12
|
+
elsif renderable.is_a?(Class) && renderable < Phlex::HTML
|
13
13
|
raise ArgumentError, "You tried to render the Phlex view class: #{renderable.name} but you probably meant to render an instance of that class instead."
|
14
14
|
else
|
15
15
|
@_target << @_view_context.render(renderable, *args, **kwargs, &block)
|
@@ -26,7 +26,13 @@ module Phlex
|
|
26
26
|
output = yield(*args, **kwargs)
|
27
27
|
unchanged = (original_length == @_target.length)
|
28
28
|
|
29
|
-
|
29
|
+
if unchanged
|
30
|
+
if defined?(ActiveSupport::SafeBuffer) && output.is_a?(ActiveSupport::SafeBuffer)
|
31
|
+
unsafe_raw(output)
|
32
|
+
else
|
33
|
+
text(output)
|
34
|
+
end
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end.html_safe
|
32
38
|
else
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "capybara"
|
4
|
+
require_relative "view_helper"
|
5
|
+
|
6
|
+
module Phlex::Testing
|
7
|
+
module Capybara
|
8
|
+
module ViewHelper
|
9
|
+
include Phlex::Testing::ViewHelper
|
10
|
+
|
11
|
+
def self.included(klass)
|
12
|
+
if defined?(Minitest::Test) && klass < Minitest::Test
|
13
|
+
require "capybara/minitest"
|
14
|
+
include ::Capybara::Minitest::Assertions
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :page
|
19
|
+
|
20
|
+
def render(view, &block)
|
21
|
+
@page = ::Capybara::Node::Simple.new(super)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
require_relative "view_helper"
|
5
|
+
|
6
|
+
module Phlex::Testing
|
7
|
+
module Nokogiri
|
8
|
+
module DocumentHelper
|
9
|
+
include Phlex::Testing::ViewHelper
|
10
|
+
|
11
|
+
def render(view, &block)
|
12
|
+
::Nokogiri::HTML5(super)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module FragmentHelper
|
17
|
+
include Phlex::Testing::ViewHelper
|
18
|
+
|
19
|
+
def render(view, &block)
|
20
|
+
::Nokogiri::HTML5.fragment(super)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "view_helper"
|
4
|
+
|
5
|
+
module Phlex::Testing
|
6
|
+
module Rails
|
7
|
+
module ViewHelper
|
8
|
+
include Phlex::Testing::ViewHelper
|
9
|
+
|
10
|
+
def view_context
|
11
|
+
controller.view_context
|
12
|
+
end
|
13
|
+
|
14
|
+
def controller
|
15
|
+
@controller ||= ActionView::TestCase::TestController.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex
|
4
|
+
module Translation
|
5
|
+
def self.included(view)
|
6
|
+
view.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
attr_writer :translation_path
|
11
|
+
|
12
|
+
def translation_path
|
13
|
+
@translation_path ||= name&.split("::")&.join(".")&.downcase.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def translate(key, **options)
|
18
|
+
key = "#{self.class.translation_path}#{key}" if key.start_with?(".")
|
19
|
+
|
20
|
+
::I18n.translate(key, **options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex
|
4
|
+
module Turbo
|
5
|
+
class Frame < Phlex::HTML
|
6
|
+
register_element :turbo_frame
|
7
|
+
|
8
|
+
def initialize(src:, loading:, disabled:, target:, autoscroll:)
|
9
|
+
@src = src
|
10
|
+
@loading = loading
|
11
|
+
@disabled = disabled
|
12
|
+
@target = target
|
13
|
+
@autoscroll = autoscroll
|
14
|
+
end
|
15
|
+
|
16
|
+
def template(&content)
|
17
|
+
turbo_frame(src: @src, loading: @loading, disabled: @disabled, target: @target, autoscroll: @autoscroll, &content)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex
|
4
|
+
module Turbo
|
5
|
+
class Stream < Phlex::HTML
|
6
|
+
register_element :turbo_stream
|
7
|
+
|
8
|
+
def initialize(action:, **attributes)
|
9
|
+
@action = action
|
10
|
+
@attributes = attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
def template(&content)
|
14
|
+
turbo_stream(action: @action, **@attributes, &content)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/phlex/version.rb
CHANGED