p2 2.0.1 → 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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +19 -19
- data/lib/p2/compiler.rb +94 -32
- data/lib/p2/proc_ext.rb +3 -1
- data/lib/p2/version.rb +1 -1
- data/lib/p2.rb +67 -77
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c34fa05073968d2f8aed0d766ec9b59275fdb061ce2a328ad18038ce0f986068
|
4
|
+
data.tar.gz: c0bab8233ffc293432b33a531e554ebc379ec7d796c2f4046e105b185e0c5f5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb711aac468494ad590779e45a97fa9593d9a7e0e172389ae14125ba1576a4dda90bf5f796c56592d8a1b939cf00d2a69cd91386f5ef583453b2045bee333f5d
|
7
|
+
data.tar.gz: 8a1ee9990feb82379a01bd1971b00c844d3b0f3f9f0cc6531596619f47abf0e56c629d04e34a54f12594e7a73718e88854ab6056b30ad8019ab9a63d133e2101
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
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
|
+
|
1
15
|
## 2.0.1 2025-08-07
|
2
16
|
|
3
17
|
- Fix source map calculation
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
P2
|
5
5
|
</h1>
|
6
6
|
|
7
|
-
<h4 align="center">
|
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">
|
@@ -86,7 +86,7 @@ P2 features:
|
|
86
86
|
- [Higher-Order Templates](#higher-order-templates)
|
87
87
|
- [Layout Template Composition](#layout-template-composition)
|
88
88
|
- [Emitting Raw HTML](#emitting-raw-html)
|
89
|
-
- [Emitting a String with HTML
|
89
|
+
- [Emitting a String with HTML Escaping](#emitting-a-string-with-html-escaping)
|
90
90
|
- [Emitting Markdown](#emitting-markdown)
|
91
91
|
- [Deferred Evaluation](#deferred-evaluation)
|
92
92
|
- [API Reference](#api-reference)
|
@@ -298,14 +298,14 @@ page.render('Hello from composed templates', [
|
|
298
298
|
```
|
299
299
|
|
300
300
|
In addition to using templates defined as constants, you can also use
|
301
|
-
non-constant templates by invoking the `#
|
301
|
+
non-constant templates by invoking the `#render` method:
|
302
302
|
|
303
303
|
```ruby
|
304
304
|
greeting = -> { span "Hello, world" }
|
305
305
|
|
306
306
|
-> {
|
307
307
|
div {
|
308
|
-
|
308
|
+
render greeting
|
309
309
|
}
|
310
310
|
}
|
311
311
|
```
|
@@ -345,7 +345,7 @@ templates or injecting template parameters.
|
|
345
345
|
Here is a higher-order template that takes a template as parameter:
|
346
346
|
|
347
347
|
```ruby
|
348
|
-
div_wrap = -> { |inner| div {
|
348
|
+
div_wrap = -> { |inner| div { render inner } }
|
349
349
|
greeter = -> { h1 'hi' }
|
350
350
|
wrapped_greeter = div_wrap.apply(greeter)
|
351
351
|
wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
@@ -382,7 +382,7 @@ default_layout = -> { |**params|
|
|
382
382
|
article_layout = default_layout.apply { |title:, body:|
|
383
383
|
article {
|
384
384
|
h1 title
|
385
|
-
|
385
|
+
markdown body
|
386
386
|
}
|
387
387
|
}
|
388
388
|
|
@@ -394,16 +394,16 @@ article_layout.render(
|
|
394
394
|
|
395
395
|
## Emitting Raw HTML
|
396
396
|
|
397
|
-
Raw HTML can be emitted using `#
|
397
|
+
Raw HTML can be emitted using `#raw`:
|
398
398
|
|
399
399
|
```ruby
|
400
|
-
wrapped = -> { |html| div {
|
400
|
+
wrapped = -> { |html| div { raw html } }
|
401
401
|
wrapped.render("<h1>hi</h1>") #=> "<div><h1>hi</h1></div>"
|
402
402
|
```
|
403
403
|
|
404
|
-
## Emitting a String with HTML
|
404
|
+
## Emitting a String with HTML Escaping
|
405
405
|
|
406
|
-
To emit a string with proper HTML
|
406
|
+
To emit a string with proper HTML escaping, without wrapping it in an HTML
|
407
407
|
element, use `#text`:
|
408
408
|
|
409
409
|
```ruby
|
@@ -414,19 +414,19 @@ element, use `#text`:
|
|
414
414
|
|
415
415
|
Markdown is rendered using the
|
416
416
|
[Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
|
417
|
-
`#
|
417
|
+
`#markdown`:
|
418
418
|
|
419
419
|
```ruby
|
420
|
-
template = -> { |md| div {
|
420
|
+
template = -> { |md| div { markdown md } }
|
421
421
|
template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
|
422
422
|
```
|
423
423
|
|
424
424
|
[Kramdown
|
425
425
|
options](https://kramdown.gettalong.org/options.html#available-options) can be
|
426
|
-
specified by adding them to the `#
|
426
|
+
specified by adding them to the `#markdown` call:
|
427
427
|
|
428
428
|
```ruby
|
429
|
-
template = -> { |md| div {
|
429
|
+
template = -> { |md| div { markdown md, auto_ids: false } }
|
430
430
|
template.render("# title") #=> "<div><h1>title</h1></div>"
|
431
431
|
```
|
432
432
|
|
@@ -478,7 +478,7 @@ integrated into the page, and adds them to the page's `<head>` element:
|
|
478
478
|
default_layout = -> { |**args|
|
479
479
|
@dependencies = DependencyMananger.new
|
480
480
|
head {
|
481
|
-
defer {
|
481
|
+
defer { render @dependencies.head_markup }
|
482
482
|
}
|
483
483
|
body { emit_yield **args }
|
484
484
|
}
|
@@ -498,11 +498,11 @@ heading = proc { |text|
|
|
498
498
|
}
|
499
499
|
|
500
500
|
page = default_layout.apply {
|
501
|
-
|
501
|
+
render heading, "What's your favorite cheese?"
|
502
502
|
|
503
|
-
|
504
|
-
|
505
|
-
|
503
|
+
render button, 'Beaufort', 'eat_beaufort()'
|
504
|
+
render button, 'Mont d''or', 'eat_montdor()'
|
505
|
+
render button, 'Époisses', 'eat_epoisses()'
|
506
506
|
}
|
507
507
|
```
|
508
508
|
|
data/lib/p2/compiler.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'cgi'
|
4
4
|
require 'sirop'
|
5
|
-
require '
|
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
|
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.
|
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 :
|
176
|
-
|
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, :
|
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
|
-
|
243
|
+
compiled_fn = "::(#{orig_proc.source_location.join(':')})"
|
225
244
|
@source_map = {
|
226
245
|
source_fn: orig_proc.source_location.first,
|
227
|
-
compiled_fn:
|
246
|
+
compiled_fn: compiled_fn
|
228
247
|
}
|
229
|
-
@source_map_line_ofs =
|
248
|
+
@source_map_line_ofs = 2
|
230
249
|
self
|
231
250
|
end
|
232
251
|
|
@@ -240,7 +259,7 @@ module P2
|
|
240
259
|
source_code = @buffer
|
241
260
|
@buffer = +''
|
242
261
|
if wrap
|
243
|
-
emit("(#{@source_map.inspect}).then { |src_map| ->(__buffer__")
|
262
|
+
emit("# frozen_string_literal: true\n(#{@source_map.inspect}).then { |src_map| ->(__buffer__")
|
244
263
|
|
245
264
|
params = orig_ast.parameters
|
246
265
|
params = params&.parameters
|
@@ -292,13 +311,13 @@ module P2
|
|
292
311
|
|
293
312
|
if node.inner_text
|
294
313
|
if is_static_node?(node.inner_text)
|
295
|
-
emit_html(node.location,
|
314
|
+
emit_html(node.location, ERB::Escape.html_escape(format_literal(node.inner_text)))
|
296
315
|
else
|
297
316
|
convert_to_s = !is_string_type_node?(node.inner_text)
|
298
317
|
if convert_to_s
|
299
|
-
emit_html(node.location, "
|
318
|
+
emit_html(node.location, interpolated("ERB::Escape.html_escape((#{format_code(node.inner_text)}).to_s)"))
|
300
319
|
else
|
301
|
-
emit_html(node.location, "
|
320
|
+
emit_html(node.location, interpolated("ERB::Escape.html_escape(#{format_code(node.inner_text)})"))
|
302
321
|
end
|
303
322
|
end
|
304
323
|
end
|
@@ -320,34 +339,50 @@ module P2
|
|
320
339
|
emit(');')
|
321
340
|
end
|
322
341
|
|
323
|
-
def
|
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
|
+
|
324
363
|
args = node.call_node.arguments.arguments
|
325
364
|
first_arg = args.first
|
326
365
|
if args.length == 1
|
327
366
|
if is_static_node?(first_arg)
|
328
|
-
emit_html(node.location, format_literal(first_arg))
|
329
|
-
elsif first_arg.is_a?(Prism::LambdaNode)
|
330
|
-
visit(first_arg.body)
|
367
|
+
emit_html(node.location, ERB::Escape.html_escape(format_literal(first_arg)))
|
331
368
|
else
|
332
|
-
emit_html(node.location, "
|
369
|
+
emit_html(node.location, interpolated("ERB::Escape.html_escape(#{format_code(first_arg)}.to_s)"))
|
333
370
|
end
|
334
371
|
else
|
335
|
-
|
336
|
-
block_embed = ", #{block_embed}" if block_embed && node.call_node.arguments
|
337
|
-
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}"
|
338
373
|
end
|
339
374
|
end
|
340
375
|
|
341
|
-
def
|
376
|
+
def visit_raw_node(node)
|
342
377
|
return if !node.call_node.arguments
|
343
378
|
|
344
379
|
args = node.call_node.arguments.arguments
|
345
380
|
first_arg = args.first
|
346
381
|
if args.length == 1
|
347
382
|
if is_static_node?(first_arg)
|
348
|
-
emit_html(node.location,
|
383
|
+
emit_html(node.location, format_literal(first_arg))
|
349
384
|
else
|
350
|
-
emit_html(node.location, "
|
385
|
+
emit_html(node.location, interpolated("(#{format_code(first_arg)}).to_s"))
|
351
386
|
end
|
352
387
|
else
|
353
388
|
raise "Don't know how to compile #{node}"
|
@@ -382,11 +417,11 @@ module P2
|
|
382
417
|
emit_html(node.location, '<!DOCTYPE html><html>')
|
383
418
|
visit(node.block.body) if node.block
|
384
419
|
emit_html(node.block.closing_loc, '</html>')
|
385
|
-
when :
|
420
|
+
when :markdown
|
386
421
|
args = node.call_node.arguments
|
387
422
|
return if !args
|
388
423
|
|
389
|
-
emit_html(node.location, "
|
424
|
+
emit_html(node.location, interpolated("P2.markdown(#{format_code(args)})"))
|
390
425
|
end
|
391
426
|
end
|
392
427
|
|
@@ -404,10 +439,20 @@ module P2
|
|
404
439
|
|
405
440
|
private
|
406
441
|
|
407
|
-
def
|
442
|
+
def interpolated(str)
|
443
|
+
"#\{#{str}}"
|
444
|
+
end
|
445
|
+
|
446
|
+
def format_code(node, klass = self.class)
|
408
447
|
klass.new(minimize_whitespace: true).to_source(node)
|
409
448
|
end
|
410
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
|
+
|
411
456
|
VOID_TAGS = %w(area base br col embed hr img input link meta param source track wbr)
|
412
457
|
|
413
458
|
def is_void_element?(tag)
|
@@ -488,7 +533,7 @@ module P2
|
|
488
533
|
!is_static_node?(it.key) || !is_static_node?(it.value)
|
489
534
|
end
|
490
535
|
|
491
|
-
return "
|
536
|
+
return interpolated("P2.format_html_attrs(#{format_code(node)})") if dynamic_attributes
|
492
537
|
|
493
538
|
parts = elements.map do
|
494
539
|
key = it.key
|
@@ -522,11 +567,20 @@ module P2
|
|
522
567
|
return if @pending_html_parts.empty?
|
523
568
|
|
524
569
|
adjust_whitespace(@html_loc_start)
|
525
|
-
|
526
|
-
|
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
|
527
581
|
end
|
582
|
+
emit_buffer_push(code, part, quotes: true) if !part.empty?
|
528
583
|
|
529
|
-
str = @pending_html_parts.join
|
530
584
|
@pending_html_parts.clear
|
531
585
|
|
532
586
|
@last_loc = @html_loc_end
|
@@ -536,7 +590,15 @@ module P2
|
|
536
590
|
@html_loc_start = nil
|
537
591
|
@html_loc_end = nil
|
538
592
|
|
539
|
-
emit
|
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
|
540
602
|
end
|
541
603
|
|
542
604
|
def emit_postlude
|
data/lib/p2/proc_ext.rb
CHANGED
@@ -12,6 +12,8 @@ class ::Proc
|
|
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
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
|
-
|
12
|
-
def compile(proc)
|
13
|
-
P2::TemplateCompiler.compile(proc)
|
14
|
-
end
|
11
|
+
extend self
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
def format_tag(tag)
|
14
|
+
tag.to_s.gsub('_', '-')
|
15
|
+
end
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
def format_html_attr_key(tag)
|
18
|
+
tag.to_s.tr('_', '-')
|
19
|
+
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
45
|
+
it
|
47
46
|
end
|
48
|
-
|
47
|
+
}
|
48
|
+
e.set_backtrace(backtrace)
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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.
|
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.
|
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.
|
25
|
+
version: 0.8.3
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: kramdown
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,20 +93,6 @@ dependencies:
|
|
93
93
|
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: 2.7.2
|
96
|
-
- !ruby/object:Gem::Dependency
|
97
|
-
name: tilt
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
99
|
-
requirements:
|
100
|
-
- - "~>"
|
101
|
-
- !ruby/object:Gem::Version
|
102
|
-
version: 2.2.0
|
103
|
-
type: :development
|
104
|
-
prerelease: false
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: 2.2.0
|
110
96
|
email: sharon@noteflakes.com
|
111
97
|
executables: []
|
112
98
|
extensions: []
|