phlex 1.2.2 β†’ 1.3.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: f62cc3d24a6690657b01ac0d35f0ee5ed669d4c374db0b1792e9178b10a01788
4
- data.tar.gz: 17296d2f834f7d29e7f107da769971b985e812416479a5f9dd94074dec124147
3
+ metadata.gz: 8df07c966d273976d91feebba2528a5cdacb5bb28116af48a54f42a97128ad94
4
+ data.tar.gz: 638529d514e4d1938ec0c0bab224969cc16a252752da3e1ab67ae2f217f4ea3e
5
5
  SHA512:
6
- metadata.gz: af0d4fa2c505788bb38e8988728d9b58469f7ba76b6cc0be52d378a5d9a50ecbd7402a3d068d1083872fb028499d6a6b334e0a52636edf2f0fc2c62703a3ccbf
7
- data.tar.gz: 1ddbce1b9d3b01820292948f1813e6973897b9ebb4ae1cbb3d3fa9e643350784161a3a33193e82a12b097f6cca36815d8ed001575e4d63308e2ede6adf0071e3
6
+ metadata.gz: 3728d8501c975eef2caa6c8b7c78d98d0f4aa654a88787e13e58c608331112b06074a177f401f6691713e8bbc4eac863e2b318e6d58cc61a3bfa38319e69e82d
7
+ data.tar.gz: bfd075ce712b522da689ed643b9a52536a80a7e779b807e8994c00a623a9e45b8a051f2dc78166efeebed047f02cda97c10f70c8165b99325e9c0236937ab918
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.0
1
+ 3.2.0
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
@@ -130,8 +130,6 @@ module Phlex
130
130
  include Helpers
131
131
 
132
132
  class << self
133
- attr_accessor :rendered_at_least_once
134
-
135
133
  def call(...)
136
134
  new(...).call
137
135
  end
@@ -146,9 +144,20 @@ module Phlex
146
144
  super
147
145
  end
148
146
  end
147
+
148
+ def rendered_at_least_once!
149
+ alias_method :__attributes__, :__final_attributes__
150
+ alias_method :call, :__final_call__
151
+ end
152
+ end
153
+
154
+ def call(...)
155
+ __final_call__(...).tap do
156
+ self.class.rendered_at_least_once!
157
+ end
149
158
  end
150
159
 
151
- def call(buffer = +"", view_context: nil, parent: nil, &block)
160
+ def __final_call__(buffer = +"", view_context: nil, parent: nil, &block)
152
161
  @_target = buffer
153
162
  @_view_context = view_context
154
163
  @_parent = parent
@@ -176,8 +185,6 @@ module Phlex
176
185
  end
177
186
  end
178
187
 
179
- self.class.rendered_at_least_once = true
180
-
181
188
  buffer
182
189
  end
183
190
 
@@ -356,12 +363,22 @@ module Phlex
356
363
  end
357
364
 
358
365
  private def __attributes__(**attributes)
359
- buffer = +""
360
- __build_attributes__(attributes, buffer: buffer)
361
-
362
- unless self.class.rendered_at_least_once
366
+ __final_attributes__(**attributes).tap do |buffer|
363
367
  Phlex::ATTRIBUTE_CACHE[attributes.hash] = buffer.freeze
364
368
  end
369
+ end
370
+
371
+ private def __final_attributes__(**attributes)
372
+ if attributes[:href]&.start_with?(/\s*javascript:/)
373
+ attributes.delete(:href)
374
+ end
375
+
376
+ if attributes["href"]&.start_with?(/\s*javascript:/)
377
+ attributes.delete("href")
378
+ end
379
+
380
+ buffer = +""
381
+ __build_attributes__(attributes, buffer: buffer)
365
382
 
366
383
  buffer
367
384
  end
@@ -376,11 +393,8 @@ module Phlex
376
393
  else k.to_s
377
394
  end
378
395
 
379
- lower_name = name.downcase
380
- next if lower_name == "href" && v.start_with?(/\s*javascript:/i)
381
-
382
396
  # Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters.
383
- if HTML::EVENT_ATTRIBUTES[lower_name] || name.match?(/[<>&"']/)
397
+ if HTML::EVENT_ATTRIBUTES[name] || name.match?(/[<>&"']/)
384
398
  raise ArgumentError, "Unsafe attribute name detected: #{k}."
385
399
  end
386
400
 
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Phlex
4
4
  class Unbuffered < BasicObject
5
- CACHE = {}
5
+ CACHE = ::Concurrent::Map.new
6
6
 
7
7
  def self.call(object)
8
- decorator = CACHE[object.class.name] ||= ::Class.new(self)
8
+ decorator = CACHE.compute_if_absent(object.class.name) { ::Class.new(self) }
9
9
  decorator.new(object)
10
10
  end
11
11
 
@@ -31,10 +31,12 @@ module Phlex
31
31
  @object.respond_to?(...)
32
32
  end
33
33
 
34
- def method_missing(name, *args, &block)
34
+ def method_missing(name, *args, **kwargs, &block)
35
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__)
36
+
37
+ __class__.define_method(name) do |*a, **k, &b|
38
+ @object.capture { @object.public_send(name, *a, **k, &b) }
39
+ end
38
40
 
39
41
  # Now we've defined this missing method, we can call it.
40
42
  __public_send__(name, *args, &block)
@@ -43,18 +45,17 @@ module Phlex
43
45
  end
44
46
  end
45
47
 
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) }
48
+ # Forward some methods to the original underlying method
49
+ def call(...)
50
+ @object.call(...)
49
51
  end
50
52
 
51
- def __forward_method__(*args, &block)
52
- @object.public_send(__callee__, *args, &block)
53
+ def send(...)
54
+ @object.send(...)
53
55
  end
54
56
 
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__
57
+ def public_send(...)
58
+ @object.public_send(...)
59
+ end
59
60
  end
60
61
  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.2"
4
+ VERSION = "1.3.0"
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.2
4
+ version: 1.3.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-28 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
@@ -87,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
97
  - !ruby/object:Gem::Version
88
98
  version: '0'
89
99
  requirements: []
90
- rubygems_version: 3.5.6
100
+ rubygems_version: 3.4.4
91
101
  signing_key:
92
102
  specification_version: 4
93
103
  summary: A framework for building views in Ruby.
@@ -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