phlex 1.1.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e06bb8d47be22009079c466771304f5e8cd413be8de6e3fb57eb0b64cc84037
4
- data.tar.gz: 45a39c4ea77541549ce3e913875a3400c5abf247c5f9c72fa4d9002b7ea1186c
3
+ metadata.gz: f1a287ded1bcdc949fef4ca4ae24eb7870a83f9eb90c02d1cdd04152e1606ea1
4
+ data.tar.gz: 0aa47c95c2a46aacf2845f8bd58af88683b5283554c8f0e90d0199386505ef14
5
5
  SHA512:
6
- metadata.gz: cb771c7f3ff0d11e513f3c7e70d6783dc7a7f40b19611f961c56b39ef22412479a55333d5a3432ee5564a8f102377122f4a43529a107adffbc82da17dfc4bdc0
7
- data.tar.gz: 8e2671c58f64d9da93581e2c5bdab188eea8f5f468d0327ae50a264d7c3297c8f8dbeadae0c59f8cce05e2f3446d178ce8d2c0a18eb82bf5e922ded811a1fd2b
6
+ metadata.gz: 9cc696d354b00376956a4574c27f00851c3ad0b0598132dcb301cccf65a9b5d95cf35adc2032afb47b53ff9f52899090b3657113d4d8483e3d410efdf19478e0
7
+ data.tar.gz: 9cd0b3c7a746f58faaf77bcd0502fb96721889622e6cc1bf0e7b9e2f97f859bb8562fd800609ee5d964f9b1cee39e31c2c66c72ff0fba93653c6d4fc6ac85bc4
data/.rubocop.yml CHANGED
@@ -14,7 +14,8 @@ Layout/CaseIndentation:
14
14
  Style/StringConcatenation:
15
15
  Enabled: false
16
16
 
17
-
17
+ Style/CaseEquality:
18
+ Enabled: false
18
19
 
19
20
  Security/Eval:
20
21
  Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.0
1
+ 3.2.0
data/Gemfile CHANGED
@@ -7,7 +7,6 @@ gemspec
7
7
 
8
8
  gem "rubocop"
9
9
  gem "sus"
10
- gem "syntax_suggest"
11
10
  gem "zeitwerk"
12
11
  gem "benchmark-ips"
13
12
  gem "erb"
data/README.md CHANGED
@@ -42,3 +42,4 @@ Thanks [Logology](https://www.logology.co) for sponsoring our logo.
42
42
  - [tubby](https://github.com/judofyr/tubby)
43
43
  - [hoshi](https://github.com/pete/hoshi)
44
44
  - [hyperstack](https://github.com/hyperstack-org/hyperstack)
45
+ - [clearwater](https://github.com/clearwater-rb/clearwater)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module BlackHole
5
+ extend self
6
+
7
+ def <<(anything)
8
+ self
9
+ end
10
+
11
+ def length
12
+ 0
13
+ end
14
+ end
15
+ end
@@ -10,8 +10,8 @@ module Phlex
10
10
  end
11
11
 
12
12
  # Alias output methods to this
13
- def __output_method__(*args, **kwargs, &block)
14
- output = __getobj__.public_send(__callee__, *args, **kwargs, &block)
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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phlex
4
+ module DeferredRender
5
+ include Experimental
6
+ end
7
+ end
@@ -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] || _attributes(**attributes)) << ">"
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] || _attributes(**attributes)) << "></#{tag}>"
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] || _attributes(**attributes)) << ">"
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
@@ -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 and subject to change."
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 _append_token(tokens, token[:then])
19
- else _append_token(tokens, token)
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 _append_token(tokens, token[:else])
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 _append_token(tokens, token)
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,6 +131,21 @@ 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)
@@ -138,15 +153,22 @@ module Phlex
138
153
  @_view_context = view_context
139
154
  @_parent = parent
140
155
 
156
+ block ||= @_content_block
157
+
141
158
  return buffer unless render?
142
159
 
143
160
  around_template do
144
- if block_given?
145
- template do |*args|
146
- if args.length > 0
147
- yield_content_with_args(*args, &block)
148
- else
149
- yield_content(&block)
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
@@ -159,7 +181,7 @@ module Phlex
159
181
  buffer
160
182
  end
161
183
 
162
- def render(renderable, *args, **kwargs, &block)
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, Float then content.to_s
216
+ when Integer then content.to_s
195
217
  else format_object(content) || content.to_s
196
218
  end
197
219
  )
@@ -223,13 +245,13 @@ module Phlex
223
245
  nil
224
246
  end
225
247
 
226
- def unsafe_raw(content = nil, &block)
248
+ def unsafe_raw(content = nil)
227
249
  return nil unless content
228
250
 
229
251
  @_target << content
230
252
  end
231
253
 
232
- def capture(&block)
254
+ def capture
233
255
  return unless block_given?
234
256
 
235
257
  original_buffer = @_target
@@ -238,9 +260,23 @@ module Phlex
238
260
 
239
261
  yield
240
262
 
263
+ new_buffer
264
+ ensure
241
265
  @_target = original_buffer
266
+ end
242
267
 
