phlex 1.0.0 → 1.2.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/.rubocop.yml +2 -1
- data/.ruby-version +1 -0
- data/Gemfile +0 -1
- data/README.md +18 -0
- data/lib/phlex/black_hole.rb +15 -0
- data/lib/phlex/buffered.rb +2 -2
- data/lib/phlex/deferred_render.rb +7 -0
- data/lib/phlex/elements.rb +7 -3
- data/lib/phlex/experimental.rb +1 -1
- data/lib/phlex/helpers.rb +4 -4
- data/lib/phlex/html.rb +81 -25
- data/lib/phlex/unbuffered.rb +60 -0
- data/lib/phlex/version.rb +1 -1
- metadata +7 -5
- data/lib/phlex/turbo/frame.rb +0 -23
- data/lib/phlex/turbo/stream.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1a287ded1bcdc949fef4ca4ae24eb7870a83f9eb90c02d1cdd04152e1606ea1
|
4
|
+
data.tar.gz: 0aa47c95c2a46aacf2845f8bd58af88683b5283554c8f0e90d0199386505ef14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cc696d354b00376956a4574c27f00851c3ad0b0598132dcb301cccf65a9b5d95cf35adc2032afb47b53ff9f52899090b3657113d4d8483e3d410efdf19478e0
|
7
|
+
data.tar.gz: 9cd0b3c7a746f58faaf77bcd0502fb96721889622e6cc1bf0e7b9e2f97f859bb8562fd800609ee5d964f9b1cee39e31c2c66c72ff0fba93653c6d4fc6ac85bc4
|
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -14,6 +14,12 @@ If you run into any trouble, please [start a discussion](https://github.com/joel
|
|
14
14
|
|
15
15
|
Everyone interacting in Phlex codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/joeldrapper/phlex/blob/main/CODE_OF_CONDUCT.md).
|
16
16
|
|
17
|
+
### Who uses Phlex?
|
18
|
+
|
19
|
+
- [Clearscope](https://www.clearscope.io)
|
20
|
+
|
21
|
+
*If you can share that your company uses Phlex in production, please open a PR to list it here.*
|
22
|
+
|
17
23
|
### Sponsorship 💖
|
18
24
|
|
19
25
|
Maintaining a library is a lot of work. If your company benefits from this work or is likely to benefit from it in the future, please consider [sponsorship](https://github.com/sponsors/joeldrapper). Phlex is actively developed and maintained by **[Joel Drapper](https://github.com/sponsors/joeldrapper)**.
|
@@ -25,3 +31,15 @@ If you’ve found a potential security issue, please email [security@phlex.fun](
|
|
25
31
|
### Thanks 🙏
|
26
32
|
|
27
33
|
Thanks [Logology](https://www.logology.co) for sponsoring our logo.
|
34
|
+
|
35
|
+
### Prior Art 🎨
|
36
|
+
|
37
|
+
- [markaby](https://github.com/markaby/markaby)
|
38
|
+
- [erector](https://github.com/erector/erector)
|
39
|
+
- [papercraft](https://github.com/digital-fabric/papercraft)
|
40
|
+
- [matestack](https://github.com/matestack/matestack-ui-core)
|
41
|
+
- [arbre](https://github.com/activeadmin/arbre)
|
42
|
+
- [tubby](https://github.com/judofyr/tubby)
|
43
|
+
- [hoshi](https://github.com/pete/hoshi)
|
44
|
+
- [hyperstack](https://github.com/hyperstack-org/hyperstack)
|
45
|
+
- [clearwater](https://github.com/clearwater-rb/clearwater)
|
data/lib/phlex/buffered.rb
CHANGED
@@ -10,8 +10,8 @@ module Phlex
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Alias output methods to this
|
13
|
-
def __output_method__(
|
14
|
-
output = __getobj__.public_send(__callee__,
|
13
|
+
def __output_method__(...)
|
14
|
+
output = __getobj__.public_send(__callee__, ...)
|
15
15
|
@buffer << output if output.is_a? String
|
16
16
|
nil
|
17
17
|
end
|
data/lib/phlex/elements.rb
CHANGED
@@ -13,11 +13,11 @@ module Phlex
|
|
13
13
|
def #{element}(**attributes, &block)
|
14
14
|
if attributes.length > 0
|
15
15
|
if block_given?
|
16
|
-
@_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] ||
|
16
|
+
@_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << ">"
|
17
17
|
yield_content(&block)
|
18
18
|
@_target << "</#{tag}>"
|
19
19
|
else
|
20
|
-
@_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] ||
|
20
|
+
@_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
|
21
21
|
end
|
22
22
|
else
|
23
23
|
if block_given?
|
@@ -31,6 +31,8 @@ module Phlex
|
|
31
31
|
|
32
32
|
nil
|
33
33
|
end
|
34
|
+
|
35
|
+
alias_method :_#{element}, :#{element}
|
34
36
|
RUBY
|
35
37
|
end
|
36
38
|
|
@@ -40,13 +42,15 @@ module Phlex
|
|
40
42
|
|
41
43
|
def #{element}(**attributes)
|
42
44
|
if attributes.length > 0
|
43
|
-
@_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] ||
|
45
|
+
@_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes.hash] || __attributes__(**attributes)) << ">"
|
44
46
|
else
|
45
47
|
@_target << "<#{tag}>"
|
46
48
|
end
|
47
49
|
|
48
50
|
nil
|
49
51
|
end
|
52
|
+
|
53
|
+
alias_method :_#{element}, :#{element}
|
50
54
|
RUBY
|
51
55
|
end
|
52
56
|
end
|
data/lib/phlex/experimental.rb
CHANGED
@@ -4,7 +4,7 @@ module Phlex
|
|
4
4
|
module Experimental
|
5
5
|
def before_template
|
6
6
|
if Phlex.configuration.experimental_warnings
|
7
|
-
puts "Warning: #{self.class.name} is experimental
|
7
|
+
puts "Warning: #{self.class.name} is using experimental Phlex features that are subject to change."
|
8
8
|
end
|
9
9
|
|
10
10
|
super
|
data/lib/phlex/helpers.rb
CHANGED
@@ -15,12 +15,12 @@ module Phlex::Helpers
|
|
15
15
|
|
16
16
|
if truthy
|
17
17
|
case token
|
18
|
-
when Hash then
|
19
|
-
else
|
18
|
+
when Hash then __append_token__(tokens, token[:then])
|
19
|
+
else __append_token__(tokens, token)
|
20
20
|
end
|
21
21
|
else
|
22
22
|
case token
|
23
|
-
when Hash then
|
23
|
+
when Hash then __append_token__(tokens, token[:else])
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -28,7 +28,7 @@ module Phlex::Helpers
|
|
28
28
|
tokens.join(" ")
|
29
29
|
end
|
30
30
|
|
31
|
-
private def
|
31
|
+
private def __append_token__(tokens, token)
|
32
32
|
case token
|
33
33
|
when nil then nil
|
34
34
|
when String then tokens << token
|
data/lib/phlex/html.rb
CHANGED
@@ -131,22 +131,44 @@ module Phlex
|
|
131
131
|
|
132
132
|
class << self
|
133
133
|
attr_accessor :rendered_at_least_once
|
134
|
+
|
135
|
+
def call(...)
|
136
|
+
new(...).call
|
137
|
+
end
|
138
|
+
alias_method :render, :call
|
139
|
+
|
140
|
+
def new(*args, **kwargs, &block)
|
141
|
+
if block
|
142
|
+
object = super(*args, **kwargs, &nil)
|
143
|
+
object.instance_variable_set(:@_content_block, block)
|
144
|
+
object
|
145
|
+
else
|
146
|
+
super
|
147
|
+
end
|
148
|
+
end
|
134
149
|
end
|
135
150
|
|
136
151
|
def call(buffer = +"", view_context: nil, parent: nil, &block)
|
137
|
-
return buffer unless render?
|
138
|
-
|
139
152
|
@_target = buffer
|
140
153
|
@_view_context = view_context
|
141
154
|
@_parent = parent
|
142
155
|
|
156
|
+
block ||= @_content_block
|
157
|
+
|
158
|
+
return buffer unless render?
|
159
|
+
|
143
160
|
around_template do
|
144
|
-
if
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
161
|
+
if block
|
162
|
+
if DeferredRender === self
|
163
|
+
__vanish__(self, &block)
|
164
|
+
template
|
165
|
+
else
|
166
|
+
template do |*args|
|
167
|
+
if args.length > 0
|
168
|
+
yield_content_with_args(*args, &block)
|
169
|
+
else
|
170
|
+
yield_content(&block)
|
171
|
+
end
|
150
172
|
end
|
151
173
|
end
|
152
174
|
else
|
@@ -154,12 +176,12 @@ module Phlex
|
|
154
176
|
end
|
155
177
|
end
|
156
178
|
|
157
|
-
self.class.rendered_at_least_once
|
179
|
+
self.class.rendered_at_least_once = true
|
158
180
|
|
159
181
|
buffer
|
160
182
|
end
|
161
183
|
|
162
|
-
def render(renderable,
|
184
|
+
def render(renderable, &block)
|
163
185
|
case renderable
|
164
186
|
when Phlex::HTML
|
165
187
|
renderable.call(@_target, view_context: @_view_context, parent: self, &block)
|
@@ -191,7 +213,7 @@ module Phlex
|
|
191
213
|
case content
|
192
214
|
when String then content
|
193
215
|
when Symbol then content.name
|
194
|
-
when Integer
|
216
|
+
when Integer then content.to_s
|
195
217
|
else format_object(content) || content.to_s
|
196
218
|
end
|
197
219
|
)
|
@@ -223,12 +245,13 @@ module Phlex
|
|
223
245
|
nil
|
224
246
|
end
|
225
247
|
|
226
|
-
def unsafe_raw(content = nil
|
227
|
-
|
228
|
-
|
248
|
+
def unsafe_raw(content = nil)
|
249
|
+
return nil unless content
|
250
|
+
|
251
|
+
@_target << content
|
229
252
|
end
|
230
253
|
|
231
|
-
def capture
|
254
|
+
def capture
|
232
255
|
return unless block_given?
|
233
256
|
|
234
257
|
original_buffer = @_target
|
@@ -237,9 +260,23 @@ module Phlex
|
|
237
260
|
|
238
261
|
yield
|
239
262
|
|
263
|
+
new_buffer
|
264
|
+
ensure
|
240
265
|
@_target = original_buffer
|
266
|
+
end
|
241
267
|
|
242
|
-
|
268
|
+
# Like `capture` but the output is vanished into a BlackHole buffer.
|
269
|
+
# Becuase the BlackHole does nothing with the output, this should be faster.
|
270
|
+
def __vanish__(*args)
|
271
|
+
return unless block_given?
|
272
|
+
|
273
|
+
original_buffer = @_target
|
274
|
+
@_target = BlackHole
|
275
|
+
|
276
|
+
yield(*args)
|
277
|
+
nil
|
278
|
+
ensure
|
279
|
+
@_target = original_buffer
|
243
280
|
end
|
244
281
|
|
245
282
|
# Default render predicate can be overridden to prevent rendering
|
@@ -247,8 +284,11 @@ module Phlex
|
|
247
284
|
true
|
248
285
|
end
|
249
286
|
|
250
|
-
private def format_object(
|
251
|
-
|
287
|
+
private def format_object(object)
|
288
|
+
case object
|
289
|
+
when Float
|
290
|
+
object.to_s
|
291
|
+
end
|
252
292
|
end
|
253
293
|
|
254
294
|
private def around_template
|
@@ -265,7 +305,7 @@ module Phlex
|
|
265
305
|
nil
|
266
306
|
end
|
267
307
|
|
268
|
-
private def yield_content
|
308
|
+
private def yield_content
|
269
309
|
return unless block_given?
|
270
310
|
|
271
311
|
original_length = @_target.length
|
@@ -278,7 +318,7 @@ module Phlex
|
|
278
318
|
@_target << ERB::Util.html_escape(content)
|
279
319
|
when Symbol
|
280
320
|
@_target << ERB::Util.html_escape(content.name)
|
281
|
-
when Integer
|
321
|
+
when Integer
|
282
322
|
@_target << ERB::Util.html_escape(content.to_s)
|
283
323
|
else
|
284
324
|
if (formatted_object = format_object(content))
|
@@ -290,7 +330,7 @@ module Phlex
|
|
290
330
|
nil
|
291
331
|
end
|
292
332
|
|
293
|
-
private def yield_content_with_args(*args
|
333
|
+
private def yield_content_with_args(*args)
|
294
334
|
return unless block_given?
|
295
335
|
|
296
336
|
original_length = @_target.length
|
@@ -315,13 +355,13 @@ module Phlex
|
|
315
355
|
nil
|
316
356
|
end
|
317
357
|
|
318
|
-
private def
|
358
|
+
private def __attributes__(**attributes)
|
319
359
|
if attributes[:href]&.start_with?(/\s*javascript/)
|
320
360
|
attributes[:href] = attributes[:href].sub(/^\s*(javascript:)+/, "")
|
321
361
|
end
|
322
362
|
|
323
363
|
buffer = +""
|
324
|
-
|
364
|
+
__build_attributes__(attributes, buffer: buffer)
|
325
365
|
|
326
366
|
unless self.class.rendered_at_least_once
|
327
367
|
Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
|
@@ -330,7 +370,7 @@ module Phlex
|
|
330
370
|
buffer
|
331
371
|
end
|
332
372
|
|
333
|
-
private def
|
373
|
+
private def __build_attributes__(attributes, buffer:)
|
334
374
|
attributes.each do |k, v|
|
335
375
|
next unless v
|
336
376
|
|
@@ -353,7 +393,14 @@ module Phlex
|
|
353
393
|
when Symbol
|
354
394
|
buffer << " " << name << '="' << ERB::Util.html_escape(v.name) << '"'
|
355
395
|
when Hash
|
356
|
-
|
396
|
+
__build_attributes__(
|
397
|
+
v.transform_keys { |subkey|
|
398
|
+
case subkey
|
399
|
+
when Symbol then"#{k}-#{subkey.name.tr('_', '-')}"
|
400
|
+
else "#{k}-#{subkey}"
|
401
|
+
end
|
402
|
+
}, buffer: buffer
|
403
|
+
)
|
357
404
|
else
|
358
405
|
buffer << " " << name << '="' << ERB::Util.html_escape(v.to_s) << '"'
|
359
406
|
end
|
@@ -361,5 +408,14 @@ module Phlex
|
|
361
408
|
|
362
409
|
buffer
|
363
410
|
end
|
411
|
+
|
412
|
+
# This should be the last method defined
|
413
|
+
def self.method_added(method_name)
|
414
|
+
if method_name[0] == "_" && Phlex::HTML.instance_methods.include?(method_name) && instance_method(method_name).owner != Phlex::HTML
|
415
|
+
raise NameError, "👋 Redefining the method `#{name}##{method_name}` is not a good idea."
|
416
|
+
end
|
417
|
+
|
418
|
+
super
|
419
|
+
end
|
364
420
|
end
|
365
421
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex
|
4
|
+
class Unbuffered < BasicObject
|
5
|
+
CACHE = {}
|
6
|
+
|
7
|
+
def self.call(object)
|
8
|
+
decorator = CACHE[object.class.name] ||= ::Class.new(self)
|
9
|
+
decorator.new(object)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(object)
|
13
|
+
@object = object
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
"Unbuffered(#{@object.class.name})[object: #{@object.inspect}]"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Borrow some important methods from Object
|
21
|
+
define_method :__class__,
|
22
|
+
::Object.instance_method(:class)
|
23
|
+
|
24
|
+
define_method :__public_send__,
|
25
|
+
::Object.instance_method(:public_send)
|
26
|
+
|
27
|
+
define_method :__callee__,
|
28
|
+
::Object.instance_method(:__callee__)
|
29
|
+
|
30
|
+
def respond_to_missing?(...)
|
31
|
+
@object.respond_to?(...)
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(name, *args, &block)
|
35
|
+
if @object.respond_to?(name)
|
36
|
+
# If the object responds to this method, we want to define it by aliasing the __output_method__.
|
37
|
+
__class__.alias_method(name, :__output_method__)
|
38
|
+
|
39
|
+
# Now we've defined this missing method, we can call it.
|
40
|
+
__public_send__(name, *args, &block)
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# This method is designed to be aliased and references the callee which is whatever alias you called.
|
47
|
+
def __output_method__(*args, &block)
|
48
|
+
@object.capture { @object.public_send(__callee__, *args, &block) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def __forward_method__(*args, &block)
|
52
|
+
@object.public_send(__callee__, *args, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Forward some methods to the original underlying method
|
56
|
+
alias_method :call, :__forward_method__
|
57
|
+
alias_method :send, :__forward_method__
|
58
|
+
alias_method :public_send, :__forward_method__
|
59
|
+
end
|
60
|
+
end
|
data/lib/phlex/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phlex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.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:
|
11
|
+
date: 2023-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -33,6 +33,7 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- ".editorconfig"
|
35
35
|
- ".rubocop.yml"
|
36
|
+
- ".ruby-version"
|
36
37
|
- CODE_OF_CONDUCT.md
|
37
38
|
- CONTRIBUTING.md
|
38
39
|
- Gemfile
|
@@ -46,18 +47,19 @@ files:
|
|
46
47
|
- fixtures/view_helper.rb
|
47
48
|
- lib/overrides/symbol/name.rb
|
48
49
|
- lib/phlex.rb
|
50
|
+
- lib/phlex/black_hole.rb
|
49
51
|
- lib/phlex/buffered.rb
|
50
52
|
- lib/phlex/callable.rb
|
51
53
|
- lib/phlex/collection.rb
|
52
54
|
- lib/phlex/configuration.rb
|
55
|
+
- lib/phlex/deferred_render.rb
|
53
56
|
- lib/phlex/elements.rb
|
54
57
|
- lib/phlex/experimental.rb
|
55
58
|
- lib/phlex/helpers.rb
|
56
59
|
- lib/phlex/html.rb
|
57
60
|
- lib/phlex/table.rb
|
58
61
|
- lib/phlex/testing/view_helper.rb
|
59
|
-
- lib/phlex/
|
60
|
-
- lib/phlex/turbo/stream.rb
|
62
|
+
- lib/phlex/unbuffered.rb
|
61
63
|
- lib/phlex/version.rb
|
62
64
|
- phlex_logo.png
|
63
65
|
- sig/phlex.rbs
|
@@ -85,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
87
|
- !ruby/object:Gem::Version
|
86
88
|
version: '0'
|
87
89
|
requirements: []
|
88
|
-
rubygems_version: 3.
|
90
|
+
rubygems_version: 3.4.4
|
89
91
|
signing_key:
|
90
92
|
specification_version: 4
|
91
93
|
summary: A framework for building views in Ruby.
|
data/lib/phlex/turbo/frame.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlex
|
4
|
-
module Turbo
|
5
|
-
class Frame < Phlex::HTML
|
6
|
-
include Experimental
|
7
|
-
|
8
|
-
register_element :turbo_frame
|
9
|
-
|
10
|
-
def initialize(src:, loading:, disabled:, target:, autoscroll:)
|
11
|
-
@src = src
|
12
|
-
@loading = loading
|
13
|
-
@disabled = disabled
|
14
|
-
@target = target
|
15
|
-
@autoscroll = autoscroll
|
16
|
-
end
|
17
|
-
|
18
|
-
def template(&content)
|
19
|
-
turbo_frame(src: @src, loading: @loading, disabled: @disabled, target: @target, autoscroll: @autoscroll, &content)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/phlex/turbo/stream.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Phlex
|
4
|
-
module Turbo
|
5
|
-
class Stream < Phlex::HTML
|
6
|
-
include Experimental
|
7
|
-
|
8
|
-
register_element :turbo_stream
|
9
|
-
|
10
|
-
def initialize(action:, **attributes)
|
11
|
-
@action = action
|
12
|
-
@attributes = attributes
|
13
|
-
end
|
14
|
-
|
15
|
-
def template(&content)
|
16
|
-
turbo_stream(action: @action, **@attributes, &content)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|