papercraft 0.8 → 0.9
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/README.md +50 -105
- data/lib/papercraft/component.rb +93 -6
- data/lib/papercraft/html.rb +36 -4
- data/lib/papercraft/renderer.rb +76 -37
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +23 -11
- metadata +47 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1f7aa5872a78830c1a5662f055639ae2012b5b9d0f029640dce041f58d1ef4e
|
4
|
+
data.tar.gz: 0c5acc8b14254c63db2132caab42ce003a8ae4713ae6daea7225d0390a420219
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02bf1f7bc1300f4fa7871a6942c6a2eae2dd2f8202c7f3cc354171806f82e8a68262c3ca5e2232b7b6dcf6788a33b134b5811641561fa5baba3b7c342643b082
|
7
|
+
data.tar.gz: 97a68a0f854f631a6a36b52a350faae4b1da1e337b27701209c6eec9dc16c62856555fdff3ab3d8f33e144e7fed8dc892b22433f38dd3f951a04010f710b63c4
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -2,26 +2,36 @@
|
|
2
2
|
|
3
3
|
[INSTALL](#installing-papercraft) |
|
4
4
|
[TUTORIAL](#getting-started) |
|
5
|
-
[
|
6
|
-
[REFERENCE](#api-reference)
|
5
|
+
[DOCS](https://www.rubydoc.info/gems/papercraft)
|
7
6
|
|
8
7
|
## What is Papercraft?
|
9
8
|
|
9
|
+
```ruby
|
10
|
+
require 'papercraft'
|
11
|
+
|
12
|
+
page = H { |*args|
|
13
|
+
html {
|
14
|
+
head { }
|
15
|
+
body { emit_yield *args }
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
hello = H.apply { |name| h1 "Hello, #{name}!" }
|
20
|
+
hello.render('world')
|
21
|
+
#=> "<html><head/><body><h1>Hello, world!</h1></body></html>"
|
22
|
+
```
|
23
|
+
|
10
24
|
Papercraft is an HTML templating engine for Ruby that offers the following
|
11
25
|
features:
|
12
26
|
|
13
|
-
- HTML templating using plain Ruby syntax
|
27
|
+
- HTML and XML templating using plain Ruby syntax
|
14
28
|
- Minimal boilerplate
|
15
29
|
- Mix logic and tags freely
|
16
|
-
-
|
17
|
-
- Automatic HTML escaping
|
30
|
+
- Automatic HTML and XML escaping
|
18
31
|
- Composable components
|
32
|
+
- Explicit parameter passing to nested components
|
19
33
|
- Higher order components
|
20
|
-
- Built-in support for rendering Markdown
|
21
|
-
|
22
|
-
> **Note** Papercraft is a new library and as such may be missing features and
|
23
|
-
> contain bugs. Also, its API may change unexpectedly. Your issue reports and
|
24
|
-
> code contributions are most welcome!
|
34
|
+
- Built-in support for rendering [Markdown](#emitting-markdown)
|
25
35
|
|
26
36
|
With Papercraft you can structure your templates as nested HTML components, in a
|
27
37
|
somewhat similar fashion to React.
|
@@ -48,7 +58,8 @@ To use Papercraft in your code just require it:
|
|
48
58
|
require 'papercraft'
|
49
59
|
```
|
50
60
|
|
51
|
-
To create a template use `Papercraft.new` or the global method
|
61
|
+
To create a template use `Papercraft::Component.new` or the global method
|
62
|
+
`Kernel#H`:
|
52
63
|
|
53
64
|
```ruby
|
54
65
|
# can also use Papercraft.new
|
@@ -114,8 +125,9 @@ H { p "foobar", class: 'important' }.render #=> "<p class=\"important\">foobar</
|
|
114
125
|
|
115
126
|
## Template parameters
|
116
127
|
|
117
|
-
|
118
|
-
template on
|
128
|
+
In Papercraft, parameters are always passed explicitly. This means that template
|
129
|
+
parameters are specified as block parameters, and are passed to the template on
|
130
|
+
rendering:
|
119
131
|
|
120
132
|
```ruby
|
121
133
|
greeting = H { |name| h1 "Hello, #{name}!" }
|
@@ -328,110 +340,43 @@ H { str 'hi&lo' }.render #=> "hi&lo"
|
|
328
340
|
|
329
341
|
## Emitting Markdown
|
330
342
|
|
331
|
-
|
343
|
+
Markdown is rendered using the
|
344
|
+
[Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
|
345
|
+
`#emit_markdown`:
|
332
346
|
|
333
347
|
```ruby
|
334
348
|
template = H { |md| div { emit_markdown md } }
|
335
|
-
template.render("Here's some *Markdown*") #=> "<div>Here's some <em>Markdown</em
|
349
|
+
template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
|
336
350
|
```
|
337
351
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
together pieces of HTML. Feel free to explore the API!
|
352
|
+
[Kramdown
|
353
|
+
options](https://kramdown.gettalong.org/options.html#available-options) can be
|
354
|
+
specified by adding them to the `#emit_markdown` call:
|
342
355
|
|
343
|
-
|
356
|
+
```ruby
|
357
|
+
template = H { |md| div { emit_markdown md, auto_ids: false } }
|
358
|
+
template.render("# title") #=> "<div><h1>title</h1></div>"
|
359
|
+
```
|
344
360
|
|
345
|
-
|
346
|
-
takes an item component as an argument. The `List` component can be reused for
|
347
|
-
rendering any kind of unordered list, and with any kind of item component:
|
361
|
+
The default Kramdown options are:
|
348
362
|
|
349
363
|
```ruby
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
li { emit item_component }
|
356
|
-
}
|
357
|
-
}
|
358
|
-
}
|
359
|
-
}
|
364
|
+
{
|
365
|
+
entity_output: :numeric,
|
366
|
+
syntax_highlighter: :rouge,
|
367
|
+
input: 'GFM',
|
368
|
+
hard_wrap: false
|
360
369
|
}
|
361
|
-
|
362
|
-
TodoItem = H {
|
363
|
-
span item.text, class: item.completed ? 'completed' : 'pending'
|
364
|
-
}
|
365
|
-
|
366
|
-
def todo_list(items)
|
367
|
-
H {
|
368
|
-
div { List(items, TodoItem) }
|
369
|
-
}
|
370
|
-
end
|
371
370
|
```
|
372
371
|
|
373
|
-
|
374
|
-
|
375
|
-
#### `Papercraft#initialize(**context, &block)` a.k.a. `Kernel#H`
|
376
|
-
|
377
|
-
- `context`: local context hash
|
378
|
-
- `block`: template block
|
372
|
+
The deafult options can be configured by accessing
|
373
|
+
`Papercraft::HTML.kramdown_options`:
|
379
374
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
#### `Papercraft#render(**context)`
|
385
|
-
|
386
|
-
- `context`: global context hash
|
387
|
-
|
388
|
-
Renders the template with an optional [global context](#global-context)
|
389
|
-
hash.
|
390
|
-
|
391
|
-
#### Methods accessible inside template blocks
|
392
|
-
|
393
|
-
#### `#<tag/component>(*args, **props, &block)`
|
394
|
-
|
395
|
-
- `args`: tag arguments. For an HTML tag Papercraft expects a single `String`
|
396
|
-
argument containing the inner text of the tag.
|
397
|
-
- `props`: hash of tag attributes
|
398
|
-
- `block`: inner HTML block
|
399
|
-
|
400
|
-
Adds a tag or component to the current template. If the method name starts with
|
401
|
-
an upper-case letter, it is considered a [component](#templates-as-components).
|
402
|
-
|
403
|
-
If a text argument is given for a tag, it will be escaped.
|
404
|
-
|
405
|
-
#### `#cache(*vary, &block)`
|
406
|
-
|
407
|
-
- `vary`: variables used in cached block. The given values will be used to
|
408
|
-
create a separate cache entry.
|
409
|
-
- `block`: inner HTML block
|
410
|
-
|
411
|
-
Caches the markup in the given block, storing it in the Papercraft cache store.
|
412
|
-
If a cache entry for the given block is found, it will be used instead of
|
413
|
-
invoking the block. If one or more variables given, those will be used to create
|
414
|
-
a separate cache entry.
|
415
|
-
|
416
|
-
#### `#context`
|
417
|
-
|
418
|
-
Accesses the [global context](#global-context).
|
419
|
-
|
420
|
-
#### `#emit(object)` a.k.a. `#e(object)`
|
421
|
-
|
422
|
-
- `object`: `Proc`, `Papercraft` instance or `String`
|
423
|
-
|
424
|
-
Adds the given object to the current template. If a `String` is given, it is
|
425
|
-
rendered verbatim, i.e. without escaping.
|
426
|
-
|
427
|
-
#### `html5(&block)`
|
428
|
-
|
429
|
-
- `block`: inner HTML block
|
430
|
-
|
431
|
-
Adds an HTML5 `doctype` tag, followed by an `html` tag with the given block.
|
432
|
-
|
433
|
-
#### `#text(data)`
|
375
|
+
```ruby
|
376
|
+
Papercraft::HTML.kramdown_options[:auto_ids] = false
|
377
|
+
```
|
434
378
|
|
435
|
-
|
379
|
+
## Documentation
|
436
380
|
|
437
|
-
|
381
|
+
The complete documentation for this library can be found
|
382
|
+
[here](https://www.rubydoc.info/gems/papercraft).
|
data/lib/papercraft/component.rb
CHANGED
@@ -3,18 +3,19 @@
|
|
3
3
|
require_relative './html'
|
4
4
|
|
5
5
|
module Papercraft
|
6
|
+
|
6
7
|
# Component represents a distinct, reusable HTML template. A component can
|
7
8
|
# include other components, and also be nested inside other components.
|
8
9
|
#
|
9
10
|
# Since in Papercraft HTML is expressed using blocks (or procs,) the Component
|
10
11
|
# class is simply a special kind of Proc, which has some enhanced
|
11
12
|
# capabilities, allowing it to be easily composed in a variety of ways.
|
12
|
-
|
13
|
+
#
|
13
14
|
# Components are usually created using the global methods `H` or `X`, for HTML
|
14
15
|
# or XML templates, respectively:
|
15
16
|
#
|
16
|
-
# greeter = H { |name| h1 "Hello, #{name}!" }
|
17
|
-
# "<h1>Hello, world!</h1>"
|
17
|
+
# greeter = H { |name| h1 "Hello, #{name}!" }
|
18
|
+
# greeter.render('world') #=> "<h1>Hello, world!</h1>"
|
18
19
|
#
|
19
20
|
# Components can also be created using the normal constructor:
|
20
21
|
#
|
@@ -23,9 +24,71 @@ module Papercraft
|
|
23
24
|
#
|
24
25
|
# In the component block, HTML elements are created by simply calling
|
25
26
|
# unqualified methods:
|
27
|
+
#
|
28
|
+
# page_layout = H {
|
29
|
+
# html5 {
|
30
|
+
# head {
|
31
|
+
# title 'foo'
|
32
|
+
# }
|
33
|
+
# body {
|
34
|
+
# h1 "Hello, world!"
|
35
|
+
# }
|
36
|
+
# }
|
37
|
+
# }
|
38
|
+
#
|
39
|
+
# Papercraft components can take explicit parameters in order to render
|
40
|
+
# dynamic content. This can be in the form of regular or named parameters. The
|
41
|
+
# `greeter` template shown above takes a single `name` parameter. Here's how a
|
42
|
+
# anchor component could be implemented with named parameters:
|
43
|
+
#
|
44
|
+
# anchor = H { |uri: , text: | a(text, href: uri) }
|
45
|
+
#
|
46
|
+
# The above component could later be rendered by passing the needed arguments:
|
47
|
+
#
|
48
|
+
# anchor.render(uri: 'https://example.com', text: 'Example')
|
49
|
+
#
|
50
|
+
# ## Component Composition
|
51
|
+
#
|
52
|
+
# A component can be included in another component using the `emit` method:
|
53
|
+
#
|
54
|
+
# links = H {
|
55
|
+
# emit anchor, uri: '/posts', text: 'Posts'
|
56
|
+
# emit anchor, uri: '/archive', text: 'Archive'
|
57
|
+
# emit anchor, uri: '/about', text: 'About'
|
58
|
+
# }
|
59
|
+
#
|
60
|
+
# Another way of composing components is to pass the components themselves as
|
61
|
+
# parameters:
|
62
|
+
#
|
63
|
+
# links = H { |anchors|
|
64
|
+
# anchors.each { |a| emit a }
|
65
|
+
# }
|
66
|
+
# links.render([
|
67
|
+
# anchor.apply(uri: '/posts', text: 'Posts'),
|
68
|
+
# anchor.apply(uri: '/archive', text: 'Archive'),
|
69
|
+
# anchor.apply(uri: '/about', text: 'About')
|
70
|
+
# ])
|
71
|
+
#
|
72
|
+
# The `#apply` method creates a new component, applying the given parameters
|
73
|
+
# such that the component can be rendered without parameters:
|
74
|
+
#
|
75
|
+
# links_with_anchors = links.apply([
|
76
|
+
# anchor.apply(uri: '/posts', text: 'Posts'),
|
77
|
+
# anchor.apply(uri: '/archive', text: 'Archive'),
|
78
|
+
# anchor.apply(uri: '/about', text: 'About')
|
79
|
+
# ])
|
80
|
+
# links_with_anchors.render
|
81
|
+
#
|
26
82
|
class Component < Proc
|
27
|
-
|
28
|
-
#
|
83
|
+
|
84
|
+
# Determines the rendering mode: `:html` or `:xml`.
|
85
|
+
attr_accessor :mode
|
86
|
+
|
87
|
+
# Initializes a component with the given block. The rendering mode (HTML or
|
88
|
+
# XML) can be passed in the `mode:` parameter. If `mode:` is not specified,
|
89
|
+
# the component defaults to HTML.
|
90
|
+
#
|
91
|
+
# @param mode [:html, :xml] rendering mode
|
29
92
|
# @param block [Proc] nested HTML block
|
30
93
|
def initialize(mode: :html, &block)
|
31
94
|
@mode = mode
|
@@ -34,7 +97,9 @@ module Papercraft
|
|
34
97
|
|
35
98
|
H_EMPTY = {}.freeze
|
36
99
|
|
37
|
-
# Renders the
|
100
|
+
# Renders the template with the given parameters and or block, and returns
|
101
|
+
# the string result.
|
102
|
+
#
|
38
103
|
# @param context [Hash] context
|
39
104
|
# @return [String]
|
40
105
|
def render(*a, **b, &block)
|
@@ -51,6 +116,24 @@ module Papercraft
|
|
51
116
|
raise Papercraft::Error, e.message
|
52
117
|
end
|
53
118
|
|
119
|
+
# Creates a new component, applying the given parameters and or block to the
|
120
|
+
# current one. Application is one of the principal methods of composing
|
121
|
+
# components, particularly when passing inner components as blocks:
|
122
|
+
#
|
123
|
+
# article_wrapper = H {
|
124
|
+
# article {
|
125
|
+
# emit_yield
|
126
|
+
# }
|
127
|
+
# }
|
128
|
+
# wrapped_article = article_wrapper.apply {
|
129
|
+
# h1 'Article title'
|
130
|
+
# }
|
131
|
+
# wrapped_article.render #=> "<article><h1>Article title</h1></article>"
|
132
|
+
#
|
133
|
+
# @param *a [<any>] normal parameters
|
134
|
+
# @param **b [Hash] named parameters
|
135
|
+
# @param &block [Proc] inner block
|
136
|
+
# @return [Papercraft::Component] applied component
|
54
137
|
def apply(*a, **b, &block)
|
55
138
|
template = self
|
56
139
|
if block
|
@@ -64,6 +147,10 @@ module Papercraft
|
|
64
147
|
end
|
65
148
|
end
|
66
149
|
|
150
|
+
# Returns the Renderer class used for rendering the templates, according to
|
151
|
+
# the component's mode.
|
152
|
+
#
|
153
|
+
# @return [Papercraft::Renderer] Renderer used for rendering the component
|
67
154
|
def renderer_class
|
68
155
|
case @mode
|
69
156
|
when :html
|
data/lib/papercraft/html.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'kramdown'
|
4
|
+
require 'rouge'
|
5
|
+
require 'kramdown-parser-gfm'
|
4
6
|
|
5
|
-
module Papercraft
|
6
|
-
# Markup extensions
|
7
|
+
module Papercraft
|
8
|
+
# HTML Markup extensions
|
7
9
|
module HTML
|
8
10
|
# Emits the p tag (overrides Object#p)
|
11
|
+
#
|
9
12
|
# @param text [String] text content of tag
|
10
13
|
# @param props [Hash] tag attributes
|
11
14
|
# @para block [Proc] nested HTML block
|
@@ -16,7 +19,8 @@ module Papercraft
|
|
16
19
|
|
17
20
|
S_HTML5_DOCTYPE = '<!DOCTYPE html>'
|
18
21
|
|
19
|
-
# Emits an HTML5 doctype tag and an html tag with the given block
|
22
|
+
# Emits an HTML5 doctype tag and an html tag with the given block.
|
23
|
+
#
|
20
24
|
# @param block [Proc] nested HTML block
|
21
25
|
# @return [void]
|
22
26
|
def html5(&block)
|
@@ -24,6 +28,11 @@ module Papercraft
|
|
24
28
|
self.html(&block)
|
25
29
|
end
|
26
30
|
|
31
|
+
# Emits a link element with a stylesheet.
|
32
|
+
#
|
33
|
+
# @param href [String] stylesheet URL
|
34
|
+
# @param custom_attributes [Hash] optional custom attributes for the link element
|
35
|
+
# @return [void]
|
27
36
|
def link_stylesheet(href, custom_attributes = nil)
|
28
37
|
attributes = {
|
29
38
|
rel: 'stylesheet',
|
@@ -34,5 +43,28 @@ module Papercraft
|
|
34
43
|
end
|
35
44
|
link(**attributes)
|
36
45
|
end
|
46
|
+
|
47
|
+
def emit_markdown(markdown, **opts)
|
48
|
+
emit Kramdown::Document.new(markdown, **kramdown_options(opts)).to_html
|
49
|
+
end
|
50
|
+
|
51
|
+
def kramdown_options(opts)
|
52
|
+
HTML.kramdown_options.merge(**opts)
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self
|
56
|
+
def kramdown_options
|
57
|
+
@kramdown_options ||= {
|
58
|
+
entity_output: :numeric,
|
59
|
+
syntax_highlighter: :rouge,
|
60
|
+
input: 'GFM',
|
61
|
+
hard_wrap: false
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def kramdown_options=(opts)
|
66
|
+
@kramdown_options = opts
|
67
|
+
end
|
68
|
+
end
|
37
69
|
end
|
38
70
|
end
|
data/lib/papercraft/renderer.rb
CHANGED
@@ -3,9 +3,19 @@
|
|
3
3
|
require_relative './html'
|
4
4
|
|
5
5
|
module Papercraft
|
6
|
+
|
6
7
|
# A Renderer renders a Papercraft component into a string
|
7
8
|
class Renderer
|
9
|
+
|
8
10
|
class << self
|
11
|
+
|
12
|
+
# Verifies that the given template proc can be called with the given
|
13
|
+
# arguments and named arguments. If the proc demands named argument keys
|
14
|
+
# that do not exist in `named_args`, `Papercraft::Error` is raised.
|
15
|
+
#
|
16
|
+
# @param template [Proc] proc to verify
|
17
|
+
# @param args [Array<any>] arguments passed to proc
|
18
|
+
# @param named_args [Hash] named arguments passed to proc
|
9
19
|
def verify_proc_parameters(template, args, named_args)
|
10
20
|
param_count = 0
|
11
21
|
template.parameters.each do |(type, name)|
|
@@ -24,31 +34,22 @@ module Papercraft
|
|
24
34
|
end
|
25
35
|
end
|
26
36
|
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
# @param
|
31
|
-
# @param block [Proc] template block
|
32
|
-
# @return [void]
|
37
|
+
# Initializes the renderer and evaulates the given template in the
|
38
|
+
# renderer's scope.
|
39
|
+
#
|
40
|
+
# @param &template [Proc] template block
|
33
41
|
def initialize(&template)
|
34
42
|
@buffer = +''
|
35
43
|
instance_eval(&template)
|
36
44
|
end
|
37
45
|
|
38
|
-
# Returns the
|
46
|
+
# Returns the rendered template.
|
47
|
+
#
|
39
48
|
# @return [String]
|
40
49
|
def to_s
|
41
50
|
@buffer
|
42
51
|
end
|
43
52
|
|
44
|
-
def escape_text(text)
|
45
|
-
raise NotImplementedError
|
46
|
-
end
|
47
|
-
|
48
|
-
def escape_uri(uri)
|
49
|
-
EscapeUtils.escape_uri(v)
|
50
|
-
end
|
51
|
-
|
52
53
|
S_TAG_METHOD_LINE = __LINE__ + 1
|
53
54
|
S_TAG_METHOD = <<~EOF
|
54
55
|
S_TAG_%<TAG>s_PRE = '<%<tag>s'.tr('_', '-')
|
@@ -74,10 +75,12 @@ module Papercraft
|
|
74
75
|
end
|
75
76
|
EOF
|
76
77
|
|
77
|
-
# Catches undefined tag method call and handles
|
78
|
+
# Catches undefined tag method call and handles it by defining the method.
|
79
|
+
#
|
78
80
|
# @param sym [Symbol] HTML tag or component identifier
|
79
|
-
# @param args [Array] method
|
80
|
-
# @param
|
81
|
+
# @param args [Array] method arguments
|
82
|
+
# @param opts [Hash] named method arguments
|
83
|
+
# @param &block [Proc] block passed to method
|
81
84
|
# @return [void]
|
82
85
|
def method_missing(sym, *args, **opts, &block)
|
83
86
|
value = @local && @local[sym]
|
@@ -89,18 +92,28 @@ module Papercraft
|
|
89
92
|
send(sym, *args, **opts, &block)
|
90
93
|
end
|
91
94
|
|
92
|
-
# Emits the given object into the rendering buffer
|
95
|
+
# Emits the given object into the rendering buffer. If the given object is a
|
96
|
+
# proc or a component, `emit` will passes any additional arguments and named
|
97
|
+
# arguments to the object when rendering it. If the given object is nil,
|
98
|
+
# nothing is emitted. Otherwise, the object is converted into a string using
|
99
|
+
# `#to_s` which is then added to the rendering buffer, without any escaping.
|
100
|
+
#
|
101
|
+
# greeter = proc { |name| h1 "Hello, #{name}!" }
|
102
|
+
# H { emit(greeter, 'world') }.render #=> "<h1>Hello, world!</h1>"
|
103
|
+
#
|
104
|
+
# H { emit 'hi&<bye>' }.render #=> "hi&<bye>"
|
105
|
+
#
|
106
|
+
# H { emit nil }.render #=> ""
|
107
|
+
#
|
93
108
|
# @param o [Proc, Papercraft::Component, String] emitted object
|
109
|
+
# @param *a [Array<any>] arguments to pass to a proc
|
110
|
+
# @param **b [Hash] named arguments to pass to a proc
|
94
111
|
# @return [void]
|
95
112
|
def emit(o, *a, **b)
|
96
113
|
case o
|
97
114
|
when ::Proc
|
98
115
|
Renderer.verify_proc_parameters(o, a, b)
|
99
116
|
instance_exec(*a, **b, &o)
|
100
|
-
# when Papercraft::Component
|
101
|
-
# o = o.template
|
102
|
-
# Renderer.verify_proc_parameters(o, a, b)
|
103
|
-
# instance_exec(*a, **b, &o)
|
104
117
|
when nil
|
105
118
|
else
|
106
119
|
@buffer << o.to_s
|
@@ -108,14 +121,15 @@ module Papercraft
|
|
108
121
|
end
|
109
122
|
alias_method :e, :emit
|
110
123
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
124
|
+
# Emits a block supplied using `Component#apply` or `Component#render`.
|
125
|
+
#
|
126
|
+
# div_wrap = H { |*args| div { emit_yield(*args) } }
|
127
|
+
# greeter = div_wrap.apply { |name| h1 "Hello, #{name}!" }
|
128
|
+
# greeter.render('world') #=> "<div><h1>Hello, world!</h1></div>"
|
129
|
+
#
|
130
|
+
# @param *a [Array<any>] arguments to pass to a proc
|
131
|
+
# @param **b [Hash] named arguments to pass to a proc
|
132
|
+
# @return [void]
|
119
133
|
def emit_yield(*a, **b)
|
120
134
|
raise Papercraft::Error, "No block given" unless @inner_block
|
121
135
|
|
@@ -131,6 +145,31 @@ module Papercraft
|
|
131
145
|
S_EQUAL_QUOTE = '="'
|
132
146
|
S_QUOTE = '"'
|
133
147
|
|
148
|
+
# Emits text into the rendering buffer, escaping any special characters to
|
149
|
+
# the respective HTML entities.
|
150
|
+
#
|
151
|
+
# @param data [String] text
|
152
|
+
# @return [void]
|
153
|
+
def text(data)
|
154
|
+
@buffer << escape_text(data)
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
# Escapes text. This method must be overriden in descendant classes.
|
160
|
+
def escape_text(text)
|
161
|
+
raise NotImplementedError
|
162
|
+
end
|
163
|
+
|
164
|
+
# Sets up a block to be called with `#emit_yield`
|
165
|
+
def with_block(block, &run_block)
|
166
|
+
old_block = @inner_block
|
167
|
+
@inner_block = block
|
168
|
+
instance_eval(&run_block)
|
169
|
+
ensure
|
170
|
+
@inner_block = old_block
|
171
|
+
end
|
172
|
+
|
134
173
|
# Emits tag attributes into the rendering buffer
|
135
174
|
# @param props [Hash] tag attributes
|
136
175
|
# @return [void]
|
@@ -153,23 +192,23 @@ module Papercraft
|
|
153
192
|
end
|
154
193
|
}
|
155
194
|
end
|
156
|
-
|
157
|
-
# Emits text into the rendering buffer
|
158
|
-
# @param data [String] text
|
159
|
-
def text(data)
|
160
|
-
@buffer << escape_text(data)
|
161
|
-
end
|
162
195
|
end
|
163
196
|
|
197
|
+
# Implements an HTML renderer
|
164
198
|
class HTMLRenderer < Renderer
|
165
199
|
include HTML
|
166
200
|
|
201
|
+
private
|
202
|
+
|
167
203
|
def escape_text(text)
|
168
204
|
EscapeUtils.escape_html(text.to_s)
|
169
205
|
end
|
170
206
|
end
|
171
207
|
|
208
|
+
# Implements an XML renderer
|
172
209
|
class XMLRenderer < Renderer
|
210
|
+
private
|
211
|
+
|
173
212
|
def escape_text(text)
|
174
213
|
EscapeUtils.escape_xml(text.to_s)
|
175
214
|
end
|
data/lib/papercraft/version.rb
CHANGED
data/lib/papercraft.rb
CHANGED
@@ -15,20 +15,32 @@ end
|
|
15
15
|
|
16
16
|
# Kernel extensions
|
17
17
|
module ::Kernel
|
18
|
-
|
19
|
-
#
|
18
|
+
|
19
|
+
# Creates a new papercraft component. `#H` can take either a proc argument or
|
20
|
+
# a block. In both cases, the proc is converted to a `Papercraft::Component`.
|
21
|
+
#
|
22
|
+
# H(proc { h1 'hi' }).render #=> "<h1>hi</h1>"
|
23
|
+
# H { h1 'hi' }.render #=> "<h1>hi</h1>"
|
24
|
+
#
|
20
25
|
# @param template [Proc] template block
|
21
|
-
# @return [Papercraft] Papercraft
|
22
|
-
def H(&template)
|
23
|
-
Papercraft::Component
|
26
|
+
# @return [Papercraft::Component] Papercraft component
|
27
|
+
def H(o = nil, &template)
|
28
|
+
return o if o.is_a?(Papercraft::Component)
|
29
|
+
template ||= o
|
30
|
+
Papercraft::Component.new(mode: :html, &template)
|
24
31
|
end
|
25
32
|
|
26
|
-
|
33
|
+
# Creates a new papercraft component in XML mode. `#X` can take either a proc argument or
|
34
|
+
# a block. In both cases, the proc is converted to a `Papercraft::Component`.
|
35
|
+
#
|
36
|
+
# X(proc { item 'foo' }).render #=> "<item>foo</item>"
|
37
|
+
# X { item 'foo' }.render #=> "<item>foo</item>"
|
38
|
+
#
|
39
|
+
# @param template [Proc] template block
|
40
|
+
# @return [Papercraft::Component] Papercraft component
|
41
|
+
def X(o = nil, &template)
|
42
|
+
return o if o.is_a?(Papercraft::Component)
|
43
|
+
template ||= o
|
27
44
|
Papercraft::Component.new(mode: :xml, &template)
|
28
45
|
end
|
29
46
|
end
|
30
|
-
|
31
|
-
# Object extensions
|
32
|
-
class Object
|
33
|
-
include Papercraft::Encoding
|
34
|
-
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: papercraft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.9'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: escape_utils
|
@@ -24,6 +24,48 @@ dependencies:
|
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.2.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: kramdown
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.3.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.3.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rouge
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.26.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.26.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: kramdown-parser-gfm
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.1.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.1.0
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: minitest
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,6 +143,9 @@ licenses:
|
|
101
143
|
- MIT
|
102
144
|
metadata:
|
103
145
|
source_code_uri: https://github.com/digital-fabric/papercraft
|
146
|
+
documentation_uri: https://www.rubydoc.info/gems/papercraft
|
147
|
+
homepage_uri: https://github.com/digital-fabric/papercraft
|
148
|
+
changelog_uri: https://github.com/digital-fabric/papercraft/blob/master/CHANGELOG.md
|
104
149
|
post_install_message:
|
105
150
|
rdoc_options:
|
106
151
|
- "--title"
|