243
- new_buffer
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
244
280
  end
245
281
 
246
282
  # Default render predicate can be overridden to prevent rendering
@@ -248,8 +284,11 @@ module Phlex
248
284
  true
249
285
  end
250
286
 
251
- private def format_object(oject)
252
- nil
287
+ private def format_object(object)
288
+ case object
289
+ when Float
290
+ object.to_s
291
+ end
253
292
  end
254
293
 
255
294
  private def around_template
@@ -266,7 +305,7 @@ module Phlex
266
305
  nil
267
306
  end
268
307
 
269
- private def yield_content(&block)
308
+ private def yield_content
270
309
  return unless block_given?
271
310
 
272
311
  original_length = @_target.length
@@ -279,7 +318,7 @@ module Phlex
279
318
  @_target << ERB::Util.html_escape(content)
280
319
  when Symbol
281
320
  @_target << ERB::Util.html_escape(content.name)
282
- when Integer, Float
321
+ when Integer
283
322
  @_target << ERB::Util.html_escape(content.to_s)
284
323
  else
285
324
  if (formatted_object = format_object(content))
@@ -291,7 +330,7 @@ module Phlex
291
330
  nil
292
331
  end
293
332
 
294
- private def yield_content_with_args(*args, &block)
333
+ private def yield_content_with_args(*args)
295
334
  return unless block_given?
296
335
 
297
336
  original_length = @_target.length
@@ -316,9 +355,13 @@ module Phlex
316
355
  nil
317
356
  end
318
357
 
319
- private def _attributes(**attributes)
358
+ private def __attributes__(**attributes)
359
+ if attributes[:href]&.start_with?(/\s*javascript/)
360
+ attributes[:href] = attributes[:href].sub(/^\s*(javascript:)+/, "")
361
+ end
362
+
320
363
  buffer = +""
321
- _build_attributes(attributes, buffer: buffer)
364
+ __build_attributes__(attributes, buffer: buffer)
322
365
 
323
366
  unless self.class.rendered_at_least_once
324
367
  Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
@@ -327,7 +370,7 @@ module Phlex
327
370
  buffer
328
371
  end
329
372
 
330
- private def _build_attributes(attributes, buffer:)
373
+ private def __build_attributes__(attributes, buffer:)
331
374
  attributes.each do |k, v|
332
375
  next unless v
333
376
 
@@ -337,11 +380,8 @@ module Phlex
337
380
  else k.to_s
338
381
  end
339
382
 
340
- lower_name = name.downcase
341
- next if lower_name == "href" && v.start_with?(/\s*javascript:/i)
342
-
343
383
  # Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters.
344
- if HTML::EVENT_ATTRIBUTES[lower_name] || name.match?(/[<>&"']/)
384
+ if HTML::EVENT_ATTRIBUTES[name] || name.match?(/[<>&"']/)
345
385
  raise ArgumentError, "Unsafe attribute name detected: #{k}."
346
386
  end
347
387
 
@@ -353,7 +393,7 @@ module Phlex
353
393
  when Symbol
354
394
  buffer << " " << name << '="' << ERB::Util.html_escape(v.name) << '"'
355
395
  when Hash
356
- _build_attributes(
396
+ __build_attributes__(
357
397
  v.transform_keys { |subkey|
358
398
  case subkey
359
399
  when Symbol then"#{k}-#{subkey.name.tr('_', '-')}"
@@ -368,5 +408,14 @@ module Phlex
368
408
 
369
409
  buffer
370
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
371
420
  end
372
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- VERSION = "1.1.1"
4
+ VERSION = "1.2.0"
5
5
  end
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.1.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: 2024-03-11 00:00:00.000000000 Z
11
+ date: 2023-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -47,18 +47,19 @@ files:
47
47
  - fixtures/view_helper.rb
48
48
  - lib/overrides/symbol/name.rb
49
49
  - lib/phlex.rb
50
+ - lib/phlex/black_hole.rb
50
51
  - lib/phlex/buffered.rb
51
52
  - lib/phlex/callable.rb
52
53
  - lib/phlex/collection.rb
53
54
  - lib/phlex/configuration.rb
55
+ - lib/phlex/deferred_render.rb
54
56
  - lib/phlex/elements.rb
55
57
  - lib/phlex/experimental.rb
56
58
  - lib/phlex/helpers.rb
57
59
  - lib/phlex/html.rb
58
60
  - lib/phlex/table.rb
59
61
  - lib/phlex/testing/view_helper.rb
60
- - lib/phlex/turbo/frame.rb
61
- - lib/phlex/turbo/stream.rb
62
+ - lib/phlex/unbuffered.rb
62
63
  - lib/phlex/version.rb
63
64
  - phlex_logo.png
64
65
  - sig/phlex.rbs
@@ -86,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  - !ruby/object:Gem::Version
87
88
  version: '0'
88
89
  requirements: []
89
- rubygems_version: 3.5.6
90
+ rubygems_version: 3.4.4
90
91
  signing_key:
91
92
  specification_version: 4
92
93
  summary: A framework for building views in Ruby.
@@ -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
@@ -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