phlex 1.2.1 β†’ 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d628d0e0b2f138923dd354f1b6b4ed47d7945d81bb95f8227978574d85d9a82f
4
- data.tar.gz: 909df1cd689153a3ea83c7200d7af5c01aa1af0bba3a569eec825f3dc1b049f2
3
+ metadata.gz: 9cd9fd26d33fcf92978a26d9d94b81485871fa3f54c98639eb4ba5932960df84
4
+ data.tar.gz: c8f0ded1ee9cd43c12e8358af687b7fe4f21e732031ca4fb009020dfaaeb72b5
5
5
  SHA512:
6
- metadata.gz: 78e83ee77668f41ed94742d45809f32a92ab8ecba22ea82ebdc2402d4ef52e2718ad157d723f730c2bc431ddc2f98749cdd5d5ed46b948bebf230c1cbf5adbe2
7
- data.tar.gz: e234afec1c0a77095082136f870c326c8f15ff3c6fdc8b3369f7d034c6bbc053d6be245f5753b36b5d8a6bafcbbb100a3e562ed4968960a4ba902475dd6c8e07
6
+ metadata.gz: 890ccb499d210d332f5b2213bfdce51bd1e2212c550b20b0d9de3d1698205ca45b82e86e7850dc037e07a43e3da70a011e27d2310618f203882a00b32750704d
7
+ data.tar.gz: fd9794d862a27ddaafa53c1f0d5fcecd3a967391083e2073922a2e83cbc19db7f95f4d19dd5db64b3c186e5c1c6292a6cef71dc05842c7216bd1e9b07046edc8
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <a href="https://www.phlex.fun"><img alt="Phlex logo" src="phlex_logo.png" width="180" /></a>
2
2
 
3
- Phlex is a framework that lets you compose web views in pure Ruby.
3
+ Phlex lets you compose web views in pure Ruby. It’s super-fast, thread-safe and supports TruffleRuby v22.2+, JRuby v9.2+ and MRI v2.7+.
4
4
 
5
5
  ### Documentation πŸ“—
6
6
 
