papercraft 0.23 → 0.25

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 341520b9db20c53da441299fb449c4eea3321924517f4b93e70af1119b4516c3
4
- data.tar.gz: 861a5a2747c3a468abb780cf50710698e52fbac1fc236c8672c2bbb328db6f4d
3
+ metadata.gz: f23902f7431dcbd6c1180e26f612ae5099d4e7f2ba468dd0e0e5e9a76e324ef4
4
+ data.tar.gz: 5bf38e4faea0ab6c6145d769b24b416e467836b29c6d8856bb96442f86b43bfe
5
5
  SHA512:
6
- metadata.gz: 7668a984a2c7957ba3f65234eab6770b4e969e0d2b4e1345839db8c0711d07afbfef5735b67de9a94c22994b4b6c1d6b2f87e07a810a0cae04ccbecb1ff5b38f
7
- data.tar.gz: 1605f9b1459ab20b9cec97805529764ece6058ce34609dc7d75fe38d8b35889c65e4afa5174f1c1d89dfb46205d282e65e1020995c999f66b84a5f9b5e505de1
6
+ metadata.gz: 69ed90f079f1db073bbc18b1b631c13cbcd2db5dae9e512781046fb7fa5ab0a164653137b28c201f48671d77e4d08ff1da3818381aca5d3686d61eb185f8a586
7
+ data.tar.gz: 85681c583e3e145ca27a52638e0d3370fa94462d5baaae8cccb477a1a2938ad56f3040ee4f6cceb2757b49019890bcae505bcdc008e2ff0931987f09ab9eb91a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.25 2023-01-12
2
+
3
+ - Implement `#def_tag` for defining custom tags inline
4
+
5
+ ## 0.24 2022-03-19
6
+
7
+ - Fix usage of const components (#13)
8
+ - Fix formatting of HTML/XML attributes for non-string values
9
+
1
10
  ## 0.23 2022-02-15
2
11
 
3
12
  - Remove unused `Encoding` module
data/README.md CHANGED
@@ -42,6 +42,10 @@ Papercraft includes built-in support for rendering Markdown (using
42
42
  creating template extensions in order to allow the creation of component
43
43
  libraries.
44
44
 
45
+ Papercraft automatically escapes all text emitted in templates according to the
46
+ template type. For more information see the section on [escaping
47
+ content](#escaping-content).
48
+
45
49
  ```ruby
46
50
  require 'papercraft'
47
51
 
@@ -59,29 +63,31 @@ hello.render('world')
59
63
  #=> "<html><head><title>Title</title></head><body><h1>Hello, world!</h1></body></html>"
60
64
  ```
61
65
 
62
- ## Table of content
63
-
64
- - [Installing papercraft](#installing-papercraft)
65
- - [Basic usage](#basic-usage)
66
- - [Adding tags](#adding-tags)
67
- - [Tag and attribute formatting](#tag-and-attribute-formatting)
68
- - [Template parameters](#template-parameters)
69
- - [Template logic](#template-logic)
70
- - [Template blocks](#template-blocks)
71
- - [Plain procs as templates](#plain-procs-as-templates)
72
- - [Template composition](#template-composition)
73
- - [Parameter and block application](#parameter-and-block-application)
74
- - [Higher-order templates](#higher-order-templates)
75
- - [Layout template composition](#layout-template-composition)
76
- - [Emitting raw HTML](#emitting-raw-html)
77
- - [Emitting a string with HTML Encoding](#emitting-a-string-with-html-encoding)
66
+ ## Table of Content
67
+
68
+ - [Installing Papercraft](#installing-papercraft)
69
+ - [Basic Usage](#basic-usage)
70
+ - [Adding Tags](#adding-tags)
71
+ - [Tag and Attribute Formatting](#tag-and-attribute-formatting)
72
+ - [Escaping Content](#escaping-content)
73
+ - [Template Parameters](#template-parameters)
74
+ - [Template Logic](#template-logic)
75
+ - [Template Blocks](#template-blocks)
76
+ - [Plain Procs as Templates](#plain-procs-as-templates)
77
+ - [Template Composition](#template-composition)
78
+ - [Parameter and Block Application](#parameter-and-block-application)
79
+ - [Higher-Order Templates](#higher-order-templates)
80
+ - [Layout Template Composition](#layout-template-composition)
81
+ - [Emitting Raw HTML](#emitting-raw-html)
82
+ - [Emitting a String with HTML Encoding](#emitting-a-string-with-html-encoding)
78
83
  - [Emitting Markdown](#emitting-markdown)
79
- - [Working with MIME types](#working-with-mime-types)
80
- - [Deferred evaluation](#deferred-evaluation)
81
- - [XML templates](#xml-templates)
82
- - [JSON templates](#json-templates)
83
- - [Papercraft extensions](#papercraft-extensions)
84
- - [Bundled extensions](#bundled-extensions)
84
+ - [Working with MIME Types](#working-with-mime-types)
85
+ - [Deferred Evaluation](#deferred-evaluation)
86
+ - [XML Templates](#xml-templates)
87
+ - [JSON Templates](#json-templates)
88
+ - [Papercraft Extensions](#papercraft-extensions)
89
+ - [Inline Helper Methods](#inline-helper-methods)
90
+ - [Bundled Extensions](#bundled-extensions)
85
91
  - [API Reference](#api-reference)
86
92
 
87
93
  ## Installing Papercraft
@@ -98,7 +104,7 @@ Or manually:
98
104
  $ gem install papercraft
99
105
  ```
100
106
 
101
- ## Basic usage
107
+ ## Basic Usage
102
108
 
103
109
  To create an HTML template use `Papercraft.html`:
104
110
 
@@ -119,7 +125,7 @@ Rendering a template is done using `#render`:
119
125
  html.render #=> "<div id="greeter"><p>Hello!</p></div>"
120
126
  ```
121
127
 
122
- ## Adding tags
128
+ ## Adding Tags
123
129
 
124
130
  Tags are added using unqualified method calls, and can be nested using blocks:
125
131
 
@@ -156,7 +162,7 @@ Papercraft.html { img src: '/my.gif' }.render #=> "<img src="/my.gif"/>
156
162
  Papercraft.html { p "foobar", class: 'important' }.render #=> "<p class=\"important\">foobar</p>"
157
163
  ```
158
164
 
159
- ## Tag and attribute formatting
165
+ ## Tag and Attribute Formatting
160
166
 
161
167
  Papercraft does not make any presumption about what tags and attributes you can
162
168
  use. You can mix upper and lower case letters, and you can include arbitrary
@@ -193,7 +199,23 @@ Papercraft.html {
193
199
  }.render #=> '<cra_zy__:!tag>foo</cra_zy__:!tag>'
194
200
  ```
195
201
 
196
- ## Template parameters
202
+ ## Escaping Content
203
+
204
+ Papercraft automatically escapes all text content emitted in a template. The
205
+ specific escaping algorithm depends on the template type. For both HTML and XML
206
+ templates, Papercraft uses
207
+ [escape_utils](https://github.com/brianmario/escape_utils), specifically:
208
+
209
+ - HTML: `escape_utils.escape_html`
210
+ - XML: `escape_utils.escape_xml`
211
+
212
+ In order to emit raw HTML/XML, you can use the `#emit` method as [described
213
+ below](#emitting-raw-html).
214
+
215
+ JSON templates are rendered using the `json` gem bundled with Ruby, which takes
216
+ care of escaping text values.
217
+
218
+ ## Template Parameters
197
219
 
198
220
  In Papercraft, parameters are always passed explicitly. This means that template
199
221
  parameters are specified as block parameters, and are passed to the template on
@@ -211,7 +233,7 @@ greeting = Papercraft.html { |name:| h1 "Hello, #{name}!" }
211
233
  greeting.render(name: 'world') #=> "<h1>Hello, world!</h1>"
212
234
  ```
213
235
 
214
- ## Template logic
236
+ ## Template Logic
215
237
 
216
238
  Since Papercraft templates are just a bunch of Ruby, you can easily write your
217
239
  view logic right in the template:
@@ -226,7 +248,7 @@ Papercraft.html { |user = nil|
226
248
  }
227
249
  ```
228
250
 
229
- ## Template blocks
251
+ ## Template Blocks
230
252
 
231
253
  Templates can also accept and render blocks by using `emit_yield`:
232
254
 
@@ -241,7 +263,7 @@ page = Papercraft.html {
241
263
  page.render { h1 'hi' }
242
264
  ```
243
265
 
244
- ## Plain procs as templates
266
+ ## Plain Procs as Templates
245
267
 
246
268
  With Papercraft you can write a template as a plain Ruby proc, and later render
247
269
  it by passing it as a block to `Papercraft.html`:
@@ -258,7 +280,7 @@ greeting = ->(name) { h1 "Hello, #{name}!" }
258
280
  Papercraft.html(&greeting).render('world')
259
281
  ```
260
282
 
261
- ## Template composition
283
+ ## Template Composition
262
284
 
263
285
  Papercraft makes it easy to compose multiple templates into a whole HTML
264
286
  document. A Papercraft template can contain other templates, as the following
@@ -308,7 +330,7 @@ Papercraft.html {
308
330
  }
309
331
  ```
310
332
 
311
- ## Parameter and block application
333
+ ## Parameter and Block Application
312
334
 
313
335
  Parameters and blocks can be applied to a template without it being rendered, by
314
336
  using `#apply`. This mechanism is what allows template composition and the
@@ -333,7 +355,7 @@ wrapped_hello_world = div_wrap.apply(&hello_world)
333
355
  wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
334
356
  ```
335
357
 
336
- ## Higher-order templates
358
+ ## Higher-Order Templates
337
359
 
338
360
  Papercraft also lets you create higher-order templates, that is,
339
361
  templates that take other templates as parameters, or as blocks. Higher-order
@@ -357,7 +379,7 @@ wrapped_greeter = div_wrap.apply { h1 'hi' }
357
379
  wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
358
380
  ```
359
381
 
360
- ## Layout template composition
382
+ ## Layout Template Composition
361
383
 
362
384
  One of the principal uses of higher-order templates is the creation of nested
363
385
  layouts. Suppose we have a website with a number of different layouts, and we'd
@@ -390,7 +412,7 @@ article_layout.render(
390
412
  )
391
413
  ```
392
414
 
393
- ## Emitting raw HTML
415
+ ## Emitting Raw HTML
394
416
 
395
417
  Raw HTML can be emitted using `#emit`:
396
418
 
@@ -399,13 +421,13 @@ wrapped = Papercraft.html { |html| div { emit html } }
399
421
  wrapped.render("<h1>hi</h1>") #=> "<div><h1>hi</h1></div>"
400
422
  ```
401
423
 
402
- ## Emitting a string with HTML Encoding
424
+ ## Emitting a String with HTML Encoding
403
425
 
404
426
  To emit a string with proper HTML encoding, without wrapping it in an HTML
405
427
  element, use `#text`:
406
428
 
407
429
  ```ruby
408
- Papercraft.html { str 'hi&lo' }.render #=> "hi&amp;lo"
430
+ Papercraft.html { text 'hi&lo' }.render #=> "hi&amp;lo"
409
431
  ```
410
432
 
411
433
  ## Emitting Markdown
@@ -454,7 +476,7 @@ The deafult options can be configured by accessing
454
476
  Papercraft.default_kramdown_options[:auto_ids] = false
455
477
  ```
456
478
 
457
- ## Working with MIME types
479
+ ## Working with MIME Types
458
480
 
459
481
  Papercraft lets you set and interrogate a template's MIME type, in order to be
460
482
  able to dynamically set the `Content-Type` HTTP response header. A template's
@@ -470,7 +492,7 @@ def serve_template(req, template)
470
492
  end
471
493
  ```
472
494
 
473
- ## Deferred evaluation
495
+ ## Deferred Evaluation
474
496
 
475
497
  Deferred evaluation allows deferring the rendering of parts of a template until
476
498
  the last moment, thus allowing an inner template to manipulate the state of the
@@ -522,7 +544,7 @@ page = default_layout.apply {
522
544
  }
523
545
  ```
524
546
 
525
- ## XML templates
547
+ ## XML Templates
526
548
 
527
549
  XML templates behave largely the same as HTML templates, with a few minor
528
550
  differences. XML templates employ a different encoding algorithm, and lack some
@@ -557,7 +579,7 @@ rss = Papercraft.xml(mime_type: 'text/xml; charset=utf-8') { |resource:, **props
557
579
  }
558
580
  ```
559
581
 
560
- ## JSON templates
582
+ ## JSON Templates
561
583
 
562
584
  JSON templates behave largely the same as HTML and XML templates. The only major
563
585
  difference is that for adding array items you'll need to use the `#item` method:
@@ -588,7 +610,7 @@ Papercraft.json {
588
610
  Papercraft uses the [JSON gem](https://rubyapi.org/3.1/o/json) under the hood in
589
611
  order to generate actual JSON.
590
612
 
591
- ## Papercraft extensions
613
+ ## Papercraft Extensions
592
614
 
593
615
  Papercraft extensions are modules that contain one or more methods that can be
594
616
  used to render complex HTML components. Extension modules can be used by
@@ -660,6 +682,57 @@ Papercraft.html {
660
682
  }
661
683
  ```
662
684
 
685
+ ## Inline Helper Methods
686
+
687
+ In addition to proper extensions defined in modules, you can also define
688
+ individual extension methods inline in your Papercraft templates. You can do
689
+ this using any of the following techniques:
690
+
691
+ 1. Define a method in the template body:
692
+
693
+ ```ruby
694
+ Papercraft.html {
695
+ def label(text)
696
+ span text, class: 'label'
697
+ end
698
+
699
+ label 'foo'
700
+ label 'bar'
701
+ }
702
+ ```
703
+
704
+ 2. Use `def_tag` to define a custom tag:
705
+
706
+ ```ruby
707
+ Papercraft.html {
708
+ def_tag(:label) { |text| span text, class: 'label' }
709
+
710
+ label 'foo'
711
+ label 'bar'
712
+ }
713
+ ```
714
+
715
+ Note that using any of the above methods you can also create custom components
716
+ that take a block with inner HTML:
717
+
718
+ ```ruby
719
+ # using def
720
+ def section(title, &inner)
721
+ div {
722
+ h1 title
723
+ emit inner
724
+ }
725
+ end
726
+
727
+ # using def_tag
728
+ def_tag(:section) do |title, &inner|
729
+ div {
730
+ h1 title
731
+ emit inner
732
+ }
733
+ end
734
+ ```
735
+
663
736
  ## Bundled Extensions
664
737
 
665
738
  Papercraft comes bundled with a few extensions that address common use cases.
@@ -669,7 +742,7 @@ be specifically required in order to be available to templates.
669
742
  For all bundled Papercraft extensions, there's no need to call
670
743
  `Papercraft.extension`, requiring the extension is sufficient.
671
744
 
672
- ### SOAP extension
745
+ ### SOAP Extension
673
746
 
674
747
  > The SOAP extension was contributed by [@aemadrid](https://github.com/aemadrid).
675
748
 
@@ -713,4 +786,4 @@ xml = Papercraft.xml {
713
786
  ## API Reference
714
787
 
715
788
  The API reference for this library can be found
716
- [here](https://www.rubydoc.info/gems/papercraft).
789
+ [here](https://www.rubydoc.info/gems/papercraft).
@@ -47,17 +47,17 @@ module Papercraft
47
47
  # components. In cases where method names in the module clash with XML
48
48
  # tag names, you can use the `#tag` method to emit the relevant tag.
49
49
  #
50
- # module ComponentLibrary
51
- # def card(title, content)
52
- # div(class: 'card') {
53
- # h3 title
54
- # div(class: 'card-content') { emit_markdown content }
55
- # }
50
+ # module ComponentLibrary
51
+ # def card(title, content)
52
+ # div(class: 'card') {
53
+ # h3 title
54
+ # div(class: 'card-content') { emit_markdown content }
55
+ # }
56
+ # end
56
57
  # end
57
- # end
58
58
  #
59
- # Papercraft.extension(components: ComponentLibrary)
60
- # Papercraft.html { components.card('Foo', '**Bar**') }
59
+ # Papercraft.extension(components: ComponentLibrary)
60
+ # Papercraft.html { components.card('Foo', '**Bar**') }
61
61
  #
62
62
  # @param map [Hash] hash mapping methods to extension modules
63
63
  # @return [void]
@@ -161,14 +161,13 @@ module Papercraft
161
161
  # @return [void]
162
162
  def method_missing(sym, *args, **opts, &block)
163
163
  tag = sym.to_s
164
- repr = tag_repr(tag)
165
- code = S_TAG_METHOD % {
166
- tag: tag,
167
- TAG: tag.upcase,
168
- tag_pre: "<#{repr}".inspect,
169
- tag_close: "</#{repr}>".inspect
170
- }
171
- self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
164
+ if tag =~ /^[A-Z]/ && (Object.const_defined?(tag))
165
+ define_const_tag_method(tag)
166
+ # return send(tag, *args, **opts)
167
+ else
168
+ define_tag_method(tag)
169
+ end
170
+
172
171
  send(sym, *args, **opts, &block)
173
172
  end
174
173
 
@@ -181,8 +180,58 @@ module Papercraft
181
180
  @buffer << escape_text(data)
182
181
  end
183
182
 
183
+ # Defines a custom tag. This is handy for defining helper or extension
184
+ # methods inside the template body.
185
+ #
186
+ # Papercraft.html {
187
+ # def_tag(:section) { |title, &inner|
188
+ # div {
189
+ # h1 title
190
+ # emit inner
191
+ # }
192
+ # }
193
+ #
194
+ # section('Foo') {
195
+ # p 'Bar'
196
+ # }
197
+ # }
198
+ #
199
+ # @param tag [Symbol, String] tag/method name
200
+ # @param block [Proc] method body
201
+ # @return [void]
202
+ def def_tag(sym, &block)
203
+ self.class.define_method(sym, &block)
204
+ end
205
+
184
206
  private
185
207
 
208
+ # Defines a method that emits the given tag based on a constant. The
209
+ # constant must be defined on the main (Object) binding.
210
+ #
211
+ # @param tag [Symbol, String] tag/method name
212
+ # @return [void]
213
+ def define_const_tag_method(tag)
214
+ const = Object.const_get(tag)
215
+ self.class.define_method(tag) { |*a, **b, &blk|
216
+ emit const, *a, **b, &blk
217
+ }
218
+ end
219
+
220
+ # Defines a normal tag method.
221
+ #
222
+ # @param tag [Symbol, String] tag/method name
223
+ # @return [void]
224
+ def define_tag_method(tag)
225
+ repr = tag_repr(tag)
226
+ code = S_TAG_METHOD % {
227
+ tag: tag,
228
+ TAG: tag.upcase,
229
+ tag_pre: "<#{repr}".inspect,
230
+ tag_close: "</#{repr}>".inspect
231
+ }
232
+ self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
233
+ end
234
+
186
235
  # Emits an arbitrary object by converting it to string, then adding it to
187
236
  # the internal buffer. This method is called internally by `Renderer#emit`.
188
237
  #
@@ -251,7 +300,7 @@ module Papercraft
251
300
  # emit nothing
252
301
  else
253
302
  @buffer << S_SPACE << att_repr(k) <<
254
- S_EQUAL_QUOTE << v << S_QUOTE
303
+ S_EQUAL_QUOTE << escape_text(v) << S_QUOTE
255
304
  end
256
305
  end
257
306
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Papercraft
4
- VERSION = '0.23'
4
+ VERSION = '0.25'
5
5
  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.23'
4
+ version: '0.25'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-15 00:00:00.000000000 Z
11
+ date: 2023-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.0.9
125
- description:
125
+ description:
126
126
  email: sharon@noteflakes.com
127
127
  executables: []
128
128
  extensions: []
@@ -152,7 +152,7 @@ metadata:
152
152
  documentation_uri: https://www.rubydoc.info/gems/papercraft
153
153
  homepage_uri: https://github.com/digital-fabric/papercraft
154
154
  changelog_uri: https://github.com/digital-fabric/papercraft/blob/master/CHANGELOG.md
155
- post_install_message:
155
+ post_install_message:
156
156
  rdoc_options:
157
157
  - "--title"
158
158
  - Papercraft
@@ -171,8 +171,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  requirements: []
174
- rubygems_version: 3.3.3
175
- signing_key:
174
+ rubygems_version: 3.4.1
175
+ signing_key:
176
176
  specification_version: 4
177
177
  summary: 'Papercraft: component-based HTML templating for Ruby'
178
178
  test_files: []