phlex 1.5.3 → 1.6.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: cb9f666a03a35cd5769f6a7fb559665f829997ad2d4267997512a21199091eee
4
- data.tar.gz: 0e4e313a93f0ad010dccf984ebce586dfbe5c72cf10bec0a101c2a8f83d0af9b
3
+ metadata.gz: eeac8033b47d1e0cf99832bc7a42a7777ca44643dd503f3cb08087666b08d4f7
4
+ data.tar.gz: 6f7e786e982dcc9133c40179d4bb582d9d45bb610814020e916a2afbf17cb0d0
5
5
  SHA512:
6
- metadata.gz: 3b2bdd2072778140464602492e213af01dcfd6b780ff8a92b21384ac31205e7c2b3ed767222e431a8e3983c9fe4483cea6494800c02979abee3617a0c89dee80
7
- data.tar.gz: '059c2b94fc6c38a55997d5e9479c64f56bd43d0d9566647584f54c54728cb84924c88a910bb2acb7093ffd3746c1215e1b6f6c257b7ab02de7c821e19c06fc28'
6
+ metadata.gz: 037caf900c1155b1e9095588a30dff67a7f224b6e16365d417dc7412e34822cb1368a194a4e48de65c20a838b91f671e7e53fca7a0f0e973852d84ebc7c03d3c
7
+ data.tar.gz: 85ad16185650ff8aa04831c745ac6709ae35913863af0fef5a70d671d75dbbdf389005db6480cb5c4533ad9e3106379cb6aa2015b64194ca78a2dd8b61cd3993
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.0
1
+ 3.2.1
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Phlex::Context
4
+ def initialize
5
+ @target = +""
6
+ end
7
+
8
+ attr_accessor :target
9
+
10
+ def with_target(new_target)
11
+ original_target = @target
12
+
13
+ begin
14
+ @target = new_target
15
+ yield
16
+ ensure
17
+ @target = original_target
18
+ end
19
+
20
+ new_target
21
+ end
22
+ end
@@ -20,19 +20,19 @@ module Phlex::Elements
20
20
  def __phlex_#{element}__(**attributes, &block)
21
21
  if attributes.length > 0 # with attributes
22
22
  if block_given? # with content block
23
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
23
+ @_context.target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
24
24
  yield_content(&block)
25
- @_target << "</#{tag}>"
25
+ @_context.target << "</#{tag}>"
26
26
  else # without content block
27
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
27
+ @_context.target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << "></#{tag}>"
28
28
  end
29
29
  else # without attributes
30
30
  if block_given? # with content block
31
- @_target << "<#{tag}>"
31
+ @_context.target << "<#{tag}>"
32
32
  yield_content(&block)
33
- @_target << "</#{tag}>"
33
+ @_context.target << "</#{tag}>"
34
34
  else # without content block
35
- @_target << "<#{tag}></#{tag}>"
35
+ @_context.target << "<#{tag}></#{tag}>"
36
36
  end
37
37
  end
38
38
 
@@ -53,9 +53,9 @@ module Phlex::Elements
53
53
 
54
54
  def __phlex_#{element}__(**attributes)
55
55
  if attributes.length > 0 # with attributes
56
- @_target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
56
+ @_context.target << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[respond_to?(:process_attributes) ? (attributes.hash + self.class.hash) : attributes.hash] || __attributes__(**attributes)) << ">"
57
57
  else # without attributes
58
- @_target << "<#{tag}>"
58
+ @_context.target << "<#{tag}>"
59
59
  end
60
60
 
61
61
  nil
data/lib/phlex/html.rb CHANGED
@@ -25,7 +25,7 @@ module Phlex
25
25
 
26
26
  # Output an HTML doctype.
27
27
  def doctype
28
- @_target << "<!DOCTYPE html>"
28
+ @_context.target << "<!DOCTYPE html>"
29
29
  nil
30
30
  end
31
31
 
data/lib/phlex/sgml.rb CHANGED
@@ -34,21 +34,21 @@ module Phlex
34
34
  end
35
35
 
36
36
  # Renders the view and returns the buffer. The default buffer is a mutable String.