@@ -12,13 +12,13 @@ If you run into any trouble, please [start a discussion](https://github.com/joel
12
12
 
13
13
  ### Community πŸ™Œ
14
14
 
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).
15
+ Everyone interacting in Phlex codebases, issue trackers or chat rooms is expected to follow the [code of conduct](https://github.com/joeldrapper/phlex/blob/main/CODE_OF_CONDUCT.md).
16
16
 
17
17
  ### Who uses Phlex?
18
18
 
19
19
  - [Clearscope](https://www.clearscope.io)
20
20
 
21
- *If you can share that your company uses Phlex in production, please open a PR to list it here.*
21
+ *If you’re using Phlex in production, please open a PR to list it here.*
22
22
 
23
23
  ### Sponsorship πŸ’–
24
24
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Phlex
4
4
  module DeferredRender
5
- include Experimental
5
+ # This module doesn't do anything. Phlex::HTML#call checks for its inclusion in the ancestry instead.
6
6
  end
7
7
  end
data/lib/phlex/html.rb CHANGED
@@ -126,12 +126,12 @@ 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
+ UNBUFFERED_MUTEX = Mutex.new
130
+
129
131
  extend Elements
130
132
  include Helpers
131
133
 
132
134
  class << self
133
- attr_accessor :rendered_at_least_once
134
-
135
135
  def call(...)
136
136
  new(...).call
137
137
  end
@@ -146,9 +146,30 @@ module Phlex
146
146
  super
147
147
  end
148
148
  end
149
+
150
+ def rendered_at_least_once!
151
+ alias_method :__attributes__, :__final_attributes__
152
+ alias_method :call, :__final_call__
153
+ end
154
+
155
+ def __unbuffered_class__
156
+ UNBUFFERED_MUTEX.synchronize do
157
+ if defined? @unbuffered_class
158
+ @unbuffered_class
159
+ else
160
+ @unbuffered_class = Class.new(Unbuffered)
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ def call(...)
167
+ __final_call__(...).tap do
168
+ self.class.rendered_at_least_once!
169
+ end
149
170
  end
150
171
 
151
- def call(buffer = +"", view_context: nil, parent: nil, &block)
172
+ def __final_call__(buffer = +"", view_context: nil, parent: nil, &block)
152
173
  @_target = buffer
153
174
  @_view_context = view_context
154
175
  @_parent = parent
@@ -176,8 +197,6 @@ module Phlex
176
197
  end
177
198
  end
178
199
 
179
- self.class.rendered_at_least_once = true
180
-
181
200
  buffer
182
201
  end
183
202
 
@@ -265,6 +284,10 @@ module Phlex
265
284
  @_target = original_buffer
266
285
  end
267
286
 
287
+ def unbuffered
288
+ self.class.__unbuffered_class__.new(self)
289
+ end
290
+
268
291
  # Like `capture` but the output is vanished into a BlackHole buffer.
269
292
  # Becuase the BlackHole does nothing with the output, this should be faster.
270
293
  private def __vanish__(*args)
@@ -356,21 +379,23 @@ module Phlex
356
379
  end
357
380
 
358
381
  private def __attributes__(**attributes)
359
- if attributes[:href]&.start_with?(/\s*javascript/)
382
+ __final_attributes__(**attributes).tap do |buffer|
383
+ Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
384
+ end
385
+ end
386
+
387
+ private def __final_attributes__(**attributes)
388
+ if attributes[:href]&.start_with?(/\s*javascript:/)
360
389
  attributes.delete(:href)
361
390
  end
362
391
 
363
- if attributes["href"]&.start_with?(/\s*javascript/)
392
+ if attributes["href"]&.start_with?(/\s*javascript:/)
364
393
  attributes.delete("href")
365
394
  end
366
395
 
367
396
  buffer = +""
368
397
  __build_attributes__(attributes, buffer: buffer)
369
398
 
370
- unless self.class.rendered_at_least_once
371
- Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
372
- end
373
-
374
399
  buffer
375
400
  end
376
401
 
@@ -2,13 +2,6 @@
2
2
 
3
3
  module Phlex
4
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
5
  def initialize(object)
13
6
  @object = object
14
7
  end
@@ -24,37 +17,35 @@ module Phlex
24
17
  define_method :__public_send__,
25
18
  ::Object.instance_method(:public_send)
26
19
 
27
- define_method :__callee__,
28
- ::Object.instance_method(:__callee__)
29
-
30
20
  def respond_to_missing?(...)
31
21
  @object.respond_to?(...)
32
22
  end
33
23
 
34
- def method_missing(name, *args, &block)
24
+ def method_missing(name, *args, **kwargs, &block)
35
25
  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__)
26
+
27
+ __class__.define_method(name) do |*a, **k, &b|
28
+ @object.capture { @object.public_send(name, *a, **k, &b) }
29
+ end
38
30
 
39
31
  # Now we've defined this missing method, we can call it.
40
- __public_send__(name, *args, &block)
32
+ __public_send__(name, *args, **kwargs, &block)
41
33
  else
42
34
  super
43
35
  end
44
36
  end
45
37
 
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) }
38
+ # Forward some methods to the original underlying method
39
+ def call(...)
40
+ @object.call(...)
49
41
  end
50
42
 
51
- def __forward_method__(*args, &block)
52
- @object.public_send(__callee__, *args, &block)
43
+ def send(...)
44
+ @object.send(...)
53
45
  end
54
46
 
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__
47
+ def public_send(...)
48
+ @object.public_send(...)
49
+ end
59
50
  end
60
51
  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.2.1"
4
+ VERSION = "1.3.1"
5
5
  end
data/lib/phlex.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "erb"
4
4
  require "zeitwerk"
5
+ require "concurrent"
5
6
 
6
7
  module Phlex
7
8
  Loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false).tap do |loader|
@@ -14,15 +15,5 @@ module Phlex
14
15
  ArgumentError = Class.new(ArgumentError) { include Error }
15
16
  NameError = Class.new(NameError) { include Error }
16
17
 
17
- extend self
18
-
19
- ATTRIBUTE_CACHE = {}
20
-
21
- def configuration
22
- @configuration ||= Configuration.new
23
- end
24
-
25
- def configure
26
- yield configuration
27
- end
18
+ ATTRIBUTE_CACHE = Concurrent::Map.new
28
19
  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: 1.2.1
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-25 00:00:00.000000000 Z
11
+ date: 2023-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.2'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: zeitwerk
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -50,14 +64,10 @@ files:
50
64
  - lib/phlex/black_hole.rb
