p2 2.9 → 2.11

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: bda78e0c4b37c895274b5d357022f3f491bf6b1fdab4d52cdc4e7aa7660f2f6a
4
- data.tar.gz: f9e1295f3208cb6b734d65d20afa41fcb46803b37b51b7d801ab56715fe24db8
3
+ metadata.gz: f9f7d2a86ea17670a7bcc69301db096ec2a2b667c7eb8f2c0df1ac28178eb823
4
+ data.tar.gz: 6dd4d03825d1a881fd7b721fea58af43b7c2f2c68f7ceb8bd8031a2fbfdab0de
5
5
  SHA512:
6
- metadata.gz: 80891d65d533e7fabac367d342c3884c0da607e5c815821ace691fe62e5ae393983e89c60cc226b69627b0ebb66bf7f1d91ae89f8be6d8baca9620498364c756
7
- data.tar.gz: 3e969bb24fbc9ec1668757e5ac152baf0052928937bd18d8c708a2031c416d8752cf8b85f3047427de82eafd008abd6126d212e3265fcfbfd9275b6ed4bc0b95
6
+ metadata.gz: 6cf0579fe72241ae904f417d1fd2f6e97ef347d8a3106b7069c232b8332440ef3eea5dc8b7c059516d3ad17903d207941d3e4c795768943950295fd345304248
7
+ data.tar.gz: 0b44037b06cc328543ed6809a5f5c0b675318902fcb36800559d1b100bf190f42e56d9249905a0ee50dd59cf0006a3fb4f17a31dcff10d6d86b70b9608d31842
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 2.11 2025-09-11
2
+
3
+ - Add mode param to `P2::Template` wrapper class
4
+
5
+ # 2.10 2025-09-11
6
+
7
+ - Add support for rendering XML, implement `Proc#render_xml`
8
+ - Fix handling of literal strings with double quotes
9
+ - Improve error handling for `P2::Error` exceptions
10
+
1
11
  # 2.9 2025-09-02
2
12
 
3
13
  - Tweak generated code to incorporate @byroot's
@@ -58,7 +58,7 @@ module P2
58
58
  def match_extension(node)
59
59
  return if node.receiver
60
60
  return if !P2::Extensions[node.name]
61
-
61
+
62
62
  ExtensionTagNode.new(node, self)
63
63
  end
64
64
 
data/lib/p2/compiler.rb CHANGED
@@ -14,15 +14,16 @@ module P2
14
14
  # generated optimized source code.
15
15
  #
16
16
  # @param proc [Proc] template
17
+ # @param mode [Symbol] compilation mode (:html, :xml)
17
18
  # @param wrap [bool] whether to wrap the generated code with a literal Proc definition
18
19
  # @return [Array] array containing the source map and generated code
19
- def self.compile_to_code(proc, wrap: true)
20
+ def self.compile_to_code(proc, mode: :html, wrap: true)
20
21
  ast = Sirop.to_ast(proc)
21
22
 
22
23
  # adjust ast root if proc is defined with proc {} / lambda {} syntax
23
24
  ast = ast.block if ast.is_a?(Prism::CallNode)
24
25
 
25
- compiler = new.with_source_map(proc, ast)
26
+ compiler = new(mode:).with_source_map(proc, ast)
26
27
  transformed_ast = TagTranslator.transform(ast.body, ast)
27
28
  compiler.format_compiled_template(transformed_ast, ast, wrap:, binding: proc.binding)
28
29
  [compiler.source_map, compiler.buffer]
@@ -37,10 +38,11 @@ module P2
37
38
  # compiled.render #=> '<h1>Hello, world!'
38
39
  #
39
40
  # @param proc [Proc] template
41
+ # @param mode [Symbol] compilation mode (:html, :xml)
40
42
  # @param wrap [bool] whether to wrap the generated code with a literal Proc definition
41
43
  # @return [Proc] compiled proc
42
- def self.compile(proc, wrap: true)
43
- source_map, code = compile_to_code(proc, wrap:)
44
+ def self.compile(proc, mode: :html, wrap: true)
45
+ source_map, code = compile_to_code(proc, mode:, wrap:)
44
46
  if ENV['DEBUG'] == '1'
45
47
  puts '*' * 40
46
48
  puts code
@@ -66,8 +68,9 @@ module P2
66
68
  attr_reader :source_map
67
69
 
68
70
  # Initializes a compiler.
69
- def initialize(**)
71
+ def initialize(mode:, **)
70
72
  super(**)
73
+ @mode = mode
71
74
  @pending_html_parts = []
72
75
  end
73
76
 
@@ -336,7 +339,7 @@ module P2
336
339
  block_params << format_code(params.keyword_rest) if params.keyword_rest
337
340
  end
338
341
  block_params = block_params.empty? ? '' : ", #{block_params.join(', ')}"
339
-
342
+
340
343
  emit(", &(proc { |__buffer__#{block_params}| #{block_body} }).compiled!")