37
- def call(buffer = nil, target: +"", view_context: nil, parent: nil, &block)
38
- __final_call__(buffer, target: target, view_context: view_context, parent: parent, &block).tap do
37
+ def call(buffer = nil, context: Phlex::Context.new, view_context: nil, parent: nil, &block)
38
+ __final_call__(buffer, context: context, view_context: view_context, parent: parent, &block).tap do
39
39
  self.class.rendered_at_least_once!
40
40
  end
41
41
  end
42
42
 
43
43
  # @api private
44
- def __final_call__(buffer = nil, target: +"", view_context: nil, parent: nil, &block)
45
- @_target = target
44
+ def __final_call__(buffer = nil, context: Phlex::Context.new, view_context: nil, parent: nil, &block)
45
+ @_context = context
46
46
  @_view_context = view_context
47
47
  @_parent = parent
48
48
 
49
49
  block ||= @_content_block
50
50
 
51
- return buffer || target unless render?
51
+ return buffer || context.target unless render?
52
52
 
53
53
  around_template do
54
54
  if block
@@ -69,7 +69,7 @@ module Phlex
69
69
  end
70
70
  end
71
71
 
72
- buffer ? (buffer << target) : target
72
+ buffer ? (buffer << context.target) : context.target
73
73
  end
74
74
 
75
75
  # Render another view
@@ -78,11 +78,15 @@ module Phlex
78
78
  def render(renderable, &block)
79
79
  case renderable
80
80
  when Phlex::SGML
81
- renderable.call(target: @_target, view_context: @_view_context, parent: self, &block)
81
+ renderable.call(context: @_context, view_context: @_view_context, parent: self, &block)
82
82
  when Class
83
83
  if renderable < Phlex::SGML
84
- renderable.new.call(target: @_target, view_context: @_view_context, parent: self, &block)
84
+ renderable.new.call(context: @_context, view_context: @_view_context, parent: self, &block)
85
85
  end
86
+ when Enumerable
87
+ renderable.each { |r| render(r, &block) }
88
+ when Proc
89
+ yield_content(&renderable)
86
90
  else
87
91
  raise ArgumentError, "You can't render a #{renderable}."
88
92
  end
@@ -95,16 +99,16 @@ module Phlex
95
99
  def plain(content)
96
100
  case content
97
101
  when String
98
- @_target << ERB::Escape.html_escape(content)
102
+ @_context.target << ERB::Escape.html_escape(content)
99
103
  when Symbol
100
- @_target << ERB::Escape.html_escape(content.name)
104
+ @_context.target << ERB::Escape.html_escape(content.name)
101
105
  when Integer
102
- @_target << ERB::Escape.html_escape(content.to_s)
106
+ @_context.target << ERB::Escape.html_escape(content.to_s)
103
107
  when nil
104
108
  nil
105
109
  else
106
110
  if (formatted_object = format_object(content))
107
- @_target << ERB::Escape.html_escape(formatted_object)
111
+ @_context.target << ERB::Escape.html_escape(formatted_object)
108
112
  end
109
113
  end
110
114
 
@@ -114,11 +118,11 @@ module Phlex
114
118
  # Output a whitespace character. This is useful for getting inline elements to wrap. If you pass a block, a whitespace will be output before and after yielding the block.
115
119
  # @return [nil]
116
120
  def whitespace
117
- @_target << " "
121
+ @_context.target << " "
118
122
 
119
123
  if block_given?
120
124
  yield
121
- @_target << " "
125
+ @_context.target << " "
122
126
  end
123
127
 
124
128
  nil
@@ -127,9 +131,9 @@ module Phlex
127
131
  # Output an HTML comment.
128
132
  # @return [nil]
129
133
  def comment(&block)
130
- @_target << "<!-- "
134
+ @_context.target << "<!-- "
131
135
  yield_content(&block)
132
- @_target << " -->"
136
+ @_context.target << " -->"
133
137
 
134
138
  nil
135
139
  end
@@ -140,27 +144,17 @@ module Phlex
140
144
  def unsafe_raw(content = nil)
141
145
  return nil unless content
142
146
 
