phlex 2.4.0.beta1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66da2693f1133dc2ee688f43e91cca9ce2fb3537a3df44b364d89c653a4c85a0
4
- data.tar.gz: 4148a9f44e78765e788758c61e38a681d47833a30d5e4a6e6cae1d99dbf61cb7
3
+ metadata.gz: d7204ba8933eeeba6ac827d6a8f965f8a69a5d63245538a882f579881a1a4f3a
4
+ data.tar.gz: c0d11845eb37d6a14cd6c750972a7d898a63ba7062ddcf14f2e024e5785c20fb
5
5
  SHA512:
6
- metadata.gz: 642bbd9e026528f5e5a8c505b6b8ff557e40ad90a9ac7ec8b83af85c32825f5963d31ce5c420b29658db8ffc0dbe337daae1f99cd42437db9d45500582be4a06
7
- data.tar.gz: c4eb6e00e6424f381cbbdc0a68c4923f6d14b6296673d669a0fa4a3a7a3b411d247108590863f949e5801414d0bf0d2880a85c9d9931e03959188867a9a7737f
6
+ metadata.gz: 33797f2a30f72abc79d5161e4d3006e38023450c0b4eee03018c19e545307c8344e4e085aeabd590184550ec1f1505e6681e943e1af2ab1bf5faf7f0392abcc1
7
+ data.tar.gz: 6ffe9a663d9be2d87d73992640c803107f26671598e058c843db8786678eee53bab16605e046af350ec0c6c9c0f5c5a5e7fe2860402f43da9727e3d54eee5e7b
@@ -1,23 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Phlex::Compiler::ClassCompiler < Refract::Visitor
4
- def initialize(compiler)
4
+ def initialize(component, path)
5
5
  super()
6
- @compiler = compiler
6
+ @component = component
7
+ @path = path
7
8
  @compiled_snippets = []
8
9
  end
9
10
 
10
11
  def compile(node)
11
12
  visit(node.body)
12
- @compiled_snippets.freeze
13
+ @compiled_snippets.compact.freeze
13
14
  end
14
15
 
15
16
  visit Refract::DefNode do |node|
16
17
  return if node.name == :initialize
17
18
  return if node.receiver
18
19
 
20
+ method = begin
21
+ Phlex::UNBOUND_INSTANCE_METHOD_METHOD.bind_call(@component, node.name)
22
+ rescue NameError
23
+ nil
24
+ end
25
+
26
+ return unless method
27
+ path, lineno = method.source_location
28
+ return unless @path == path
29
+ return unless node.start_line == lineno
30
+
19
31
  @compiled_snippets << Phlex::Compiler::MethodCompiler.new(
20
- @compiler.component
32
+ @component
21
33
  ).compile(node)
22
34
  end
23
35
 
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Phlex::Compiler::Compactor < Refract::MutationVisitor
4
+ visit Refract::StatementsNode do |node|
5
+ queue = []
6
+ results = []
7
+ current_buffer = nil
8
+ nil_context = false
9
+
10
+ node.body.reverse_each { |n| queue << n }
11
+
12
+ while (child_node = queue.pop)
13
+ case child_node
14
+ when Refract::StatementsNode
15
+ child_node.body.reverse_each { |n| queue << n }
16
+ when Phlex::Compiler::Concat
17
+ if current_buffer
18
+ current_buffer << child_node.node
19
+ unless nil_context
20
+ results << Refract::NilNode.new
21
+ nil_context = true
22
+ end
23
+ else
24
+ current_buffer = [child_node.node]
25
+ results << Refract::ParenthesesNode.new(
26
+ body: Refract::StatementsNode.new(
27
+ body: [
28
+ Refract::IfNode.new(
29
+ inline: false,
30
+ predicate: Refract::LocalVariableReadNode.new(
31
+ name: :__phlex_should_render__
32
+ ),
33
+ statements: Refract::StatementsNode.new(
34
+ body: [
35
+ Refract::CallNode.new(
36
+ receiver: Refract::CallNode.new(
37
+ name: :__phlex_buffer__,
38
+ ),
39
+ name: :<<,
40
+ arguments: Refract::ArgumentsNode.new(
41
+ arguments: [
42
+ Refract::InterpolatedStringNode.new(
43
+ parts: current_buffer
44
+ ),
45
+ ]
46
+ )
47
+ ),
48
+ ]
49
+ )
50
+ ),
51
+ Refract::NilNode.new,
52
+ ]
53
+ )
54
+ )
55
+ nil_context = true
56
+ end
57
+ else
58
+ resolved = visit(child_node)
59
+ case resolved
60
+ when Refract::StatementsNode
61
+ resolved.body.reverse_each { |n| queue << n }
62
+ else
63
+ current_buffer = nil
64
+ results << resolved
65
+ nil_context = false
66
+ end
67
+ end
68
+ end
69
+
70
+ node.copy(
71
+ body: results
72
+ )
73
+ end
74
+ end
@@ -3,16 +3,16 @@
3
3
  class Phlex::Compiler::FileCompiler < Refract::Visitor