341
344
  end
342
345
  emit(")")
@@ -421,11 +424,11 @@ module P2
421
424
  # @param node [Prism::Node] AST
422
425
  # @return [String] generated source code
423
426
  def format_code(node)
424
- Compiler.new(minimize_whitespace: true).to_source(node)
427
+ Compiler.new(mode: @mode, minimize_whitespace: true).to_source(node)
425
428
  end
426
429
 
427
430
  def format_inline_block(node)
428
- Compiler.new(minimize_whitespace: true).format_compiled_template(node, node, wrap: false, binding: @binding)
431
+ Compiler.new(mode: @mode, minimize_whitespace: true).format_compiled_template(node, node, wrap: false, binding: @binding)
429
432
  end
430
433
 
431
434
  # Formats a comma separated list of AST nodes. Used for formatting partial
@@ -434,7 +437,7 @@ module P2
434
437
  # @param list [Array<Prism::Node>] node list
435
438
  # @return [String] generated source code
436
439
  def format_code_comma_separated_nodes(list)
437
- compiler = self.class.new(minimize_whitespace: true)
440
+ compiler = Compiler.new(mode: @mode, minimize_whitespace: true)
438
441
  compiler.visit_comma_separated_nodes(list)
439
442
  compiler.buffer
440
443
  end
@@ -446,6 +449,8 @@ module P2
446
449
  # @param tag [String, Symbol] HTML tag
447
450
  # @return [bool] void or not
448
451
  def is_void_element?(tag)
452
+ return false if @mode == :xml
453
+
449
454
  VOID_TAGS.include?(tag.to_s)
450
455
  end
451
456
 
@@ -495,7 +500,9 @@ module P2
495
500
  def format_literal(node)
496
501
  case node
497
502
  when Prism::SymbolNode, Prism::StringNode
498
- node.unescaped
503
+ # since the value is copied verbatim into a quoted string, we need to
504
+ # add a backslash before any double quote.
505
+ node.unescaped.gsub('"', '\"')
499
506
  when Prism::IntegerNode, Prism::FloatNode
500
507
  node.value.to_s
501
508
  when Prism::InterpolatedStringNode
@@ -601,6 +608,7 @@ module P2
601
608
  last_loc = @html_loc_start
602
609
  @pending_html_parts.each do |(loc, part)|
