p2 2.0 → 2.1

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: 37c651c3157074dde83015d5e4e6db8a44ef90b12a8dd890a585539e3eb80b4d
4
- data.tar.gz: 384d6a649bc089c24d578185f75a7e5a09b22825d70f1367e55b0e8a7f0e3cd8
3
+ metadata.gz: c34fa05073968d2f8aed0d766ec9b59275fdb061ce2a328ad18038ce0f986068
4
+ data.tar.gz: c0bab8233ffc293432b33a531e554ebc379ec7d796c2f4046e105b185e0c5f5e
5
5
  SHA512:
6
- metadata.gz: 6565857078969d64aced3c864d295ca9d4627b08bff842eda2578d8759022ac78c3553b3feef99550efee9498a5f0a943ffae2ca12f5dd001162165101bad557
7
- data.tar.gz: d0b758535f27c4ec60dac2649d79504c01fcc3e021e318259ea475215cd3d62efec1e360a9529c2bdc6bc1068ed31c44d6339fd44e99aae1bcb4bd8eb97983e8
6
+ metadata.gz: bb711aac468494ad590779e45a97fa9593d9a7e0e172389ae14125ba1576a4dda90bf5f796c56592d8a1b939cf00d2a69cd91386f5ef583453b2045bee333f5d
7
+ data.tar.gz: 8a1ee9990feb82379a01bd1971b00c844d3b0f3f9f0cc6531596619f47abf0e56c629d04e34a54f12594e7a73718e88854ab6056b30ad8019ab9a63d133e2101
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ # 2.1 2025-08-08
2
+
3
+ - Optimize output code: directly invoke component templates instead of calling
4
+ `P2.render_emit_call`. P2 is now
5
+ - Optimize output code: use separate pushes to buffer instead of interpolated
6
+ strings.
7
+ - Streamline API: `emit proc` => `render`, `emit str` => `raw`, `emit_markdown`
8
+ => `markdown`
9
+ - Optimize output code: add `frozen_string_literal` to top of compiled code
10
+ - Add more benchmarks (#1)
11
+ - Optimize output code: use ERB::Escape.html_escape instead of CGI.escape_html
12
+ (#2)
13
+ - Fix source map calculation
14
+
15
+ ## 2.0.1 2025-08-07
16
+
17
+ - Fix source map calculation
18
+
1
19
  ## 2.0 2025-08-07
2
20
 
3
21
  - Passes all HTML, compilation tests from Papercraft
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  P2
5
5
  </h1>
6
6
 
7
- <h4 align="center">Composable templating for Ruby</h4>
7
+ <h4 align="center">Functional HTML templating for Ruby</h4>
8
8
 
9
9
  <p align="center">
10
10
  <a href="http://rubygems.org/gems/p2">
@@ -24,9 +24,9 @@
24
24
 
25
25
  ## What is P2?
26
26
 
27
- P2 is a templating engine for dynamically producing [HTML](#html-templates). P2
28
- templates are expressed as Ruby procs, leading to easier debugging, better
29
- protection against HTML/XML injection attacks, and better code reuse.
27
+ P2 is a templating engine for dynamically producing HTML. P2 templates are
28
+ expressed as Ruby procs, leading to easier debugging, better protection against
29
+ HTML injection attacks, and better code reuse.
30
30
 
31
31
  P2 templates can be composed in a variety of ways, facilitating the usage of
32
32
  layout templates, and enabling a component-oriented approach to building complex
@@ -37,11 +37,8 @@ making the data flow easy to follow and understand. P2 also lets developers
37
37
  create derivative templates using full or partial parameter application.
38
38
 
39
39
  P2 includes built-in support for rendering Markdown (using
40
- [Kramdown](https://github.com/gettalong/kramdown/)).
41
-
42
- P2 automatically escapes all text emitted in templates according to the template
43
- type. For more information see the section on [escaping
44
- content](#escaping-content).
40
+ [Kramdown](https://github.com/gettalong/kramdown/)). P2 also automatically
41
+ escapes all text emitted in templates.
45
42
 
46
43
  ```ruby
47
44
  require 'p2'
@@ -64,6 +61,16 @@ hello.render(name: 'world')
64
61
  #=> "<html><head><title>Title</title></head><body><h1>Hello, world!</h1></body></html>"
65
62
  ```
66
63
 
64
+ P2 features:
65
+
66
+ - Express HTML using plain Ruby procs.
67
+ - Automatic compilation for super-fast execution (up to 2X faster than ERB templates).
68
+ - Deferred rendering using `defer`.
69
+ - Template composition (for uses such as layouts).
70
+ - Automatic conversion of backtraces for exceptions occurring while rendering,
71
+ in order to point to the correct spot in the original template code.
72
+
73
+
67
74
  ## Table of Content
68
75
 
69
76
  - [Installing P2](#installing-p2)
@@ -79,7 +86,7 @@ hello.render(name: 'world')
79
86
  - [Higher-Order Templates](#higher-order-templates)
80
87
  - [Layout Template Composition](#layout-template-composition)
81
88
  - [Emitting Raw HTML](#emitting-raw-html)
82
- - [Emitting a String with HTML Encoding](#emitting-a-string-with-html-encoding)
89
+ - [Emitting a String with HTML Escaping](#emitting-a-string-with-html-escaping)
83
90
  - [Emitting Markdown](#emitting-markdown)
84
91
  - [Deferred Evaluation](#deferred-evaluation)
85
92
  - [API Reference](#api-reference)
@@ -291,14 +298,14 @@ page.render('Hello from composed templates', [
291
298
  ```
292
299
 
293
300
  In addition to using templates defined as constants, you can also use
294
- non-constant templates by invoking the `#emit` method:
301
+ non-constant templates by invoking the `#render` method:
295
302
 
296
303
  ```ruby
297
304
  greeting = -> { span "Hello, world" }
298
305
 
299
306
  -> {
300
307
  div {
301
- emit greeting
308
+ render greeting
302
309
  }
303
310
  }
304
311
  ```
@@ -338,7 +345,7 @@ templates or injecting template parameters.
338
345
  Here is a higher-order template that takes a template as parameter:
339
346
 
340
347
  ```ruby
341
- div_wrap = -> { |inner| div { emit inner } }
348
+ div_wrap = -> { |inner| div { render inner } }
342
349
  greeter = -> { h1 'hi' }
343
350
  wrapped_greeter = div_wrap.apply(greeter)
344
351
  wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
@@ -375,7 +382,7 @@ default_layout = -> { |**params|
375
382
  article_layout = default_layout.apply { |title:, body:|
376
383
  article {
377
384
  h1 title
378
- emit_markdown body
385
+ markdown body
379
386
  }
380
387
  }
381
388
 
@@ -387,16 +394,16 @@ article_layout.render(
387
394
 
388
395
  ## Emitting Raw HTML
389
396
 
390
- Raw HTML can be emitted using `#emit`:
397
+ Raw HTML can be emitted using `#raw`:
391
398
 
392
399
  ```ruby
393
- wrapped = -> { |html| div { emit html } }
400
+ wrapped = -> { |html| div { raw html } }
394
401
  wrapped.render("<h1>hi</h1>") #=> "<div><h1>hi</h1></div>"
395
402
  ```
396
403
 
397
- ## Emitting a String with HTML Encoding
404
+ ## Emitting a String with HTML Escaping
398
405
 
399
- To emit a string with proper HTML encoding, without wrapping it in an HTML
406
+ To emit a string with proper HTML escaping, without wrapping it in an HTML
400
407
  element, use `#text`:
401
408
 
402
409
  ```ruby
@@ -407,19 +414,19 @@ element, use `#text`:
407
414
 
408
415
  Markdown is rendered using the
409
416
  [Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
410
- `#emit_markdown`:
417
+ `#markdown`:
411
418
 
412
419
  ```ruby
413
- template = -> { |md| div { emit_markdown md } }
420
+ template = -> { |md| div { markdown md } }
414
421
  template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
415
422
  ```
416
423
 
417
424
  [Kramdown
418
425
  options](https://kramdown.gettalong.org/options.html#available-options) can be
419
- specified by adding them to the `#emit_markdown` call:
426
+ specified by adding them to the `#markdown` call:
420
427
 
421
428
  ```ruby
422
- template = -> { |md| div { emit_markdown md, auto_ids: false } }
429
+ template = -> { |md| div { markdown md, auto_ids: false } }
423
430
  template.render("# title") #=> "<div><h1>title</h1></div>"
424
431
  ```
425
432
 
@@ -471,7 +478,7 @@ integrated into the page, and adds them to the page's `<head>` element:
471
478
  default_layout = -> { |**args|
472
479
  @dependencies = DependencyMananger.new
473
480
  head {
474
- defer { emit @dependencies.head_markup }
481
+ defer { render @dependencies.head_markup }
475
482
  }
476
483
  body { emit_yield **args }
477
484
  }
@@ -491,11 +498,11 @@ heading = proc { |text|
491
498
  }
492
499
 
493
500
  page = default_layout.apply {
494
- emit heading, "What's your favorite cheese?"
501
+ render heading, "What's your favorite cheese?"
495
502
 
496
- emit button, 'Beaufort', 'eat_beaufort()'
497
- emit button, 'Mont d''or', 'eat_montdor()'
498
- emit button, 'Époisses', 'eat_epoisses()'
503
+ render button, 'Beaufort', 'eat_beaufort()'
504
+ render button, 'Mont d''or', 'eat_montdor()'
505
+ render button, 'Époisses', 'eat_epoisses()'
499
506
  }
500
507
  ```
501
508
 
data/lib/p2/compiler.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'cgi'
4
4
  require 'sirop'
5
- require 'digest/md5'
5
+ require 'erb/escape'
6
6
 
7
7
  module P2
8
8
  class TagNode
@@ -48,7 +48,7 @@ module P2
48
48
  end
49
49
  end
50
50
 
51
- class EmitNode
51
+ class RenderNode
52
52
  attr_reader :call_node, :location, :block
53
53
 
54
54
  include Prism::DSL
@@ -107,7 +107,7 @@ module P2
107
107
  end
108
108
 
109
109
  def accept(visitor)
110
- visitor.visit_emit_node(self)
110
+ visitor.visit_render_node(self)
111
111
  end
112
112
  end
113
113
 
@@ -124,6 +124,19 @@ module P2
124
124
  end
125
125
  end
126
126
 
127
+ class RawNode
128
+ attr_reader :call_node, :location
129
+
130
+ def initialize(call_node, _compiler)
131
+ @call_node = call_node
132
+ @location = call_node.location
133
+ end
134
+
135
+ def accept(visitor)
136
+ visitor.visit_raw_node(self)
137
+ end
138
+ end
139
+
127
140
  class DeferNode
128
141
  attr_reader :call_node, :location, :block
129
142
 
@@ -172,13 +185,15 @@ module P2
172
185
  )
173
186
  when :raise
174
187
  super(node)
175
- when :emit, :e
176
- EmitNode.new(node, self)
188
+ when :render
189
+ RenderNode.new(node, self)
190
+ when :raw
191
+ RawNode.new(node, self)
177
192
  when :text
178
193
  TextNode.new(node, self)
179
194
  when :defer
180
195
  DeferNode.new(node, self)
181
- when :html5, :emit_markdown, :markdown
196
+ when :html5, :markdown
182
197
  CustomTagNode.new(node, self)
183
198
  else
184
199
  TagNode.new(node, self)
@@ -207,6 +222,10 @@ module P2
207
222
 
208
223
  def self.compile(proc, wrap: true)
209
224
  source_map, code = compile_to_code(proc, wrap:)
225
+ if ENV['DEBUG'] == '1'
226
+ puts '*' * 40
227
+ puts code
228
+ end
210
229
  eval(code, proc.binding, source_map[:compiled_fn])
211
230
  end
212
231
 
@@ -221,12 +240,12 @@ module P2
221
240
  end
222
241
 
223
242
  def with_source_map(orig_proc, orig_ast)
224
- ast_digest = Digest::MD5.hexdigest(orig_ast.inspect)
243
+ compiled_fn = "::(#{orig_proc.source_location.join(':')})"
225
244
  @source_map = {
226
245
  source_fn: orig_proc.source_location.first,
227
- compiled_fn: "::#{ast_digest}"
246
+ compiled_fn: compiled_fn
228
247
  }
229
- @source_map_line_ofs = 1
248
+ @source_map_line_ofs = 2
230
249
  self
231
250
  end
232
251
 
@@ -235,11 +254,12 @@ module P2
235
254
  @binding = binding
236
255
  visit(ast)
237
256
  flush_html_parts!(semicolon_prefix: true)
257
+ update_source_map
238
258
 
239
259
  source_code = @buffer
240
260
  @buffer = +''
241
261
  if wrap
242
- emit("(#{@source_map.inspect}).then { |src_map| ->(__buffer__")
262
+ emit("# frozen_string_literal: true\n(#{@source_map.inspect}).then { |src_map| ->(__buffer__")
243
263
 
244
264
  params = orig_ast.parameters
245
265
  params = params&.parameters
@@ -291,13 +311,13 @@ module P2
291
311
 
292
312
  if node.inner_text
293
313
  if is_static_node?(node.inner_text)
294
- emit_html(node.location, CGI.escape_html(format_literal(node.inner_text)))
314
+ emit_html(node.location, ERB::Escape.html_escape(format_literal(node.inner_text)))
295
315
  else
296
316
  convert_to_s = !is_string_type_node?(node.inner_text)
297
317
  if convert_to_s
298
- emit_html(node.location, "#\{CGI.escape_html((#{format_code(node.inner_text)}).to_s)}")
318
+ emit_html(node.location, interpolated("ERB::Escape.html_escape((#{format_code(node.inner_text)}).to_s)"))
299
319
  else
300
- emit_html(node.location, "#\{CGI.escape_html(#{format_code(node.inner_text)})}")
320
+ emit_html(node.location, interpolated("ERB::Escape.html_escape(#{format_code(node.inner_text)})"))
301
321
  end
302
322
  end
303
323
  end
@@ -319,34 +339,50 @@ module P2
319
339
  emit(');')
320
340
  end
321
341
 
322
- def visit_emit_node(node)
342
+ def visit_render_node(node)
343
+ args = node.call_node.arguments.arguments
344
+ first_arg = args.first
345
+
346
+ block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}.compiled!)"
347
+ block_embed = ", #{block_embed}" if block_embed && node.call_node.arguments
348
+
349
+ flush_html_parts!
350
+ adjust_whitespace(node.location)
351
+
352
+ if args.length == 1
353
+ emit("; #{format_code(first_arg)}.compiled_proc.(__buffer__#{block_embed})")
354
+ else
355
+ args_code = format_code_comma_separated_nodes(args[1..])
356
+ emit("; #{format_code(first_arg)}.compiled_proc.(__buffer__, #{args_code}#{block_embed})")
357
+ end
358
+ end
359
+
360
+ def visit_text_node(node)
361
+ return if !node.call_node.arguments
362
+
323
363
  args = node.call_node.arguments.arguments
324
364
  first_arg = args.first
325
365
  if args.length == 1
326
366
  if is_static_node?(first_arg)
327
- emit_html(node.location, format_literal(first_arg))
328
- elsif first_arg.is_a?(Prism::LambdaNode)
329
- visit(first_arg.body)
367
+ emit_html(node.location, ERB::Escape.html_escape(format_literal(first_arg)))
330
368
  else
331
- emit_html(node.location, "#\{P2.render_emit_call(#{format_code(first_arg)})}")
369
+ emit_html(node.location, interpolated("ERB::Escape.html_escape(#{format_code(first_arg)}.to_s)"))
332
370
  end
333
371
  else
334
- block_embed = node.block ? "&(->(__buffer__) #{format_code(node.block)}.compiled!)" : nil
335
- block_embed = ", #{block_embed}" if block_embed && node.call_node.arguments
336
- emit_html(node.location, "#\{P2.render_emit_call(#{format_code(node.call_node.arguments)}#{block_embed})}")
372
+ raise "Don't know how to compile #{node}"
337
373
  end
338
374
  end
339
375
 
340
- def visit_text_node(node)
376
+ def visit_raw_node(node)
341
377
  return if !node.call_node.arguments
342
378
 
343
379
  args = node.call_node.arguments.arguments
344
380
  first_arg = args.first
345
381
  if args.length == 1
346
382
  if is_static_node?(first_arg)
347
- emit_html(node.location, CGI.escape_html(format_literal(first_arg)))
383
+ emit_html(node.location, format_literal(first_arg))
348
384
  else
349
- emit_html(node.location, "#\{CGI.escape_html(#{format_code(first_arg)}.to_s)}")
385
+ emit_html(node.location, interpolated("(#{format_code(first_arg)}).to_s"))
350
386
  end
351
387
  else
352
388
  raise "Don't know how to compile #{node}"
@@ -381,11 +417,11 @@ module P2
381
417
  emit_html(node.location, '<!DOCTYPE html><html>')
382
418
  visit(node.block.body) if node.block
383
419
  emit_html(node.block.closing_loc, '</html>')
384
- when :emit_markdown, :markdown
420
+ when :markdown
385
421
  args = node.call_node.arguments
386
422
  return if !args
387
423
 
388
- emit_html(node.location, "#\{P2.markdown(#{format_code(args)})}")
424
+ emit_html(node.location, interpolated("P2.markdown(#{format_code(args)})"))
389
425
  end
390
426
  end
391
427
 
@@ -403,10 +439,20 @@ module P2
403
439
 
404
440
  private
405
441
 
406
- def format_code(node, klass = TemplateCompiler)
442
+ def interpolated(str)
443
+ "#\{#{str}}"
444
+ end
445
+
446
+ def format_code(node, klass = self.class)
407
447
  klass.new(minimize_whitespace: true).to_source(node)
408
448
  end
409
449
 
450
+ def format_code_comma_separated_nodes(list)
451
+ compiler = self.class.new(minimize_whitespace: true)
452
+ compiler.visit_comma_separated_nodes(list)
453
+ compiler.buffer
454
+ end
455
+
410
456
  VOID_TAGS = %w(area base br col embed hr img input link meta param source track wbr)
411
457
 
412
458
  def is_void_element?(tag)
@@ -487,7 +533,7 @@ module P2
487
533
  !is_static_node?(it.key) || !is_static_node?(it.value)
488
534
  end
489
535
 
490
- return "#\{P2.format_html_attrs(#{format_code(node)})}" if dynamic_attributes
536
+ return interpolated("P2.format_html_attrs(#{format_code(node)})") if dynamic_attributes
491
537
 
492
538
  parts = elements.map do
493
539
  key = it.key
@@ -521,11 +567,20 @@ module P2
521
567
  return if @pending_html_parts.empty?
522
568
 
523
569
  adjust_whitespace(@html_loc_start)
524
- if semicolon_prefix && @buffer =~ /[^\s]\s*$/m
525
- emit '; '
570
+
571
+ code = +''
572
+ part = +''
573
+
574
+ @pending_html_parts.each do
575
+ if (m = it.match(/^#\{(.+)\}$/m))
576
+ emit_buffer_push(code, part, quotes: true) if !part.empty?
577
+ emit_buffer_push(code, m[1])
578
+ else
579
+ part << it
580
+ end
526
581
  end
582
+ emit_buffer_push(code, part, quotes: true) if !part.empty?
527
583
 
528
- str = @pending_html_parts.join
529
584
  @pending_html_parts.clear
530
585
 
531
586
  @last_loc = @html_loc_end
@@ -535,7 +590,15 @@ module P2
535
590
  @html_loc_start = nil
536
591
  @html_loc_end = nil
537
592
 
538
- emit "__buffer__ << \"#{str}\""
593
+ emit code
594
+ end
595
+
596
+ def emit_buffer_push(buf, part, quotes: false)
597
+ return if part.empty?
598
+
599
+ q = quotes ? '"' : ''
600
+ buf << "; __buffer__ << #{q}#{part}#{q}"
601
+ part.clear
539
602
  end
540
603
 
541
604
  def emit_postlude
data/lib/p2/proc_ext.rb CHANGED
@@ -5,13 +5,15 @@ require_relative './compiler'
5
5
  # Extensions to the Proc class
6
6
  class ::Proc
7
7
  def compiled_code
8
- P2::TemplateCompiler.compile_to_code(self)
8
+ P2::TemplateCompiler.compile_to_code(self).last
9
9
  end
10
10
 
11
11
  def compiled?
12
12
  @is_compiled
13
13
  end
14
14
 
15
+ # marks the proc as compiled, i.e. can render directly and takes a string
16
+ # buffer as first argument
15
17
  def compiled!
16
18
  @is_compiled = true
17
19
  self
@@ -22,7 +24,7 @@ class ::Proc
22
24
  end
23
25
 
24
26
  def compile
25
- P2.compile(self).compiled!
27
+ P2::TemplateCompiler.compile(self).compiled!
26
28
  rescue Sirop::Error
27
29
  uncompiled_renderer
28
30
  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.0'
4
+ VERSION = '2.1'
5
5
  end
data/lib/p2.rb CHANGED
@@ -8,94 +8,84 @@ module P2
8
8
  # Exception class used to signal templating-related errors
9
9
  class Error < RuntimeError; end
10
10
 
11
- class << self
12
- def compile(proc)
13
- P2::TemplateCompiler.compile(proc)
14
- end
11
+ extend self
15
12
 
16
- def format_tag(tag)
17
- tag.to_s.gsub('_', '-')
18
- end
13
+ def format_tag(tag)
14
+ tag.to_s.gsub('_', '-')
15
+ end
19
16
 
20
- def format_html_attr_key(tag)
21
- tag.to_s.tr('_', '-')
22
- end
17
+ def format_html_attr_key(tag)
18
+ tag.to_s.tr('_', '-')
19
+ end
23
20
 
24
- def format_html_attrs(attrs)
25
- attrs.each_with_object(+'') do |(k, v), html|
26
- case v
27
- when nil, false
28
- when true
29
- html << ' ' if !html.empty?
30
- html << format_html_attr_key(k)
31
- else
32
- html << ' ' if !html.empty?
33
- v = v.join(' ') if v.is_a?(Array)
34
- html << "#{format_html_attr_key(k)}=\"#{v}\""
35
- end
21
+ def format_html_attrs(attrs)
22
+ attrs.each_with_object(+'') do |(k, v), html|
23
+ case v
24
+ when nil, false
25
+ when true
26
+ html << ' ' if !html.empty?
27
+ html << format_html_attr_key(k)
28
+ else
29
+ html << ' ' if !html.empty?
30
+ v = v.join(' ') if v.is_a?(Array)
31
+ html << "#{format_html_attr_key(k)}=\"#{v}\""
36
32
  end
37
33
  end
34
+ end
38
35
 
39
- def render_emit_call(o, *a, **b, &block)
40
- case o
41
- when nil
42
- # do nothing
43
- when ::Proc
44
- o.render(*a, **b, &block)
36
+ def translate_backtrace(e, source_map)
37
+ re = compute_source_map_re(source_map)
38
+ source_fn = source_map[:source_fn]
39
+ backtrace = e.backtrace.map {
40
+ if (m = it.match(re))
41
+ line = m[2].to_i
42
+ source_line = source_map[line] || "?(#{line})"
43
+ it.sub(m[1], "#{source_fn}:#{source_line}")
45
44
  else
46
- o.to_s
45
+ it
47
46
  end
48
- end
47
+ }
48
+ e.set_backtrace(backtrace)
49
+ end
49
50
 
50
- def translate_backtrace(e, source_map)
51
- re = /^(#{source_map[:compiled_fn]}\:(\d+))/
52
- source_fn = source_map[:source_fn]
53
- backtrace = e.backtrace.map {
54
- if (m = it.match(re))
55
- line = m[2].to_i
56
- source_line = source_map[line] || "?(#{line})"
57
- it.sub(m[1], "#{source_fn}:#{source_line}")
58
- else
59
- it
60
- end
61
- }
62
- e.set_backtrace(backtrace)
63
- end
51
+ def compute_source_map_re(source_map)
52
+ escaped = source_map[:compiled_fn].gsub(/[\(\)]/) { "\\#{it[0]}" }
53
+ /^(#{escaped}\:(\d+))/
54
+ end
64
55
 
65
- # Renders Markdown into HTML. The `opts` argument will be merged with the
66
- # default Kramdown options in order to change the rendering behaviour.
67
- #
68
- # @param markdown [String] Markdown
69
- # @param opts [Hash] Kramdown option overrides
70
- # @return [String] HTML
71
- def markdown(markdown, **opts)
72
- # require relevant deps on use
73
- require 'kramdown'
74
- require 'rouge'
75
- require 'kramdown-parser-gfm'
76
-
77
- opts = default_kramdown_options.merge(opts)
78
- Kramdown::Document.new(markdown, **opts).to_html
79
- end
56
+ # Renders Markdown into HTML. The `opts` argument will be merged with the
57
+ # default Kramdown options in order to change the rendering behaviour.
58
+ #
59
+ # @param markdown [String] Markdown
60
+ # @param opts [Hash] Kramdown option overrides
61
+ # @return [String] HTML
62
+ def markdown(markdown, **opts)
63
+ # require relevant deps on use
64
+ require 'kramdown'
65
+ require 'rouge'
66
+ require 'kramdown-parser-gfm'
67
+
68
+ opts = default_kramdown_options.merge(opts)
69
+ Kramdown::Document.new(markdown, **opts).to_html
70
+ end
80
71
 
81
- # Returns the default Kramdown options used for rendering Markdown.
82
- #
83
- # @return [Hash] Kramdown options
84
- def default_kramdown_options
85
- @default_kramdown_options ||= {
86
- entity_output: :numeric,
87
- syntax_highlighter: :rouge,
88
- input: 'GFM',
89
- hard_wrap: false
90
- }
91
- end
72
+ # Returns the default Kramdown options used for rendering Markdown.
73
+ #
74
+ # @return [Hash] Kramdown options
75
+ def default_kramdown_options
76
+ @default_kramdown_options ||= {
77
+ entity_output: :numeric,
78
+ syntax_highlighter: :rouge,
79
+ input: 'GFM',
80
+ hard_wrap: false
81
+ }
82
+ end
92
83
 
93
- # Sets the default Kramdown options used for rendering Markdown.
94
- #
95
- # @param opts [Hash] Kramdown options
96
- # @return [void]
97
- def default_kramdown_options=(opts)
98
- @default_kramdown_options = opts
99
- end
84
+ # Sets the default Kramdown options used for rendering Markdown.
85
+ #
86
+ # @param opts [Hash] Kramdown options
87
+ # @return [void]
88
+ def default_kramdown_options=(opts)
89
+ @default_kramdown_options = opts
100
90
  end
101
91
  end
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.0'
4
+ version: '2.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.8.1
18
+ version: 0.8.3
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.8.1
25
+ version: 0.8.3
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: kramdown
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -111,9 +111,8 @@ homepage: http://github.com/digital-fabric/p2
111
111
  licenses:
112
112
  - MIT
113
113
  metadata:
114
- source_code_uri: https://github.com/digital-fabric/p2
115
- documentation_uri: https://www.rubydoc.info/gems/p2
116
114
  homepage_uri: https://github.com/digital-fabric/p2
115
+ documentation_uri: https://www.rubydoc.info/gems/p2
117
116
  changelog_uri: https://github.com/digital-fabric/p2/blob/master/CHANGELOG.md
118
117
  rdoc_options:
119
118
  - "--title"