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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e074c926f013d613e2523b41c8dd604758d087c64d377b50a343c651b0164c0d
4
- data.tar.gz: 56019e811dff06ef053adfeb20160fe29c247ee2e2f37848eba1580d240c41fd
3
+ metadata.gz: f1a287ded1bcdc949fef4ca4ae24eb7870a83f9eb90c02d1cdd04152e1606ea1
4
+ data.tar.gz: 0aa47c95c2a46aacf2845f8bd58af88683b5283554c8f0e90d0199386505ef14
5
5
  SHA512:
6
- metadata.gz: 24489ae7f67415aa23ce258d6a121fe37418ce32e543888410b730821feeafd5fda6f164dd6f390f9dd4a0ec7a8a92ae5c50deb53376f95f987203ad4da2acf2
7
- data.tar.gz: 1ddfa0b6dff09c597cec5f77f521faf89344981e4f3ddd3237722d94cb4d4faeec0d385a7ef4a0a18acec770d7f22811ac489ae94dfdfc3fc92fbf1cbb676f20
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 ADDED
@@ -0,0 +1 @@
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
@@ -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)
@@ -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,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 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
@@ -154,12 +176,12 @@ module Phlex
154
176
  end
155
177
  end
156
178
 
157
- self.class.rendered_at_least_once ||= true
179
+ self.class.rendered_at_least_once = true
158
180
 
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,12 +245,13 @@ module Phlex
223
245
  nil
224
246
  end
225
247
 
226
- def unsafe_raw(content = nil, &block)
227
- @_target << (content || instance_exec(&block))
228
- nil
248
+ def unsafe_raw(content = nil)
249
+ return nil unless content
250
+
251
+ @_target << content
229
252
  end
230
253
 
231
- def capture(&block)
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
- 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
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(oject)
251
- nil
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(&block)
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, Float
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, &block)
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 _attributes(**attributes)
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
- _build_attributes(attributes, buffer: buffer)
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 _build_attributes(attributes, buffer:)
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
- _build_attributes(v.transform_keys { "#{k}-#{_1.name.tr('_', '-')}" }, buffer: buffer)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phlex
4
- VERSION = "1.0.0"
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.0.0
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: 2022-12-08 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
@@ -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/turbo/frame.rb
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.3.25
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.
@@ -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