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 +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/p2/compiler/tag_translator.rb +1 -1
- data/lib/p2/compiler.rb +18 -10
- data/lib/p2/proc_ext.rb +37 -19
- data/lib/p2/template.rb +20 -5
- data/lib/p2/version.rb +1 -1
- data/lib/p2.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9f7d2a86ea17670a7bcc69301db096ec2a2b667c7eb8f2c0df1ac28178eb823
|
4
|
+
data.tar.gz: 6dd4d03825d1a881fd7b721fea58af43b7c2f2c68f7ceb8bd8031a2fbfdab0de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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 =
|
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
|
-
|
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
|
-
#
|
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
|
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
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def
|
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
data/lib/p2.rb
CHANGED
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.
|
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.
|
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: []
|