143
- @_target << content
147
+ @_context.target << content
144
148
  nil
145
149
  end
146
150
 
147
151
  # Capture a block of output as a String.
152
+ # @note This only works if the block's receiver is the current component or the block returns a String.
148
153
  # @return [String]
149
154
  def capture(&block)
150
- return "" unless block_given?
155
+ return "" unless block
151
156
 
152
- original_buffer_content = @_target.dup
153
- @_target.clear
154
-
155
- begin
156
- yield_content(&block)
157
- new_buffer_content = @_target.dup
158
- ensure
159
- @_target.clear
160
- @_target << original_buffer_content
161
- end
162
-
163
- new_buffer_content.is_a?(String) ? new_buffer_content : ""
157
+ @_context.with_target(+"") { yield_content(&block) }
164
158
  end
165
159
 
166
160
  # Like `capture` but the output is vanished into a BlackHole buffer.
@@ -169,14 +163,7 @@ module Phlex
169
163
  private def __vanish__(*args)
170
164
  return unless block_given?
171
165
 
172
- original_buffer = @_target
173
-
174
- begin
175
- @_target = BlackHole
176
- yield(*args)
177
- ensure
178
- @_target = original_buffer
179
- end
166
+ @_context.with_target(BlackHole) { yield(*args) }
180
167
 
181
168
  nil
182
169
  end
@@ -223,10 +210,10 @@ module Phlex
223
210
  private def yield_content
224
211
  return unless block_given?
225
212
 
226
- original_length = @_target.length
213
+ original_length = @_context.target.length
227
214
  content = yield(self)
228
215
 
229
- plain(content) if original_length == @_target.length
216
+ plain(content) if original_length == @_context.target.length
230
217
 
231
218
  nil
232
219
  end
@@ -236,9 +223,9 @@ module Phlex
236
223
  private def yield_content_with_args(*args)
237
224
  return unless block_given?
238
225
 
239
- original_length = @_target.length
226
+ original_length = @_context.target.length
240
227
  content = yield(*args)
241
- plain(content) if original_length == @_target.length
228
+ plain(content) if original_length == @_context.target.length
242
229
 
243
230
  nil
244
231
  end
@@ -256,6 +243,14 @@ module Phlex
256
243
  attributes = process_attributes(**attributes)
257
244
  end
258
245
 
246
+ if attributes[:href]&.start_with?(/\s*javascript:/)
247
+ attributes.delete(:href)
248
+ end
249
+
250
+ if attributes["href"]&.start_with?(/\s*javascript:/)
251
+ attributes.delete("href")
252
+ end
253
+
259
254
  buffer = +""
260
255
  __build_attributes__(attributes, buffer: buffer)
261
256
 
@@ -273,11 +268,8 @@ module Phlex
273
268
  else k.to_s
274
269
  end
275
270
 
276
- lower_name = name.downcase
277
- next if lower_name == "href" && v.to_s.downcase.tr("\t \n", "").start_with?("javascript:")
278
-
279
271
  # Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters.
280
- if HTML::EVENT_ATTRIBUTES[lower_name] || name.match?(/[<>&"']/)
272
+ if HTML::EVENT_ATTRIBUTES[name] || name.match?(/[<>&"']/)
281
273
  raise ArgumentError, "Unsafe attribute name detected: #{k}."
282
274
  end
283
275
 
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.5.3"
4
+ VERSION = "1.6.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.5.3
4
+ version: 1.6.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-04-17 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -76,6 +76,7 @@ files:
76
76
  - lib/phlex.rb
77
77
  - lib/phlex/black_hole.rb
78
78
  - lib/phlex/callable.rb
79
+ - lib/phlex/context.rb
79
80
  - lib/phlex/deferred_render.rb
80
81
  - lib/phlex/element_clobbering_guard.rb
81
82
  - lib/phlex/elements.rb
@@ -116,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
117
  - !ruby/object:Gem::Version
117
118
  version: '0'
118
119
  requirements: []
119
- rubygems_version: 3.5.9
120
+ rubygems_version: 3.4.6
120
121
  signing_key:
121
122
  specification_version: 4
122
123
  summary: A framework for building views in Ruby.