603
610
  if (m = part.match(/^#\{(.+)\}$/m))
611
+ # interpolated part
604
612
  emit_html_buffer_push(concatenated, quotes: true) if !concatenated.empty?
605
613
  # adjust_whitespace(loc, advance_to_end: false)
606
614
  emit_html_buffer_push(m[1], loc:)
data/lib/p2/proc_ext.rb CHANGED
@@ -2,34 +2,40 @@
2
2
 
3
3
  require_relative './compiler'
4
4
 
5
- # Extensions to the Proc class
5
+ # Extensions to the Proc class.
6
6
  class ::Proc
7
- # Returns the compiled form code for the proc
7
+ # Returns the compiled form code for the proc.
8
8
  #
9
9
  # @return [String] compiled proc code
10
10
  def compiled_code
11
11
  P2::Compiler.compile_to_code(self).last
12
12
  end
13
13
 
14
+ # Returns the source map for the compiled proc.
15
+ #
16
+ # @return [Array<String>] source map
14
17
  def source_map
15
18
  loc = source_location
16
19
  fn = compiled? ? loc.first : P2::Compiler.source_location_to_fn(loc)
17
20
  P2::Compiler.source_map_store[fn]
18
21
  end
19
22
 
23
+ # Returns the AST for the proc.
24
+ #
25
+ # @return [Prism::Node] AST root
20
26
  def ast
21
27
  Sirop.to_ast(self)
22
28
  end
23
29
 
24
- # Returns true if proc is marked as compiled
30
+ # Returns true if proc is marked as compiled.
25
31
  #
26
32
  # @return [bool] is the proc marked as compiled
27
33
  def compiled?
28
34
  @is_compiled
29
35
  end
30
36
 
31
- # marks the proc as compiled, i.e. can render directly and takes a string
32
- # buffer as first argument
37
+ # Marks the proc as compiled, i.e. can render directly and takes a string
38
+ # buffer as first argument.
33
39
  #
34
40
  # @return [self]
35
41
  def compiled!
@@ -40,34 +46,43 @@ class ::Proc
40
46
  # Returns the compiled proc for the given proc. If marked as compiled, returns
41
47
  # self.
42
48
  #
49
+ # @param mode [Symbol] compilation mode (:html, :xml)
43
50
  # @return [Proc] compiled proc or self
44
- def compiled_proc
45
- @compiled_proc ||= @is_compiled ? self : compile
51
+ def compiled_proc(mode: :html)
52
+ @compiled_proc ||= @is_compiled ? self : compile(mode:)
46
53
  end
47
54
 
48
- # Compiles the proc into the compiled form
55
+ # Compiles the proc into the compiled form.
49
56
  #
57
+ # @param mode [Symbol] compilation mode (:html, :xml)
50
58
  # @return [Proc] compiled proc
51
- def compile
52
- P2::Compiler.compile(self).compiled!
53
- rescue Sirop::Error => e
54
- puts '!' * 40
55
- p self
56
- p e
59
+ def compile(mode: :html)
60
+ P2::Compiler.compile(self, mode:).compiled!
61
+ rescue Sirop::Error
57
62
  raise P2::Error, "Dynamically defined procs cannot be compiled"
58
63
  end
59
64
 
60
- # Renders the proc to HTML with the given arguments
65
+ # Renders the proc to HTML with the given arguments.
61
66
  #
62
67
  # @return [String] HTML string
63
68
  def render(*a, **b, &c)
64
69
  compiled_proc.(+'', *a, **b, &c)
65
70
  rescue Exception => e
66
- raise P2.translate_backtrace(e)
71
+ e.is_a?(P2::Error) ? raise : raise(P2.translate_backtrace(e))
67
72
  end
68
73
 
69
- # Renders the proc into the given buffer
74
+ # Renders the proc to XML with the given arguments.
70
75
  #
76
+ # @return [String] XML string
77
+ def render_xml(*a, **b, &c)
78
+ compiled_proc(mode: :xml).(+'', *a, **b, &c)
79
+ rescue Exception => e
80
+ e.is_a?(P2::Error) ? raise : raise(P2.translate_backtrace(e))
81
+ end
82
+
83
+ # Renders the proc to HTML with the given arguments into the given buffer.
84
+ #
85
+ # @param buf [String] buffer
71
86
  # @return [String] HTML string
72
87
  def render_to_buffer(buf, *a, **b, &c)
73
88
  compiled_proc.(buf, *a, **b, &c)
@@ -75,7 +90,7 @@ class ::Proc
75
90
  raise P2.translate_backtrace(e)
76
91
  end
77
92
 
78
- # Returns a proc that applies the given arguments to the original proc
93
+ # Returns a proc that applies the given arguments to the original proc.
79
94
  #
80
95
  # @return [Proc] applied proc
81
96
  def apply(*a, **b, &c)
@@ -91,7 +106,10 @@ class ::Proc
91
106
  }.compiled!
92
107
  end
93
108
 
94
- # Caches and returns
109
+ # Caches and returns the rendered HTML for the template with the given
110
+ # arguments.
111
+ #
112
+ # @return [String] HTML string
95
113
  def render_cached(*args, **kargs, &block)
96
114
  @render_cache ||= {}
97
115
  key = args.empty? && kargs.empty? && !block ? nil : [args, kargs, block&.source_location]
data/lib/p2/template.rb CHANGED
@@ -4,10 +4,25 @@ module P2
4
4
  # Template wrapper class. This class can be used to distinguish between P2
5
5
  # templates and other kinds of procs.
6
6
  class Template
7
- attr_reader :proc
8
- def initialize(proc) = @proc = proc
9
- def render(*, **, &) = @proc.render(*, **, &)
10
- def apply(*, **, &) = Template.new(@proc.apply(*, **, &))
11
- def compiled_proc = @proc.compiled_proc
7
+ attr_reader :proc, :mode
8
+
9
+ # @param proc [Proc] template proc
10
+ # @param mode [Symbol] mode (:html, :xml)
11
+ def initialize(proc, mode: :html)
12
+ @proc = proc
13
+ @mode = mode
14
+ end
15
+
16
+ def render(*, **, &)
17
+ (mode == :xml) ? @proc.render_xml(*, **, &) : @proc.render(*, **, &)
18
+ end
19
+
20
+ def apply(*, **, &)
21
+ Template.new(@proc.apply(*, **, &), mode: @mode)
22
+ end
23
+
24
+ def compiled_proc
25
+ @proc.compiled_proc(mode: @mode)
26
+ end
12
27
  end
13
28
  end
data/lib/p2/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module P2
4
- VERSION = '2.9'
4
+ VERSION = '2.11'
5
5
  end
data/lib/p2.rb CHANGED
@@ -16,7 +16,7 @@ module P2
16
16
  Extensions = {}
17
17
 
18
18
  # Registers extensions to the P2 syntax.
19
- #
19
+ #
20
20
  # @param spec [Hash] hash mapping symbols to procs
21
21
  # @return [self]
22
22
  def extension(spec)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: p2
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.9'
4
+ version: '2.11'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -135,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
135
  - !ruby/object:Gem::Version
136
136
  version: '0'
137
137
  requirements: []
138
- rubygems_version: 3.6.9
138
+ rubygems_version: 3.7.0.dev
139
139
  specification_version: 4
140
140
  summary: 'P2: component-based HTML templating for Ruby'
141
141
  test_files: []