51
65
  - lib/phlex/buffered.rb
52
66
  - lib/phlex/callable.rb
53
- - lib/phlex/collection.rb
54
- - lib/phlex/configuration.rb
55
67
  - lib/phlex/deferred_render.rb
56
68
  - lib/phlex/elements.rb
57
- - lib/phlex/experimental.rb
58
69
  - lib/phlex/helpers.rb
59
70
  - lib/phlex/html.rb
60
- - lib/phlex/table.rb
61
71
  - lib/phlex/testing/view_helper.rb
62
72
  - lib/phlex/unbuffered.rb
63
73
  - lib/phlex/version.rb
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlex
4
- module Collection
5
- include Experimental
6
-
7
- def initialize(collection: nil, item: nil)
8
- unless collection || item
9
- raise ArgumentError, "You must pass a collection or an item as a keyword argument."
10
- end
11
-
12
- @collection = collection
13
- @item = item
14
- end
15
-
16
- def template
17
- @item ? item_template : collection_template { yield_items }
18
- end
19
-
20
- private def yield_items
21
- if @item
22
- raise ArgumentError, "You can only yield_items when rendering a collection. You are currently rendering an item."
23
- end
24
-
25
- @collection.each do |item|
26
- @item = item
27
- item_template
28
- end
29
-
30
- @item = nil
31
- end
32
- end
33
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlex
4
- class Configuration
5
- attr_writer :experimental_warnings
6
-
7
- def experimental_warnings
8
- return @experimental_warnings if defined? @experimental_warnings
9
-
10
- true
11
- end
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlex
4
- module Experimental
5
- def before_template
6
- if Phlex.configuration.experimental_warnings
7
- puts "Warning: #{self.class.name} is using experimental Phlex features that are subject to change."
8
- end
9
-
10
- super
11
- end
12
- end
13
- end
data/lib/phlex/table.rb DELETED
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlex
4
- module Table
5
- include Experimental
6
- include Collection
7
-
8
- module ClassMethods
9
- attr_accessor :header
10
-
11
- def property(header = nil, **attributes, &body)
12
- if header.is_a?(String)
13
- header_text = header
14
- header = -> { head_header(scope: "col") { header_text } }
15
- end
16
-
17
- properties << {
18
- header: header,
19
- body: body,
20
- attributes: attributes,
21
- }
22
- end
23
-
24
- def properties
25
- @properties ||= []
26
- end
27
- end
28
-
29
- def self.included(child)
30
- child.extend ClassMethods
31
-
32
- child.alias_method :head, :thead
33
- child.alias_method :body, :tbody
34
- child.alias_method :foot, :tfoot
35
-
36
- child.alias_method :row, :tr
37
-
38
- child.alias_method :header, :th
39
- child.alias_method :cell, :td
40
-
41
- child.alias_method :head_row, :row
42
- child.alias_method :body_row, :row
43
- child.alias_method :foot_row, :row
44
-
45
- child.alias_method :head_header, :header
46
- child.alias_method :foot_header, :header
47
-
48
- child.alias_method :head_cell, :cell
49
- child.alias_method :body_cell, :cell
50
- child.alias_method :foot_cell, :cell
51
- end
52
-
53
- private def properties
54
- self.class.properties
55
- end
56
-
57
- private def collection_template(&block)
58
- table do
59
- head_template
60
- body_template(&block)
61
- foot_template
62
- end
63
- end
64
-
65
- private def item_template
66
- row_template
67
- end
68
-
69
- private def head_template
70
- if self.class.properties.any? { |p| p[:header] }
71
- head do
72
- head_row do
73
- self.class.properties.each do |property|
74
- case property[:header]
75
- when Proc
76
- instance_exec(&property[:header])
77
- when Symbol
78
- send(property[:header])
79
- end
80
- end
81
- end
82
- end
83
- end
84
- end
85
-
86
- private def body_template
87
- body { yield_items }
88
- end
89
-
90
- private def foot_template
91
- end
92
-
93
- private def row_template
94
- body_row do
95
- self.class.properties.each do |property|
96
- body_cell(**property[:attributes]) do
97
- instance_exec(@item, &property[:body])
98
- end
99
- end
100
- end
101
- end
102
- end
103
- end