4
4
  Result = Data.define(:namespace, :compiled_snippets)
5
5
 
6
- def initialize(compiler)
6
+ def initialize(path)
7
7
  super()
8
- @compiler = compiler
8
+ @path = path
9
9
  @current_namespace = []
10
+ @results = []
10
11
  end
11
12
 
12
13
  def compile(node)
13
- catch(:phlex_compiler_result) do
14
- visit(node)
15
- end
14
+ visit(node)
15
+ @results.freeze
16
16
  end
17
17
 
18
18
  visit Refract::ModuleNode do |node|
@@ -24,10 +24,16 @@ class Phlex::Compiler::FileCompiler < Refract::Visitor
24
24
  visit Refract::ClassNode do |node|
25
25
  @current_namespace.push(node)
26
26
 
27
- if @compiler.line == node.start_line
28
- throw :phlex_compiler_result, Result.new(
27
+ namespace = @current_namespace.map do |node|
28
+ Refract::Formatter.new.format_node(node.constant_path).source
29
+ end.join("::")
30
+
31
+ const = eval(namespace, TOPLEVEL_BINDING)
32
+
33
+ if Class === const && Phlex::SGML > const
34
+ @results << Result.new(
29
35
  namespace: @current_namespace.dup.freeze,
30
- compiled_snippets: Phlex::Compiler::ClassCompiler.new(@compiler).compile(node)
36
+ compiled_snippets: Phlex::Compiler::ClassCompiler.new(const, @path).compile(node)
31
37
  )
32
38
  else
33
39
  super(node)
@@ -7,22 +7,20 @@ module Phlex::Compiler
7
7
  def initialize(component)
8
8
  super()
9
9
  @component = component
10
- @current_buffer = nil
11
10
  @preamble = []
11
+ @optimized = false
12
12
  end
13
13
 
14
14
  def compile(node)
15
- visit(node)
16
- end
17
-
18
- def around_visit(node)
19
- result = super
20
-
21
- # We want to clear the buffer when there’s a node that isn’t a statements node,
22
- # but we should ignore nils, which are usually other buffers.
23
- clear_buffer unless result in Refract::StatementsNode | nil
15
+ tree = visit(node)
24
16
 
25
- result
17
+ if @optimized
18
+ Compactor.new.visit(
19
+ tree
20
+ )
21
+ else
22
+ nil
23
+ end
26
24
  end
27
25
 
28
26
  visit Refract::ClassNode do |node|
@@ -36,14 +34,49 @@ module Phlex::Compiler
36
34
  visit Refract::DefNode do |node|
37
35
  if @stack.size == 1
38
36
  node.copy(
39
- body: Refract::StatementsNode.new(
40
- body: [
41
- Refract::StatementsNode.new(
42
- body: @preamble
37
+ body: Refract::BeginNode.new(
38
+ statements: Refract::StatementsNode.new(
39
+ body: [
40
+ Refract::StatementsNode.new(
41
+ body: @preamble
42
+ ),
43
+ Refract::NilNode.new,
44
+ visit(node.body),
45
+ ]
46
+ ),
47
+ rescue_clause: Refract::RescueNode.new(
48
+ exceptions: [],
49
+ reference: Refract::LocalVariableTargetNode.new(
50
+ name: :__phlex_exception__
43
51
  ),
44
- Refract::NilNode.new,
45
- visit(node.body),
46
- ]
52
+ statements: Refract::StatementsNode.new(
53
+ body: [
54
+ Refract::CallNode.new(
55
+ receiver: Refract::ConstantReadNode.new(
56
+ name: :Kernel
57
+ ),
58
+ name: :raise,
59
+ arguments: Refract::ArgumentsNode.new(
60
+ arguments: [
61
+ Refract::CallNode.new(
62
+ name: :__map_exception__,
63
+ arguments: Refract::ArgumentsNode.new(
64
+ arguments: [
65
+ Refract::LocalVariableReadNode.new(
66
+ name: :__phlex_exception__
67
+ ),
68
+ ]
69
+ )
70
+ ),
71
+ ]
72
+ )
73
+ ),
74
+ ]
75
+ ),
76
+ subsequent: nil
77
+ ),
78
+ else_clause: nil,
79
+ ensure_clause: nil
47
80
  )
48
81
  )
49
82
  else
@@ -76,11 +109,15 @@ module Phlex::Compiler
76
109
  end
77
110
 
78
111
  visit Refract::BlockNode do |node|
79
- node.copy(
80
- body: compile_block_body_node(
81
- node.body
112
+ if node.body
113
+ node.copy(
114
+ body: compile_block_body_node(
115
+ node.body
116
+ )
82
117
  )
83
- )
118
+ else
119
+ node
120
+ end
84
121
  end
85
122
 
86
123
  def compile_standard_element(node, tag)
@@ -88,19 +125,19 @@ module Phlex::Compiler
88
125
 
89
126
  Refract::StatementsNode.new(
90
127
  body: [
91
- buffer("<#{tag}"),
128
+ raw("<#{tag}"),
92
129
  *(
93
130
  if node.arguments
94
131
  compile_phlex_attributes(node.arguments)
95
132
  end
96
133
  ),
97
- buffer(">"),
134
+ raw(">"),
98
135
  *(
99
136
  if node.block
100
137
  compile_phlex_block(node.block)
101
138
  end
102
139
  ),
103
- buffer("</#{tag}>"),
140
+ raw("</#{tag}>"),
104
141
  ]
105
142
  )
106
143
  end
@@ -110,13 +147,13 @@ module Phlex::Compiler
110
147
 
111
148
  Refract::StatementsNode.new(
112
149
  body: [
113
- buffer("<#{tag}"),
150
+ raw("<#{tag}"),
114
151
  *(
115
152
  if node.arguments
116
153
  compile_phlex_attributes(node.arguments)
117
154
  end
118
155
  ),
119
- buffer(">"),
156
+ raw(">"),
120
157
  ]
121
158
  )
122
159
  end
@@ -131,17 +168,16 @@ module Phlex::Compiler
131
168
  end
132
169
 
133
170
  if literal_attributes
134
- return buffer(
171
+ return raw(
135
172
  Phlex::SGML::Attributes.generate_attributes(
136
173
  eval(
137
- "{#{Refract::Formatter.new.format_node(node)}}"
174
+ "{#{Refract::Formatter.new.format_node(node).source}}",
175
+ TOPLEVEL_BINDING
138
176
  )
139
177
  )
140
178
  )
141
179
  end
142
180
 
143
- clear_buffer
144
-
145
181
  Refract::CallNode.new(
146
182
  name: :__render_attributes__,
147
183
  arguments: Refract::ArgumentsNode.new(
@@ -162,7 +198,7 @@ module Phlex::Compiler
162
198
  content = node.body.body.first
163
199
  case content
164
200
  when Refract::StringNode, Refract::SymbolNode
165
- return buffer(Phlex::Escape.html_escape(content.unescaped))
201
+ return plain(content.unescaped)
166
202
  when Refract::InterpolatedStringNode
167
203
  return compile_interpolated_string_node(content)
168
204
  when Refract::NilNode
@@ -173,7 +209,6 @@ module Phlex::Compiler
173
209
  end
174
210
  end
175
211
 
176
- clear_buffer
177
212
  Refract::CallNode.new(
178
213
  name: :__yield_content__,
179
214
  block: node
@@ -181,7 +216,7 @@ module Phlex::Compiler
181
216
  end
182
217
 
183
218
  def compile_block_body_node(node)
184
- node => Refract::StatementsNode
219
+ node => Refract::StatementsNode | Refract::BeginNode
185
220
 
186
221
  Refract::StatementsNode.new(
187
222
  body: [
@@ -216,7 +251,7 @@ module Phlex::Compiler
216
251
  body: node.parts.map do |part|
217
252
  case part
218
253
  when Refract::StringNode
219
- buffer(Phlex::Escape.html_escape(part.unescaped))
254
+ plain(part.unescaped)
220
255
  when Refract::EmbeddedVariableNode
221
256
  interpolate(part.variable)
222
257
  when Refract::EmbeddedStatementsNode
@@ -234,27 +269,27 @@ module Phlex::Compiler
234
269
  if node.block
235
270
  Refract::StatementsNode.new(
236
271
  body: [
237
- buffer(" "),
272
+ raw(" "),
238
273
  compile_phlex_block(node.block),
239
- buffer(" "),
274
+ raw(" "),
240
275
  ]
241
276
  )
242
277
  else
243
- buffer(" ")
278
+ raw(" ")
244
279
  end
245
280
  end
246
281
 
247
282
  def compile_doctype_helper(node)
248
283
  node => Refract::CallNode
249
284
 
250
- buffer("<!doctype html>")
285
+ raw("<!doctype html>")
251
286
  end
252
287
 
253
288
  def compile_plain_helper(node)
254
289
  node => Refract::CallNode
255
290
 
256
- if node.arguments in [Refract::StringNode]
257
- buffer(node.arguments.arguments.first.unescaped)
291
+ if node.arguments.arguments in [Refract::StringNode]
292
+ raw(node.arguments.arguments.first.unescaped)
258
293
  else
259
294
  node
260
295
  end
@@ -306,51 +341,53 @@ module Phlex::Compiler
306
341
 
307
342
  Refract::StatementsNode.new(
308
343
  body: [
309
- buffer("<!-- "),
344
+ raw("<!-- "),
310
345
  compile_phlex_block(node.block),
311
- buffer(" -->"),
346
+ raw(" -->"),
312
347
  ]
313
348
  )
314
349
  end
315
350
 
316
351
  def compile_raw_helper(node)
317
352
  node => Refract::CallNode
318
-
319
353
  node
320
354
  end
321
355
 
322
- private def buffer(value)
323
- if @current_buffer
324
- @current_buffer << Refract::StringNode.new(
325
- unescaped: value
326
- )
356
+ private def plain(value)
357
+ value => String
358
+ raw(Phlex::Escape.html_escape(value))
359
+ end
327
360
 
328
- nil
329
- else
330
- new_buffer = [
331
- Refract::StringNode.new(
332
- unescaped: value
333
- ),
334
- ]
361
+ private def raw(value)
362
+ value => String
335
363
 
336
- @current_buffer = new_buffer
364
+ buffer(
365
+ Refract::StringNode.new(
366
+ unescaped: value
367
+ )
368
+ )
369
+ end
337
370
 
338
- Refract::IfNode.new(
339
- inline: false,
340
- predicate: Refract::LocalVariableReadNode.new(
341
- name: should_render_local
342
- ),
371
+ private def interpolate(statements)
372
+ buffer(
373
+ Refract::EmbeddedStatementsNode.new(
343
374
  statements: Refract::StatementsNode.new(
344
375
  body: [
345
376
  Refract::CallNode.new(
346
- receiver: Refract::CallNode.new(
347
- name: buffer_local,
377
+ receiver: Refract::ConstantPathNode.new(
378
+ parent: Refract::ConstantPathNode.new(
379
+ name: "Phlex"
380
+ ),
381
+ name: "Escape"
348
382
  ),
349
- name: :<<,
383
+ name: :html_escape,
350
384
  arguments: Refract::ArgumentsNode.new(
351
385
  arguments: [
352
- Refract::InterpolatedStringNode.new(
353
- parts: new_buffer
386
+ Refract::CallNode.new(
387
+ receiver: Refract::ParenthesesNode.new(
388
+ body: statements
389
+ ),
390
+ name: :to_s
354
391
  ),
355
392
  ]
356
393
  )
@@ -358,76 +395,27 @@ module Phlex::Compiler
358
395
  ]
359
396
  )
360
397
  )
361
- end
362
- end
363
-
364
- private def interpolate(statements, escape: true)
365
- embedded_statement = Refract::EmbeddedStatementsNode.new(
366
- statements: Refract::StatementsNode.new(
367
- body: [
368
- Refract::CallNode.new(
369
- receiver: Refract::ConstantPathNode.new(
370
- parent: Refract::ConstantPathNode.new(
371
- name: "Phlex"
372
- ),
373
- name: "Escape"
374
- ),
375
- name: :html_escape,
376
- arguments: Refract::ArgumentsNode.new(
377
- arguments: [
378
- Refract::CallNode.new(
379
- receiver: Refract::ParenthesesNode.new(
380
- body: statements
381
- ),
382
- name: :to_s
383
- ),
384
- ]
385
- )
386
- ),
387
- ]
388
- )
389
398
  )
399
+ end
390
400
 
391
- if @current_buffer
392
- @current_buffer << embedded_statement
393
-
394
- nil
395
- else
396
- new_buffer = [embedded_statement]
401
+ private def buffer(node)
402
+ @optimized = true
403
+ node => Refract::StringNode | Refract::EmbeddedStatementsNode
397
404
 
398
- @current_buffer = new_buffer
405
+ should_render_local
406
+ buffer_local
399
407
 
400
- Refract::IfNode.new(
401
- predicate: Refract::LocalVariableReadNode.new(
402
- name: should_render_local
408
+ Refract::StatementsNode.new(
409
+ body: [
410
+ Concat.new(
411
+ node
403
412
  ),
404
- statements: Refract::StatementsNode.new(
405
- body: [
406
- Refract::CallNode.new(
407
- receiver: Refract::CallNode.new(
408
- name: buffer_local,
409
- ),
410
- name: :<<,
411
- arguments: Refract::ArgumentsNode.new(
412
- arguments: [
413
- Refract::InterpolatedStringNode.new(
414
- parts: new_buffer
415
- ),
416
- ]
417
- )
418
- ),
419
- ]
420
- )
421
- )
422
- end
423
- end
424
-
425
- private def clear_buffer
426
- @current_buffer = nil
413
+ ]
414
+ )
427
415
  end
428
416
 
429
417
  private def output_block?(node)
430
- node.body.body.any? do |child|
418
+ node.body&.body&.any? do |child|
431
419
  Refract::CallNode === child && (
432
420
  standard_element?(child) ||
433
421
  void_element?(child) ||
@@ -439,13 +427,13 @@ module Phlex::Compiler
439
427
  end
440
428
 
441
429
  private def static_content_block?(node)
442
- return false unless node.body.body.length == 1
430
+ return false unless node.body&.body&.length == 1
443
431
  node.body.body.first in Refract::StringNode | Refract::InterpolatedStringNode | Refract::SymbolNode | Refract::NilNode
444
432
  end
445
433
 
446
434
  private def standard_element?(node)
447
435
  if (tag = Phlex::HTML::StandardElements.__registered_elements__[node.name]) &&
448
- (Phlex::HTML::StandardElements == @component.instance_method(node.name).owner)
436
+ (Phlex::HTML::StandardElements == Phlex::UNBOUND_INSTANCE_METHOD_METHOD.bind_call(@component, node.name).owner)
449
437
 
450
438
  tag
451
439
  else
@@ -455,7 +443,7 @@ module Phlex::Compiler
455
443
 
456
444
  private def void_element?(node)
457
445
  if (tag = Phlex::HTML::VoidElements.__registered_elements__[node.name]) &&
458
- (Phlex::HTML::VoidElements == @component.instance_method(node.name).owner)
446
+ (Phlex::HTML::VoidElements == Phlex::UNBOUND_INSTANCE_METHOD_METHOD.bind_call(@component, node.name).owner)
459
447
 
460
448
  tag
461
449
  else
@@ -474,7 +462,7 @@ module Phlex::Compiler
474
462
  when Refract::AssocNode
475
463
  (Refract::StringNode === value.key || Refract::SymbolNode === value.key) && static_attribute_value_literal?(value.value)
476
464
  when Refract::CallNode
477
- if value in { receiver: Prism::ConstantReadNode[name: :Set]| Prism::ConstantPathNode[name: :Set, parent: nil], name: :[] }
465
+ if value in { receiver: Prism::ConstantReadNode[name: :Set] | Prism::ConstantPathNode[name: :Set, parent: nil], name: :[] }
478
466
  value.arguments.arguments.all? { |n| static_token_value_literal?(n) }
479
467
  else
480
468
  false
@@ -521,7 +509,7 @@ module Phlex::Compiler
521
509
 
522
510
  ALLOWED_OWNERS = Set[Phlex::SGML, Phlex::HTML, Phlex::SVG]
523
511
  private def own_method_without_scope?(node)
524
- ALLOWED_OWNERS.include?(@component.instance_method(node.name).owner)
512
+ ALLOWED_OWNERS.include?(Phlex::UNBOUND_INSTANCE_METHOD_METHOD.bind_call(@component, node.name).owner)
525
513
  end
526
514
 
527
515
  private def state_local
@@ -4,15 +4,54 @@ require "prism"
4
4
  require "refract"
5
5
 
6
6
  module Phlex::Compiler
7
+ MAP = {}
7
8
  Error = Class.new(StandardError)
8
9
 
10
+ Concat = Data.define(:node) do
11
+ def start_line = nil
12
+ def accept(visitor) = self
13
+ end
14
+
9
15
  def self.compile(component)
10
16
  path, line = Object.const_source_location(component.name)
11
- return unless File.exist?(path)
17
+ compile_file(path)
18
+ end
19
+
20
+ def self.compile_file(path)
21
+ unless File.exist?(path)
22
+ raise ArgumentError, "Can’t compile #{path} because it doesn’t exist."
23
+ end
24
+
25
+ require(path)
26
+
12
27
  source = File.read(path)
13
28
  tree = Prism.parse(source).value
14
29
  refract = Refract::Converter.new.visit(tree)
15
30
 
16
- Compilation.new(component, path, line, source, refract).compile
31
+ last_line = source.count("\n")
32
+
33
+ starting_line = last_line + 1
34
+
35
+ results = FileCompiler.new(path).compile(refract)
36
+
37
+ result = Refract::StatementsNode.new(
38
+ body: results.map do |result|
39
+ result.namespace.reverse_each.reduce(
40
+ result.compiled_snippets
41
+ ) do |body, scope|
42
+ scope.copy(
43
+ body: Refract::StatementsNode.new(
44
+ body: [body]
45
+ )
46
+ )
47
+ end
48
+ end
49
+ )
50
+
51
+ formatting_result = Refract::Formatter.new(starting_line:).format_node(result)
52
+
53
+ MAP[path] = formatting_result.source_map
54
+
55
+ eval("# frozen_string_literal: true\n#{formatting_result.source}", TOPLEVEL_BINDING, path, starting_line - 1)
17
56
  end
18
57
  end
data/lib/phlex/fifo.rb CHANGED
@@ -36,7 +36,7 @@ class Phlex::FIFO
36
36
  @bytesize += value.bytesize
37
37
 
38
38
  while @bytesize > @max_bytesize
39
- k, v = @store.shift
39
+ _k, v = @store.shift
40
40
  @bytesize -= v[1].bytesize
41
41
  end
42
42
  end
data/lib/phlex/kit.rb CHANGED
@@ -63,8 +63,8 @@ module Phlex::Kit
63
63
  end
64
64
 
65
65
  define_singleton_method(name) do |*args, **kwargs, &block|
66
- component, fiber_id = Thread.current[:__phlex_component__]
67
- if (component && fiber_id == Fiber.current.object_id)
66
+ component = Thread.current[:__phlex_component__]
67
+ if component
68
68
  component.instance_exec do
69
69
  constant = me.const_get(name)
70
70
  render(constant.new(*args, **kwargs), &block)
@@ -53,7 +53,11 @@ module Phlex::SGML::Attributes
53
53
  when Phlex::SGML::SafeObject
54
54
  v.to_s.gsub('"', "&quot;")
55
55
  else
56
- raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
56
+ if v.respond_to?(:to_h)
57
+ generate_nested_attributes(v.to_h, "#{name}-", buffer)
58
+ else
59
+ raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
60
+ end
57
61
  end
58
62
 
59
63
  lower_name = name.downcase
data/lib/phlex/sgml.rb CHANGED
@@ -2,17 +2,6 @@
2
2
 
3
3
  # **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
4
4
  class Phlex::SGML
5
- ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
6
- compiler.pre_cmd = [""]
7
- compiler.put_cmd = "@_state.buffer.<<"
8
- compiler.insert_cmd = "__implicit_output__"
9
- compiler.post_cmd = ["nil"]
10
-
11
- def compiler.add_insert_cmd(out, content)
12
- out.push("#{@insert_cmd}((#{content}))")
13
- end
14
- end
15
-
16
5
  include Phlex::Helpers
17
6
 
18
7
  class << self
@@ -32,40 +21,6 @@ class Phlex::SGML
32
21
  super
33
22
  end
34
23
  end
35
-
36
- def erb(method_name, erb = nil, locals: nil, &block)
37
- loc = caller_locations(1, 1)[0]
38
- path = loc.path.delete_suffix(".rb")
39
- file = loc.path
40
- line = loc.lineno - 1
41
-
42
- unless erb
43
- method_path = "#{path}/#{method_name}.html.erb"
44
- sidecar_path = "#{path}.html.erb"
45
-
46
- if File.exist?(method_path)
47
- erb = File.read(method_path)
48
- file = method_path
49
- line = 1
50
- elsif method_name == :view_template && File.exist?(sidecar_path)
51
- erb = File.read(sidecar_path)
52
- file = sidecar_path
53
- line = 1
54
- else
55
- raise Phlex::RuntimeError.new(<<~MESSAGE)
56
- No ERB template found for #{method_name}
57
- MESSAGE
58
- end
59
- end
60
-
61
- code, _enc = ERBCompiler.compile(erb)
62
-
63
- class_eval(<<~RUBY, file, line)
64
- def #{method_name} #{locals}
65
- #{code}
66
- end
67
- RUBY
68
- end
69
24
  end
70
25
 
71
26
  def view_template
@@ -105,7 +60,8 @@ class Phlex::SGML
105
60
 
106
61
  block ||= @_content_block
107
62
 
108
- Thread.current[:__phlex_component__] = [self, Fiber.current.object_id].freeze
63
+ previous_phlex_component = Thread.current[:__phlex_component__]
64
+ Thread.current[:__phlex_component__] = self
109
65
 
110
66
  state.around_render(self) do
111
67
  before_template(&block)
@@ -127,7 +83,7 @@ class Phlex::SGML
127
83
  after_template(&block)
128
84
  end
129
85
  ensure
130
- Thread.current[:__phlex_component__] = [parent, Fiber.current.object_id].freeze
86
+ Thread.current[:__phlex_component__] = previous_phlex_component
131
87
  end
132
88
 
133
89
  def context
@@ -481,4 +437,18 @@ class Phlex::SGML
481
437
  path, line = instance_method(method_name).source_location
482
438
  Phlex::Compiler::Method.new(self, path, line, method_name).compile
483
439
  end
440
+
441
+ def __map_exception__(exception)
442
+ exception.set_backtrace(
443
+ exception.backtrace_locations.map do |loc|
444
+ if ((map = Phlex::Compiler::MAP[loc.path]) && (line = map[loc.lineno]))
445
+ "[Phlex] #{loc.path}:#{line}:#{loc.label}"
446
+ else
447
+ "#{loc.path}:#{loc.lineno}:#{loc.label}"
448
+ end
449
+ end
450
+ )
451
+
452
+ exception
453
+ end
484
454
  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 = "2.4.0.beta1"
4
+ VERSION = "2.4.0"
5
5
  end
data/lib/phlex.rb CHANGED
@@ -27,6 +27,8 @@ module Phlex
27
27
  CACHED_FILES = Set.new
28
28
  ATTRIBUTE_CACHE = FIFO.new
29
29
 
30
+ UNBOUND_INSTANCE_METHOD_METHOD = Module.instance_method(:instance_method)
31
+
30
32
  def self.__expand_attribute_cache__(file_path)
31
33
  unless CACHED_FILES.include?(file_path)
32
34
  CACHED_FILES << file_path
@@ -41,6 +41,8 @@ module RubyLsp
41
41
  RubyIndexer::Entry::BlockParameter.new(name: :content),
42
42
  ]),
43
43
  ], visibility: :public, comments: "Outputs a `<#{tag_name}>` tag.")
44
+ else
45
+ # no-op
44
46
  end
45
47
  end
46
48
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0.beta1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
@@ -50,7 +50,7 @@ files:
50
50
  - lib/phlex.rb
51
51
  - lib/phlex/compiler.rb
52
52
  - lib/phlex/compiler/class_compiler.rb
53
- - lib/phlex/compiler/compilation.rb
53
+ - lib/phlex/compiler/compactor.rb
54
54
  - lib/phlex/compiler/file_compiler.rb
55
55
  - lib/phlex/compiler/method_compiler.rb
56
56
  - lib/phlex/csv.rb
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
99
  - !ruby/object:Gem::Version
100
100
  version: '0'
101
101
  requirements: []
102
- rubygems_version: 3.6.7
102
+ rubygems_version: 4.0.3
103
103
  specification_version: 4
104
104
  summary: Object-oriented views in Ruby.
105
105
  test_files: []
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phlex::Compiler
4
- class Compilation
5
- def initialize(component, path, line, source, tree)
6
- @component = component
7
- @path = path
8
- @line = line
9
- @source = source
10
- @tree = tree
11
- freeze
12
- end
13
-
14
- attr_reader :component, :line, :source, :path
15
-
16
- def compile
17
- result = FileCompiler.new(self).compile(@tree)
18
-
19
- result.compiled_snippets.each do |snippet|
20
- start_line = snippet.start_line
21
-
22
- namespaced = result.namespace.reverse_each.reduce(snippet) do |body, scope|
23
- start_line -= 1
24
-
25
- scope.copy(
26
- body: Refract::StatementsNode.new(
27
- body: [body]
28
- )
29
- )
30
- end
31
-
32
- source = Refract::Formatter.new.format_node(namespaced)
33
-
34
- redefine_method(
35
- source,
36
- start_line
37
- )
38
- end
39
- end
40
-
41
- def redefine_method(source, line)
42
- eval("# frozen_string_literal: true\n#{source}", TOPLEVEL_BINDING, @path, line - 1)
43
- end
44
- end
45
- end