papercraft 2.22 → 2.24
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 +15 -589
 - data/lib/papercraft/compiler.rb +11 -11
 - data/lib/papercraft/proc_ext.rb +14 -57
 - data/lib/papercraft/template.rb +2 -2
 - data/lib/papercraft/version.rb +1 -1
 - data/lib/papercraft.rb +37 -0
 - metadata +17 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ea6b8d53d5110e44678302644fef523e5e9735b7f862f67b20a9f1bb4883c8b8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3aacb8f0fb578c691230aefc5a1c0c16b1ddae30666004e977094f1f4a7793da
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 3014b7b8a22b5128f5aecb7fd7e7c181d6af5fd1f0727f71dafaa1994f16fda643c6f80aad04fcffc332042e392c11803d9f75d4b18ec8704edc94f374a927ab
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: e8d2017554d39c57d18ccd4ddf3a39f5fc4592d1c16584c47f5c31028a131c28379b43adad55557b70191df34d32f28d4e78989ed8d59700fc0f78bfd69dfcde
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # 2.24 2025-10-14
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            - Update gem links
         
     | 
| 
      
 4 
     | 
    
         
            +
            - Simplify `render_cache`, caller must provide cache key
         
     | 
| 
      
 5 
     | 
    
         
            +
            - Reduce surface area of Proc extensions
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            # 2.23 2025-10-12
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            - Update ERB to version 5.1.1
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       1 
11 
     | 
    
         
             
            # 2.22 2025-10-08
         
     | 
| 
       2 
12 
     | 
    
         | 
| 
       3 
13 
     | 
    
         
             
            - Use `prepend` instead of `include` to extend the `Proc` class
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -18,603 +18,29 @@ 
     | 
|
| 
       18 
18 
     | 
    
         
             
              </a>
         
     | 
| 
       19 
19 
     | 
    
         
             
            </p>
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
            <p align="center">
         
     | 
| 
       22 
     | 
    
         
            -
              <a href="https://www.rubydoc.info/gems/papercraft">API reference</a>
         
     | 
| 
       23 
     | 
    
         
            -
            </p>
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
            ## What is Papercraft?
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
21 
     | 
    
         
             
            ```ruby
         
     | 
| 
       28 
22 
     | 
    
         
             
            require 'papercraft'
         
     | 
| 
       29 
23 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
               
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
              }
         
     | 
| 
       35 
     | 
    
         
            -
            }
         
     | 
| 
       36 
     | 
    
         
            -
            page.render {
         
     | 
| 
       37 
     | 
    
         
            -
              p 'foo'
         
     | 
| 
       38 
     | 
    
         
            -
            }
         
     | 
| 
       39 
     | 
    
         
            -
            #=> "<html><head><title>Title</title></head><body><p>foo</p></body></html>"
         
     | 
| 
      
 24 
     | 
    
         
            +
            -> {
         
     | 
| 
      
 25 
     | 
    
         
            +
              h1 "Hello from Papercraft!"
         
     | 
| 
      
 26 
     | 
    
         
            +
            }.render
         
     | 
| 
      
 27 
     | 
    
         
            +
            #=> "<h1>Hello from Papercraft</h1>"
         
     | 
| 
       40 
28 
     | 
    
         
             
            ```
         
     | 
| 
       41 
29 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
            Papercraft is a templating engine for dynamically producing HTML in Ruby apps. 
     | 
| 
       43 
     | 
    
         
            -
            templates are expressed as Ruby procs, leading to easier debugging, 
     | 
| 
       44 
     | 
    
         
            -
            protection against HTML injection attacks, and better code reuse.
         
     | 
| 
      
 30 
     | 
    
         
            +
            Papercraft is a templating engine for dynamically producing HTML in Ruby apps.
         
     | 
| 
      
 31 
     | 
    
         
            +
            Papercraft templates are expressed as Ruby procs, leading to easier debugging,
         
     | 
| 
      
 32 
     | 
    
         
            +
            better protection against HTML injection attacks, and better code reuse.
         
     | 
| 
       45 
33 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
            Papercraft templates can be composed in a variety of ways, facilitating the 
     | 
| 
       47 
     | 
    
         
            -
            layout templates, and enabling a component-oriented approach to 
     | 
| 
       48 
     | 
    
         
            -
            interfaces of arbitrary complexity.
         
     | 
| 
      
 34 
     | 
    
         
            +
            Papercraft templates can be composed in a variety of ways, facilitating the
         
     | 
| 
      
 35 
     | 
    
         
            +
            usage of layout templates, and enabling a component-oriented approach to
         
     | 
| 
      
 36 
     | 
    
         
            +
            building web interfaces of arbitrary complexity.
         
     | 
| 
       49 
37 
     | 
    
         | 
| 
       50 
38 
     | 
    
         
             
            In Papercraft, dynamic data is passed explicitly to the template as block/lambda
         
     | 
| 
       51 
     | 
    
         
            -
            arguments, making the data flow easy to follow and understand. Papercraft also 
     | 
| 
       52 
     | 
    
         
            -
            developers create derivative templates using full or partial parameter
         
     | 
| 
      
 39 
     | 
    
         
            +
            arguments, making the data flow easy to follow and understand. Papercraft also
         
     | 
| 
      
 40 
     | 
    
         
            +
            lets developers create derivative templates using full or partial parameter
         
     | 
| 
       53 
41 
     | 
    
         
             
            application.
         
     | 
| 
       54 
42 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
            require 'papercraft'
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
            page = ->(**props) {
         
     | 
| 
       59 
     | 
    
         
            -
              html {
         
     | 
| 
       60 
     | 
    
         
            -
                head { title 'My Title' }
         
     | 
| 
       61 
     | 
    
         
            -
                body { render_children **props }
         
     | 
| 
       62 
     | 
    
         
            -
              }
         
     | 
| 
       63 
     | 
    
         
            -
            }
         
     | 
| 
       64 
     | 
    
         
            -
            page.render {
         
     | 
| 
       65 
     | 
    
         
            -
              p(class: 'big') 'foo'
         
     | 
| 
       66 
     | 
    
         
            -
            }
         
     | 
| 
       67 
     | 
    
         
            -
            #=> "<html><head><title>Title</title></head><body><p class="big">foo</p></body></html>"
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
            hello_page = page.apply ->(name:, **) {
         
     | 
| 
       70 
     | 
    
         
            -
              h1 "Hello, #{name}!"
         
     | 
| 
       71 
     | 
    
         
            -
            }
         
     | 
| 
       72 
     | 
    
         
            -
            hello.render(name: 'world')
         
     | 
| 
       73 
     | 
    
         
            -
            #=> "<html><head><title>Title</title></head><body><h1>Hello, world!</h1></body></html>"
         
     | 
| 
       74 
     | 
    
         
            -
            ```
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
            Papercraft features:
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
            - Express HTML using plain Ruby procs.
         
     | 
| 
       79 
     | 
    
         
            -
            - Automatic compilation for super-fast execution (about as
         
     | 
| 
       80 
     | 
    
         
            -
              [fast](https://github.com/digital-fabric/papercraft/blob/master/examples/perf.rb) as
         
     | 
| 
       81 
     | 
    
         
            -
              compiled ERB/ERubi).
         
     | 
| 
       82 
     | 
    
         
            -
            - Deferred rendering using `defer`.
         
     | 
| 
       83 
     | 
    
         
            -
            - Simple and easy template composition (for uses such as layouts, or modular
         
     | 
| 
       84 
     | 
    
         
            -
              templates).
         
     | 
| 
       85 
     | 
    
         
            -
            - Markdown rendering using [Kramdown](https://github.com/gettalong/kramdown/).
         
     | 
| 
       86 
     | 
    
         
            -
            - Rudimentary support for generating XML.
         
     | 
| 
       87 
     | 
    
         
            -
            - Support for extensions.
         
     | 
| 
       88 
     | 
    
         
            -
            - Simple caching API for caching the rendering result.
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
            ## Table of Content
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
            - [Getting Started](#getting-started)
         
     | 
| 
       93 
     | 
    
         
            -
            - [Basic Markup](#basic-markup)
         
     | 
| 
       94 
     | 
    
         
            -
            - [Builtin Methods](#builtin-methods)
         
     | 
| 
       95 
     | 
    
         
            -
            - [Template Parameters](#template-parameters)
         
     | 
| 
       96 
     | 
    
         
            -
            - [Template Logic](#template-logic)
         
     | 
| 
       97 
     | 
    
         
            -
            - [Template Blocks](#template-blocks)
         
     | 
| 
       98 
     | 
    
         
            -
            - [Template Composition](#template-composition)
         
     | 
| 
       99 
     | 
    
         
            -
            - [Parameter and Block Application](#parameter-and-block-application)
         
     | 
| 
       100 
     | 
    
         
            -
            - [Higher-Order Templates](#higher-order-templates)
         
     | 
| 
       101 
     | 
    
         
            -
            - [Layout Template Composition](#layout-template-composition)
         
     | 
| 
       102 
     | 
    
         
            -
            - [Rendering Markdown](#rendering-markdown)
         
     | 
| 
       103 
     | 
    
         
            -
            - [Deferred Evaluation](#deferred-evaluation)
         
     | 
| 
       104 
     | 
    
         
            -
            - [Cached Rendering](#cached-rendering)
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
            A typical example for a dashboard-type app markup can be found here:
         
     | 
| 
       107 
     | 
    
         
            -
            https://github.com/digital-fabric/papercraft/blob/master/examples/dashboard.rb
         
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
            ## Getting Started
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
            In Papercraft, an HTML template is expressed as a proc:
         
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       114 
     | 
    
         
            -
            html = -> {
         
     | 
| 
       115 
     | 
    
         
            -
              div(id: 'greeter') { p 'Hello!' }
         
     | 
| 
       116 
     | 
    
         
            -
            }
         
     | 
| 
       117 
     | 
    
         
            -
            ```
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
            Rendering a template is done using `Proc#render`:
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       122 
     | 
    
         
            -
            require 'papercraft'
         
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
     | 
    
         
            -
            html.render #=> "<div id="greeter"><p>Hello!</p></div>"
         
     | 
| 
       125 
     | 
    
         
            -
            ```
         
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
            ## Basic Markup
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
            Tags are added using unqualified method calls, and can be nested using blocks:
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       132 
     | 
    
         
            -
            -> {
         
     | 
| 
       133 
     | 
    
         
            -
              html {
         
     | 
| 
       134 
     | 
    
         
            -
                head {
         
     | 
| 
       135 
     | 
    
         
            -
                  title 'page title'
         
     | 
| 
       136 
     | 
    
         
            -
                }
         
     | 
| 
       137 
     | 
    
         
            -
                body {
         
     | 
| 
       138 
     | 
    
         
            -
                  article {
         
     | 
| 
       139 
     | 
    
         
            -
                    h1 'article title'
         
     | 
| 
       140 
     | 
    
         
            -
                  }
         
     | 
| 
       141 
     | 
    
         
            -
                }
         
     | 
| 
       142 
     | 
    
         
            -
              }
         
     | 
| 
       143 
     | 
    
         
            -
            }
         
     | 
| 
       144 
     | 
    
         
            -
            ```
         
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
            Tag methods accept a string argument, a block, or no argument at all:
         
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       149 
     | 
    
         
            -
            -> { p 'hello' }.render #=> "<p>hello</p>"
         
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
            -> { p { span '1'; span '2' } }.render #=> "<p><span>1</span><span>2</span></p>"
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
            -> { hr() }.render #=> "<hr/>"
         
     | 
| 
       154 
     | 
    
         
            -
            ```
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
            Tag methods also accept tag attributes, given as a hash:
         
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       159 
     | 
    
         
            -
            -> { img src: '/my.gif' }.render #=> "<img src=\"/my.gif\"/>"
         
     | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
            -> { p "foobar", class: 'important' }.render #=> "<p class=\"important\">foobar</p>"
         
     | 
| 
       162 
     | 
    
         
            -
            ```
         
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
            A `true` attribute value will emit a valueless attribute. A `nil` or `false`
         
     | 
| 
       165 
     | 
    
         
            -
            attribute value will emit nothing:
         
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
       167 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       168 
     | 
    
         
            -
            -> { button disabled: nil }.render #=> "<button></button>"
         
     | 
| 
       169 
     | 
    
         
            -
            -> { button disabled: true }.render #=> "<button disabled></button>"
         
     | 
| 
       170 
     | 
    
         
            -
            ```
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
            An attribute value given as an array will be joined by space characters:
         
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       175 
     | 
    
         
            -
            -> { div class: [:foo, :bar] }.render #=> "<div class=\"foo bar\"></div>"
         
     | 
| 
       176 
     | 
    
         
            -
            ```
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
            ### Tag and Attribute Formatting
         
     | 
| 
       179 
     | 
    
         
            -
             
     | 
| 
       180 
     | 
    
         
            -
            Papercraft does not make any assumption about what tags and attributes you can use. You
         
     | 
| 
       181 
     | 
    
         
            -
            can mix upper and lower case letters, and you can include arbitrary characters
         
     | 
| 
       182 
     | 
    
         
            -
            in tag and attribute names. However, in order to best adhere to the HTML specs
         
     | 
| 
       183 
     | 
    
         
            -
            and common practices, tag names and attributes will be formatted according to
         
     | 
| 
       184 
     | 
    
         
            -
            the following rules, depending on the template type:
         
     | 
| 
       185 
     | 
    
         
            -
             
     | 
| 
       186 
     | 
    
         
            -
            - HTML: underscores are converted to dashes:
         
     | 
| 
       187 
     | 
    
         
            -
             
     | 
| 
       188 
     | 
    
         
            -
              ```ruby
         
     | 
| 
       189 
     | 
    
         
            -
              -> {
         
     | 
| 
       190 
     | 
    
         
            -
                foo_bar { p 'Hello', data_name: 'world' }
         
     | 
| 
       191 
     | 
    
         
            -
              }.render #=> '<foo-bar><p data-name="world">Hello</p></foo-bar>'
         
     | 
| 
       192 
     | 
    
         
            -
              ```
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
            If you need more precise control over tag names, you can use the `#tag` method,
         
     | 
| 
       195 
     | 
    
         
            -
            which takes the tag name as its first parameter, then the rest of the parameters
         
     | 
| 
       196 
     | 
    
         
            -
            normally used for tags:
         
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       199 
     | 
    
         
            -
            -> {
         
     | 
| 
       200 
     | 
    
         
            -
              tag 'cra_zy__:!tag', 'foo'
         
     | 
| 
       201 
     | 
    
         
            -
            }.render #=> '<cra_zy__:!tag>foo</cra_zy__:!tag>'
         
     | 
| 
       202 
     | 
    
         
            -
            ```
         
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
            ### Escaping Content
         
     | 
| 
       205 
     | 
    
         
            -
             
     | 
| 
       206 
     | 
    
         
            -
            Papercraft automatically escapes all text content emitted in a template. The specific
         
     | 
| 
       207 
     | 
    
         
            -
            escaping algorithm depends on the template type. To emit raw HTML, use the
         
     | 
| 
       208 
     | 
    
         
            -
            `#raw` method as [described below](#builtin-methods).
         
     | 
| 
       209 
     | 
    
         
            -
             
     | 
| 
       210 
     | 
    
         
            -
            ## Builtin Methods
         
     | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
       212 
     | 
    
         
            -
            In addition to normal tags, Papercraft provides the following method calls for templates:
         
     | 
| 
       213 
     | 
    
         
            -
             
     | 
| 
       214 
     | 
    
         
            -
            ### `#text` - emit escaped text
         
     | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
     | 
    
         
            -
            `#text` is used for emitting text that will be escaped. This method can be used
         
     | 
| 
       217 
     | 
    
         
            -
            to emit text not directly inside an enclosing tag:
         
     | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
       219 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       220 
     | 
    
         
            -
            -> {
         
     | 
| 
       221 
     | 
    
         
            -
              p {
         
     | 
| 
       222 
     | 
    
         
            -
                text 'The time is: '
         
     | 
| 
       223 
     | 
    
         
            -
                span(Time.now, id: 'clock')
         
     | 
| 
       224 
     | 
    
         
            -
              }
         
     | 
| 
       225 
     | 
    
         
            -
            }.render #=> <p>The time is: <span id="clock">XX:XX:XX</span></p>
         
     | 
| 
       226 
     | 
    
         
            -
            ```
         
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
            ### `#raw` - emit raw HTML
         
     | 
| 
       229 
     | 
    
         
            -
             
     | 
| 
       230 
     | 
    
         
            -
            `#raw` is used for emitting raw HTML, i.e. without escaping. You can use this to
         
     | 
| 
       231 
     | 
    
         
            -
            emit an HTML snippet:
         
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
       233 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       234 
     | 
    
         
            -
            TITLE_HTML = '<h1>hi</h1>'
         
     | 
| 
       235 
     | 
    
         
            -
            -> {
         
     | 
| 
       236 
     | 
    
         
            -
              div {
         
     | 
| 
       237 
     | 
    
         
            -
                raw TITLE_HTML
         
     | 
| 
       238 
     | 
    
         
            -
              }
         
     | 
| 
       239 
     | 
    
         
            -
            }.render #=> <div><h1>hi</h1></div>
         
     | 
| 
       240 
     | 
    
         
            -
            ```
         
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
            ### `#render_children` - render the given block
         
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
            `#render_children` is used to emit a given block. If no block is given, a
         
     | 
| 
       245 
     | 
    
         
            -
            `LocalJumpError` exception is raised:
         
     | 
| 
       246 
     | 
    
         
            -
             
     | 
| 
       247 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       248 
     | 
    
         
            -
            Card = ->(**props) {
         
     | 
| 
       249 
     | 
    
         
            -
              card { render_children(**props) }
         
     | 
| 
       250 
     | 
    
         
            -
            }
         
     | 
| 
       251 
     | 
    
         
            -
             
     | 
| 
       252 
     | 
    
         
            -
            Card.render(foo: 'bar') { |foo|
         
     | 
| 
       253 
     | 
    
         
            -
              h1 foo
         
     | 
| 
       254 
     | 
    
         
            -
            } #=> <card><h1>bar</h1></card>
         
     | 
| 
       255 
     | 
    
         
            -
            ```
         
     | 
| 
       256 
     | 
    
         
            -
             
     | 
| 
       257 
     | 
    
         
            -
            `render_children` can be called with or without arguments, which are passed to the
         
     | 
| 
       258 
     | 
    
         
            -
            given block.
         
     | 
| 
       259 
     | 
    
         
            -
             
     | 
| 
       260 
     | 
    
         
            -
            ### `#defer` - emit deferred HTML
         
     | 
| 
       261 
     | 
    
         
            -
             
     | 
| 
       262 
     | 
    
         
            -
            `#defer` is used to emit HTML in a deferred fashion - the deferred part will be
         
     | 
| 
       263 
     | 
    
         
            -
            evaluated only after processing the entire template:
         
     | 
| 
       264 
     | 
    
         
            -
             
     | 
| 
       265 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       266 
     | 
    
         
            -
            Layout = -> {
         
     | 
| 
       267 
     | 
    
         
            -
              head {
         
     | 
| 
       268 
     | 
    
         
            -
                defer {
         
     | 
| 
       269 
     | 
    
         
            -
                  title @title
         
     | 
| 
       270 
     | 
    
         
            -
                }
         
     | 
| 
       271 
     | 
    
         
            -
              }
         
     | 
| 
       272 
     | 
    
         
            -
              body {
         
     | 
| 
       273 
     | 
    
         
            -
                render_children
         
     | 
| 
       274 
     | 
    
         
            -
              }
         
     | 
| 
       275 
     | 
    
         
            -
            }
         
     | 
| 
       276 
     | 
    
         
            -
             
     | 
| 
       277 
     | 
    
         
            -
            Layout.render {
         
     | 
| 
       278 
     | 
    
         
            -
              @title = 'Foobar'
         
     | 
| 
       279 
     | 
    
         
            -
              h1 'hi'
         
     | 
| 
       280 
     | 
    
         
            -
            } #=> <head><title>Foobar</title></head><body><h1>hi</h1></body>
         
     | 
| 
       281 
     | 
    
         
            -
            ```
         
     | 
| 
       282 
     | 
    
         
            -
             
     | 
| 
       283 
     | 
    
         
            -
            ### `#render` - render the given template inline
         
     | 
| 
       284 
     | 
    
         
            -
             
     | 
| 
       285 
     | 
    
         
            -
            `#render` is used to emit the given template. This can be used to compose
         
     | 
| 
       286 
     | 
    
         
            -
            templates:
         
     | 
| 
       287 
     | 
    
         
            -
             
     | 
| 
       288 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       289 
     | 
    
         
            -
            partial = -> { p 'foo' }
         
     | 
| 
       290 
     | 
    
         
            -
            -> {
         
     | 
| 
       291 
     | 
    
         
            -
              div {
         
     | 
| 
       292 
     | 
    
         
            -
                render partial
         
     | 
| 
       293 
     | 
    
         
            -
              }
         
     | 
| 
       294 
     | 
    
         
            -
            }.render #=> <div><p>foo</p></div>
         
     | 
| 
       295 
     | 
    
         
            -
            ```
         
     | 
| 
       296 
     | 
    
         
            -
             
     | 
| 
       297 
     | 
    
         
            -
            Any argument following the given template is passed to the template for
         
     | 
| 
       298 
     | 
    
         
            -
            rendering:
         
     | 
| 
       299 
     | 
    
         
            -
             
     | 
| 
       300 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       301 
     | 
    
         
            -
            large_button = ->(title) { button(title, class: 'large') }
         
     | 
| 
       302 
     | 
    
         
            -
             
     | 
| 
       303 
     | 
    
         
            -
            -> {
         
     | 
| 
       304 
     | 
    
         
            -
              render large_button, 'foo'
         
     | 
| 
       305 
     | 
    
         
            -
            }.render #=> <button class="large">foo</button>
         
     | 
| 
       306 
     | 
    
         
            -
            ```
         
     | 
| 
       307 
     | 
    
         
            -
             
     | 
| 
       308 
     | 
    
         
            -
            ### `#html`/`#html5` - emit an HTML5 document type declaration and html tag
         
     | 
| 
       309 
     | 
    
         
            -
             
     | 
| 
       310 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       311 
     | 
    
         
            -
            -> {
         
     | 
| 
       312 
     | 
    
         
            -
              html5 {
         
     | 
| 
       313 
     | 
    
         
            -
                p 'hi'
         
     | 
| 
       314 
     | 
    
         
            -
              }
         
     | 
| 
       315 
     | 
    
         
            -
            } #=> <!DOCTYPE html><html><p>hi</p></html>
         
     | 
| 
       316 
     | 
    
         
            -
            ```
         
     | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
       318 
     | 
    
         
            -
            ### `#markdown` emit markdown content
         
     | 
| 
       319 
     | 
    
         
            -
             
     | 
| 
       320 
     | 
    
         
            -
            `#markdown` is used for rendering markdown content. The call converts the given
         
     | 
| 
       321 
     | 
    
         
            -
            markdown to HTML and emits it into the rendered HTML:
         
     | 
| 
       322 
     | 
    
         
            -
             
     | 
| 
       323 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       324 
     | 
    
         
            -
            -> {
         
     | 
| 
       325 
     | 
    
         
            -
              div {
         
     | 
| 
       326 
     | 
    
         
            -
                markdown 'This is *markdown*'
         
     | 
| 
       327 
     | 
    
         
            -
              }
         
     | 
| 
       328 
     | 
    
         
            -
            }.render #=> <p>This is <em>markdown</em></p>
         
     | 
| 
       329 
     | 
    
         
            -
            ```
         
     | 
| 
       330 
     | 
    
         
            -
             
     | 
| 
       331 
     | 
    
         
            -
            ## Template Parameters
         
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
            In Papercraft, parameters are always passed explicitly. This means that template
         
     | 
| 
       334 
     | 
    
         
            -
            parameters are specified as block parameters, and are passed to the template on
         
     | 
| 
       335 
     | 
    
         
            -
            rendering:
         
     | 
| 
       336 
     | 
    
         
            -
             
     | 
| 
       337 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       338 
     | 
    
         
            -
            greeting = ->(name) { h1 "Hello, #{name}!" }
         
     | 
| 
       339 
     | 
    
         
            -
            greeting.render('world') #=> "<h1>Hello, world!</h1>"
         
     | 
| 
       340 
     | 
    
         
            -
            ```
         
     | 
| 
       341 
     | 
    
         
            -
             
     | 
| 
       342 
     | 
    
         
            -
            Templates can also accept named parameters:
         
     | 
| 
       343 
     | 
    
         
            -
             
     | 
| 
       344 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       345 
     | 
    
         
            -
            greeting = ->(name:) { h1 "Hello, #{name}!" }
         
     | 
| 
       346 
     | 
    
         
            -
            greeting.render(name: 'world') #=> "<h1>Hello, world!</h1>"
         
     | 
| 
       347 
     | 
    
         
            -
            ```
         
     | 
| 
       348 
     | 
    
         
            -
             
     | 
| 
       349 
     | 
    
         
            -
            ## Template Logic
         
     | 
| 
       350 
     | 
    
         
            -
             
     | 
| 
       351 
     | 
    
         
            -
            Since Papercraft templates are just a bunch of Ruby, you can easily embed your view
         
     | 
| 
       352 
     | 
    
         
            -
            logic right in the template:
         
     | 
| 
       353 
     | 
    
         
            -
             
     | 
| 
       354 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       355 
     | 
    
         
            -
            ->(user = nil) {
         
     | 
| 
       356 
     | 
    
         
            -
              if user
         
     | 
| 
       357 
     | 
    
         
            -
                span "Hello, #{user.name}!"
         
     | 
| 
       358 
     | 
    
         
            -
              else
         
     | 
| 
       359 
     | 
    
         
            -
                span "Hello, guest!"
         
     | 
| 
       360 
     | 
    
         
            -
              end
         
     | 
| 
       361 
     | 
    
         
            -
            }
         
     | 
| 
       362 
     | 
    
         
            -
            ```
         
     | 
| 
       363 
     | 
    
         
            -
             
     | 
| 
       364 
     | 
    
         
            -
            ## Template Blocks
         
     | 
| 
       365 
     | 
    
         
            -
             
     | 
| 
       366 
     | 
    
         
            -
            Templates can also accept and render blocks by using `render_children`:
         
     | 
| 
       367 
     | 
    
         
            -
             
     | 
| 
       368 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       369 
     | 
    
         
            -
            page = -> {
         
     | 
| 
       370 
     | 
    
         
            -
              html {
         
     | 
| 
       371 
     | 
    
         
            -
                body { render_children }
         
     | 
| 
       372 
     | 
    
         
            -
              }
         
     | 
| 
       373 
     | 
    
         
            -
            }
         
     | 
| 
       374 
     | 
    
         
            -
             
     | 
| 
       375 
     | 
    
         
            -
            # we pass the inner HTML
         
     | 
| 
       376 
     | 
    
         
            -
            page.render { h1 'hi' }
         
     | 
| 
       377 
     | 
    
         
            -
            ```
         
     | 
| 
       378 
     | 
    
         
            -
             
     | 
| 
       379 
     | 
    
         
            -
            ## Template Composition
         
     | 
| 
       380 
     | 
    
         
            -
             
     | 
| 
       381 
     | 
    
         
            -
            Papercraft makes it easy to compose multiple templates into a whole HTML document. A Papercraft
         
     | 
| 
       382 
     | 
    
         
            -
            template can contain other templates, as the following example shows.
         
     | 
| 
       383 
     | 
    
         
            -
             
     | 
| 
       384 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       385 
     | 
    
         
            -
            Title = ->(title) { h1 title }
         
     | 
| 
       386 
     | 
    
         
            -
             
     | 
| 
       387 
     | 
    
         
            -
            Item = ->(id:, text:, checked:) {
         
     | 
| 
       388 
     | 
    
         
            -
              li {
         
     | 
| 
       389 
     | 
    
         
            -
                input name: id, type: 'checkbox', checked: checked
         
     | 
| 
       390 
     | 
    
         
            -
                label text, for: id
         
     | 
| 
       391 
     | 
    
         
            -
              }
         
     | 
| 
       392 
     | 
    
         
            -
            }
         
     | 
| 
       393 
     | 
    
         
            -
             
     | 
| 
       394 
     | 
    
         
            -
            ItemList = ->(items) {
         
     | 
| 
       395 
     | 
    
         
            -
              ul {
         
     | 
| 
       396 
     | 
    
         
            -
                items.each { |i|
         
     | 
| 
       397 
     | 
    
         
            -
                  Item(**i)
         
     | 
| 
       398 
     | 
    
         
            -
                }
         
     | 
| 
       399 
     | 
    
         
            -
              }
         
     | 
| 
       400 
     | 
    
         
            -
            }
         
     | 
| 
       401 
     | 
    
         
            -
             
     | 
| 
       402 
     | 
    
         
            -
            page = ->(title, items) {
         
     | 
| 
       403 
     | 
    
         
            -
              html5 {
         
     | 
| 
       404 
     | 
    
         
            -
                head { Title(title) }
         
     | 
| 
       405 
     | 
    
         
            -
                body { ItemList(items) }
         
     | 
| 
       406 
     | 
    
         
            -
              }
         
     | 
| 
       407 
     | 
    
         
            -
            }
         
     | 
| 
       408 
     | 
    
         
            -
             
     | 
| 
       409 
     | 
    
         
            -
            page.render('Hello from composed templates', [
         
     | 
| 
       410 
     | 
    
         
            -
              { id: 1, text: 'foo', checked: false },
         
     | 
| 
       411 
     | 
    
         
            -
              { id: 2, text: 'bar', checked: true }
         
     | 
| 
       412 
     | 
    
         
            -
            ])
         
     | 
| 
       413 
     | 
    
         
            -
            ```
         
     | 
| 
       414 
     | 
    
         
            -
             
     | 
| 
       415 
     | 
    
         
            -
            In addition to using templates defined as constants, you can also use
         
     | 
| 
       416 
     | 
    
         
            -
            non-constant templates by invoking the `#render` method:
         
     | 
| 
       417 
     | 
    
         
            -
             
     | 
| 
       418 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       419 
     | 
    
         
            -
            greeting = -> { span "Hello, world" }
         
     | 
| 
       420 
     | 
    
         
            -
             
     | 
| 
       421 
     | 
    
         
            -
            -> {
         
     | 
| 
       422 
     | 
    
         
            -
              div {
         
     | 
| 
       423 
     | 
    
         
            -
                render greeting
         
     | 
| 
       424 
     | 
    
         
            -
              }
         
     | 
| 
       425 
     | 
    
         
            -
            }
         
     | 
| 
       426 
     | 
    
         
            -
            ```
         
     | 
| 
       427 
     | 
    
         
            -
             
     | 
| 
       428 
     | 
    
         
            -
            ## Parameter and Block Application
         
     | 
| 
       429 
     | 
    
         
            -
             
     | 
| 
       430 
     | 
    
         
            -
            Parameters and blocks can be applied to a template without it being rendered, by
         
     | 
| 
       431 
     | 
    
         
            -
            using `#apply`. This mechanism is what allows template composition and the
         
     | 
| 
       432 
     | 
    
         
            -
            creation of higher-order templates.
         
     | 
| 
       433 
     | 
    
         
            -
             
     | 
| 
       434 
     | 
    
         
            -
            The `#apply` method returns a new template which applies the given parameters
         
     | 
| 
       435 
     | 
    
         
            -
            and or block to the original template:
         
     | 
| 
       436 
     | 
    
         
            -
             
     | 
| 
       437 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       438 
     | 
    
         
            -
            # parameter application
         
     | 
| 
       439 
     | 
    
         
            -
            hello = -> { |name| h1 "Hello, #{name}!" }
         
     | 
| 
       440 
     | 
    
         
            -
            hello_world = hello.apply('world')
         
     | 
| 
       441 
     | 
    
         
            -
            hello_world.render #=> "<h1>Hello, world!</h1>"
         
     | 
| 
       442 
     | 
    
         
            -
             
     | 
| 
       443 
     | 
    
         
            -
            # block application
         
     | 
| 
       444 
     | 
    
         
            -
            div_wrap = -> { div { render_children } }
         
     | 
| 
       445 
     | 
    
         
            -
            wrapped_h1 = div_wrap.apply { h1 'hi' }
         
     | 
| 
       446 
     | 
    
         
            -
            wrapped_h1.render #=> "<div><h1>hi</h1></div>"
         
     | 
| 
       447 
     | 
    
         
            -
             
     | 
| 
       448 
     | 
    
         
            -
            # wrap a template
         
     | 
| 
       449 
     | 
    
         
            -
            wrapped_hello_world = div_wrap.apply(&hello_world)
         
     | 
| 
       450 
     | 
    
         
            -
            wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
         
     | 
| 
       451 
     | 
    
         
            -
            ```
         
     | 
| 
       452 
     | 
    
         
            -
             
     | 
| 
       453 
     | 
    
         
            -
            ## Higher-Order Templates
         
     | 
| 
       454 
     | 
    
         
            -
             
     | 
| 
       455 
     | 
    
         
            -
            Papercraft also lets you create higher-order templates, that is, templates that take
         
     | 
| 
       456 
     | 
    
         
            -
            other templates as parameters, or as blocks. Higher-order templates are handy
         
     | 
| 
       457 
     | 
    
         
            -
            for creating layouts, wrapping templates in arbitrary markup, enhancing
         
     | 
| 
       458 
     | 
    
         
            -
            templates or injecting template parameters.
         
     | 
| 
       459 
     | 
    
         
            -
             
     | 
| 
       460 
     | 
    
         
            -
            Here is a higher-order template that takes a template as parameter:
         
     | 
| 
       461 
     | 
    
         
            -
             
     | 
| 
       462 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       463 
     | 
    
         
            -
            div_wrap = -> { |inner| div { render inner } }
         
     | 
| 
       464 
     | 
    
         
            -
            greeter = -> { h1 'hi' }
         
     | 
| 
       465 
     | 
    
         
            -
            wrapped_greeter = div_wrap.apply(greeter)
         
     | 
| 
       466 
     | 
    
         
            -
            wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
         
     | 
| 
       467 
     | 
    
         
            -
            ```
         
     | 
| 
       468 
     | 
    
         
            -
             
     | 
| 
       469 
     | 
    
         
            -
            The inner template can also be passed as a block, as shown above:
         
     | 
| 
       470 
     | 
    
         
            -
             
     | 
| 
       471 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       472 
     | 
    
         
            -
            div_wrap = -> { div { render_children } }
         
     | 
| 
       473 
     | 
    
         
            -
            wrapped_greeter = div_wrap.apply { h1 'hi' }
         
     | 
| 
       474 
     | 
    
         
            -
            wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
         
     | 
| 
       475 
     | 
    
         
            -
            ```
         
     | 
| 
       476 
     | 
    
         
            -
             
     | 
| 
       477 
     | 
    
         
            -
            ## Layout Template Composition
         
     | 
| 
       478 
     | 
    
         
            -
             
     | 
| 
       479 
     | 
    
         
            -
            One of the principal uses of higher-order templates is the creation of nested
         
     | 
| 
       480 
     | 
    
         
            -
            layouts. Suppose we have a website with a number of different layouts, and we'd
         
     | 
| 
       481 
     | 
    
         
            -
            like to avoid having to repeat the same code in the different layouts. We can do
         
     | 
| 
       482 
     | 
    
         
            -
            this by creating a `default` page template that takes a block, then use `#apply`
         
     | 
| 
       483 
     | 
    
         
            -
            to create the other templates:
         
     | 
| 
       484 
     | 
    
         
            -
             
     | 
| 
       485 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       486 
     | 
    
         
            -
            default_layout = -> { |**params|
         
     | 
| 
       487 
     | 
    
         
            -
              html5 {
         
     | 
| 
       488 
     | 
    
         
            -
                head {
         
     | 
| 
       489 
     | 
    
         
            -
                  title: params[:title]
         
     | 
| 
       490 
     | 
    
         
            -
                }
         
     | 
| 
       491 
     | 
    
         
            -
                body {
         
     | 
| 
       492 
     | 
    
         
            -
                  render_children(**params)
         
     | 
| 
       493 
     | 
    
         
            -
                }
         
     | 
| 
       494 
     | 
    
         
            -
              }
         
     | 
| 
       495 
     | 
    
         
            -
            }
         
     | 
| 
       496 
     | 
    
         
            -
             
     | 
| 
       497 
     | 
    
         
            -
            article_layout = default_layout.apply { |title:, body:|
         
     | 
| 
       498 
     | 
    
         
            -
              article {
         
     | 
| 
       499 
     | 
    
         
            -
                h1 title
         
     | 
| 
       500 
     | 
    
         
            -
                markdown body
         
     | 
| 
       501 
     | 
    
         
            -
              }
         
     | 
| 
       502 
     | 
    
         
            -
            }
         
     | 
| 
       503 
     | 
    
         
            -
             
     | 
| 
       504 
     | 
    
         
            -
            article_layout.render(
         
     | 
| 
       505 
     | 
    
         
            -
              title: 'This is a title',
         
     | 
| 
       506 
     | 
    
         
            -
              body: 'Hello from *markdown body*'
         
     | 
| 
       507 
     | 
    
         
            -
            )
         
     | 
| 
       508 
     | 
    
         
            -
            ```
         
     | 
| 
       509 
     | 
    
         
            -
             
     | 
| 
       510 
     | 
    
         
            -
            ## Rendering Markdown
         
     | 
| 
       511 
     | 
    
         
            -
             
     | 
| 
       512 
     | 
    
         
            -
            Markdown is rendered using the
         
     | 
| 
       513 
     | 
    
         
            -
            [Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
         
     | 
| 
       514 
     | 
    
         
            -
            `#markdown`:
         
     | 
| 
       515 
     | 
    
         
            -
             
     | 
| 
       516 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       517 
     | 
    
         
            -
            template = -> { |md| div { markdown md } }
         
     | 
| 
       518 
     | 
    
         
            -
            template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
         
     | 
| 
       519 
     | 
    
         
            -
            ```
         
     | 
| 
       520 
     | 
    
         
            -
             
     | 
| 
       521 
     | 
    
         
            -
            [Kramdown
         
     | 
| 
       522 
     | 
    
         
            -
            options](https://kramdown.gettalong.org/options.html#available-options) can be
         
     | 
| 
       523 
     | 
    
         
            -
            specified by adding them to the `#markdown` call:
         
     | 
| 
       524 
     | 
    
         
            -
             
     | 
| 
       525 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       526 
     | 
    
         
            -
            template = -> { |md| div { markdown md, auto_ids: false } }
         
     | 
| 
       527 
     | 
    
         
            -
            template.render("# title") #=> "<div><h1>title</h1></div>"
         
     | 
| 
       528 
     | 
    
         
            -
            ```
         
     | 
| 
       529 
     | 
    
         
            -
             
     | 
| 
       530 
     | 
    
         
            -
            You can also use `Papercraft.markdown` directly:
         
     | 
| 
       531 
     | 
    
         
            -
             
     | 
| 
       532 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       533 
     | 
    
         
            -
            Papercraft.markdown('# title') #=> "<h1>title</h1>"
         
     | 
| 
       534 
     | 
    
         
            -
            ```
         
     | 
| 
       535 
     | 
    
         
            -
             
     | 
| 
       536 
     | 
    
         
            -
            The default Kramdown options are:
         
     | 
| 
      
 43 
     | 
    
         
            +
            ## Documentation
         
     | 
| 
       537 
44 
     | 
    
         | 
| 
       538 
     | 
    
         
            -
             
     | 
| 
       539 
     | 
    
         
            -
             
     | 
| 
       540 
     | 
    
         
            -
              entity_output: :numeric,
         
     | 
| 
       541 
     | 
    
         
            -
              syntax_highlighter: :rouge,
         
     | 
| 
       542 
     | 
    
         
            -
              input: 'GFM',
         
     | 
| 
       543 
     | 
    
         
            -
              hard_wrap: false
         
     | 
| 
       544 
     | 
    
         
            -
            }
         
     | 
| 
       545 
     | 
    
         
            -
            ```
         
     | 
| 
       546 
     | 
    
         
            -
             
     | 
| 
       547 
     | 
    
         
            -
            The deafult options can be configured by accessing
         
     | 
| 
       548 
     | 
    
         
            -
            `Papercraft.default_kramdown_options`, e.g.:
         
     | 
| 
       549 
     | 
    
         
            -
             
     | 
| 
       550 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       551 
     | 
    
         
            -
            Papercraft.default_kramdown_options[:auto_ids] = false
         
     | 
| 
       552 
     | 
    
         
            -
            ```
         
     | 
| 
       553 
     | 
    
         
            -
             
     | 
| 
       554 
     | 
    
         
            -
            ## Deferred Evaluation
         
     | 
| 
       555 
     | 
    
         
            -
             
     | 
| 
       556 
     | 
    
         
            -
            Deferred evaluation allows deferring the rendering of parts of a template until
         
     | 
| 
       557 
     | 
    
         
            -
            the last moment, thus allowing an inner template to manipulate the state of the
         
     | 
| 
       558 
     | 
    
         
            -
            outer template. To in order to defer a part of a template, use `#defer`, and
         
     | 
| 
       559 
     | 
    
         
            -
            include any markup in the provided block. This technique, in in conjunction with
         
     | 
| 
       560 
     | 
    
         
            -
            holding state in instance variables, is an alternative to passing parameters,
         
     | 
| 
       561 
     | 
    
         
            -
            which can be limiting in some situations.
         
     | 
| 
       562 
     | 
    
         
            -
             
     | 
| 
       563 
     | 
    
         
            -
            A few use cases for deferred evaulation come to mind:
         
     | 
| 
       564 
     | 
    
         
            -
             
     | 
| 
       565 
     | 
    
         
            -
            - Setting the page title.
         
     | 
| 
       566 
     | 
    
         
            -
            - Adding a flash message to a page.
         
     | 
| 
       567 
     | 
    
         
            -
            - Using templates that dynamically add static dependencies (JS and CSS) to the
         
     | 
| 
       568 
     | 
    
         
            -
              page.
         
     | 
| 
       569 
     | 
    
         
            -
             
     | 
| 
       570 
     | 
    
         
            -
            The last use case is particularly interesting. Imagine a `DependencyMananger`
         
     | 
| 
       571 
     | 
    
         
            -
            class that can collect JS and CSS dependencies from the different templates
         
     | 
| 
       572 
     | 
    
         
            -
            integrated into the page, and adds them to the page's `<head>` element:
         
     | 
| 
       573 
     | 
    
         
            -
             
     | 
| 
       574 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       575 
     | 
    
         
            -
            deps = DependencyMananger.new
         
     | 
| 
       576 
     | 
    
         
            -
             
     | 
| 
       577 
     | 
    
         
            -
            default_layout = -> { |**args|
         
     | 
| 
       578 
     | 
    
         
            -
              head {
         
     | 
| 
       579 
     | 
    
         
            -
                defer { render deps.head_markup }
         
     | 
| 
       580 
     | 
    
         
            -
              }
         
     | 
| 
       581 
     | 
    
         
            -
              body { render_children **args }
         
     | 
| 
       582 
     | 
    
         
            -
            }
         
     | 
| 
       583 
     | 
    
         
            -
             
     | 
| 
       584 
     | 
    
         
            -
            button = proc { |text, onclick|
         
     | 
| 
       585 
     | 
    
         
            -
              deps.js '/static/js/button.js'
         
     | 
| 
       586 
     | 
    
         
            -
              deps.css '/static/css/button.css'
         
     | 
| 
       587 
     | 
    
         
            -
             
     | 
| 
       588 
     | 
    
         
            -
              button text, onclick: onclick
         
     | 
| 
       589 
     | 
    
         
            -
            }
         
     | 
| 
       590 
     | 
    
         
            -
             
     | 
| 
       591 
     | 
    
         
            -
            heading = proc { |text|
         
     | 
| 
       592 
     | 
    
         
            -
              deps.js '/static/js/heading.js'
         
     | 
| 
       593 
     | 
    
         
            -
              deps.css '/static/css/heading.css'
         
     | 
| 
       594 
     | 
    
         
            -
             
     | 
| 
       595 
     | 
    
         
            -
              h1 text
         
     | 
| 
       596 
     | 
    
         
            -
            }
         
     | 
| 
       597 
     | 
    
         
            -
             
     | 
| 
       598 
     | 
    
         
            -
            page = default_layout.apply {
         
     | 
| 
       599 
     | 
    
         
            -
              render heading, "What's your favorite cheese?"
         
     | 
| 
       600 
     | 
    
         
            -
             
     | 
| 
       601 
     | 
    
         
            -
              render button, 'Beaufort', 'eat_beaufort()'
         
     | 
| 
       602 
     | 
    
         
            -
              render button, 'Mont d''or', 'eat_montdor()'
         
     | 
| 
       603 
     | 
    
         
            -
              render button, 'Époisses', 'eat_epoisses()'
         
     | 
| 
       604 
     | 
    
         
            -
            }
         
     | 
| 
       605 
     | 
    
         
            -
            ```
         
     | 
| 
       606 
     | 
    
         
            -
             
     | 
| 
       607 
     | 
    
         
            -
            ## Cached Rendering
         
     | 
| 
       608 
     | 
    
         
            -
             
     | 
| 
       609 
     | 
    
         
            -
            Papercraft provides a simple API for caching the result of a rendering. The cache stores
         
     | 
| 
       610 
     | 
    
         
            -
            renderings of a template respective to the given arguments. To automatically
         
     | 
| 
       611 
     | 
    
         
            -
            retrieve the cached rendered HTML, or generate it for the first time, use
         
     | 
| 
       612 
     | 
    
         
            -
            `Proc#render_cached`:
         
     | 
| 
       613 
     | 
    
         
            -
             
     | 
| 
       614 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       615 
     | 
    
         
            -
            template = ->(title) { div { h1 title } }
         
     | 
| 
       616 
     | 
    
         
            -
            template.render_cached('foo') #=> <div><h1>foo</h1></div>
         
     | 
| 
       617 
     | 
    
         
            -
            template.render_cached('foo') #=> <div><h1>foo</h1></div> (from cache)
         
     | 
| 
       618 
     | 
    
         
            -
            template.render_cached('bar') #=> <div><h1>bar</h1></div>
         
     | 
| 
       619 
     | 
    
         
            -
            template.render_cached('bar') #=> <div><h1>bar</h1></div> (from cache)
         
     | 
| 
       620 
     | 
    
         
            -
            ```
         
     | 
| 
      
 45 
     | 
    
         
            +
            For more information, please consult the [Papercraft
         
     | 
| 
      
 46 
     | 
    
         
            +
            website](https://papercraft.noteflakes.com/).
         
     | 
    
        data/lib/papercraft/compiler.rb
    CHANGED
    
    | 
         @@ -178,7 +178,7 @@ module Papercraft 
     | 
|
| 
       178 
178 
     | 
    
         
             
                  when Prism::BlockArgumentNode
         
     | 
| 
       179 
179 
     | 
    
         
             
                    flush_html_parts!
         
     | 
| 
       180 
180 
     | 
    
         
             
                    adjust_whitespace(node.block)
         
     | 
| 
       181 
     | 
    
         
            -
                    emit("; #{format_code(node.block.expression)}. 
     | 
| 
      
 181 
     | 
    
         
            +
                    emit("; #{format_code(node.block.expression)}.__compiled_proc__.(__buffer__)")
         
     | 
| 
       182 
182 
     | 
    
         
             
                  end
         
     | 
| 
       183 
183 
     | 
    
         | 
| 
       184 
184 
     | 
    
         
             
                  if node.inner_text
         
     | 
| 
         @@ -213,7 +213,7 @@ module Papercraft 
     | 
|
| 
       213 
213 
     | 
    
         
             
                    emit(format_code(node.call_node.receiver))
         
     | 
| 
       214 
214 
     | 
    
         
             
                    emit('::')
         
     | 
| 
       215 
215 
     | 
    
         
             
                  end
         
     | 
| 
       216 
     | 
    
         
            -
                  emit("#{node.call_node.name}. 
     | 
| 
      
 216 
     | 
    
         
            +
                  emit("#{node.call_node.name}.__compiled_proc__.(__buffer__")
         
     | 
| 
       217 
217 
     | 
    
         
             
                  if node.call_node.arguments
         
     | 
| 
       218 
218 
     | 
    
         
             
                    emit(', ')
         
     | 
| 
       219 
219 
     | 
    
         
             
                    visit(node.call_node.arguments)
         
     | 
| 
         @@ -229,17 +229,17 @@ module Papercraft 
     | 
|
| 
       229 
229 
     | 
    
         
             
                  args = node.call_node.arguments.arguments
         
     | 
| 
       230 
230 
     | 
    
         
             
                  first_arg = args.first
         
     | 
| 
       231 
231 
     | 
    
         | 
| 
       232 
     | 
    
         
            -
                  block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}. 
     | 
| 
      
 232 
     | 
    
         
            +
                  block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}.__compiled__!)"
         
     | 
| 
       233 
233 
     | 
    
         
             
                  block_embed = ", #{block_embed}" if block_embed && node.call_node.arguments
         
     | 
| 
       234 
234 
     | 
    
         | 
| 
       235 
235 
     | 
    
         
             
                  flush_html_parts!
         
     | 
| 
       236 
236 
     | 
    
         
             
                  adjust_whitespace(node.location)
         
     | 
| 
       237 
237 
     | 
    
         | 
| 
       238 
238 
     | 
    
         
             
                  if args.length == 1
         
     | 
| 
       239 
     | 
    
         
            -
                    emit("; #{format_code(first_arg)}. 
     | 
| 
      
 239 
     | 
    
         
            +
                    emit("; #{format_code(first_arg)}.__compiled_proc__.(__buffer__#{block_embed})")
         
     | 
| 
       240 
240 
     | 
    
         
             
                  else
         
     | 
| 
       241 
241 
     | 
    
         
             
                    args_code = format_code_comma_separated_nodes(args[1..])
         
     | 
| 
       242 
     | 
    
         
            -
                    emit("; #{format_code(first_arg)}. 
     | 
| 
      
 242 
     | 
    
         
            +
                    emit("; #{format_code(first_arg)}.__compiled_proc__.(__buffer__, #{args_code}#{block_embed})")
         
     | 
| 
       243 
243 
     | 
    
         
             
                  end
         
     | 
| 
       244 
244 
     | 
    
         
             
                end
         
     | 
| 
       245 
245 
     | 
    
         | 
| 
         @@ -336,7 +336,7 @@ module Papercraft 
     | 
|
| 
       336 
336 
     | 
    
         
             
                def visit_extension_tag_node(node)
         
     | 
| 
       337 
337 
     | 
    
         
             
                  flush_html_parts!
         
     | 
| 
       338 
338 
     | 
    
         
             
                  adjust_whitespace(node.location)
         
     | 
| 
       339 
     | 
    
         
            -
                  emit("; Papercraft::Extensions[#{node.tag.inspect}]. 
     | 
| 
      
 339 
     | 
    
         
            +
                  emit("; Papercraft::Extensions[#{node.tag.inspect}].__compiled_proc__.(__buffer__")
         
     | 
| 
       340 
340 
     | 
    
         
             
                  if node.call_node.arguments
         
     | 
| 
       341 
341 
     | 
    
         
             
                    emit(', ')
         
     | 
| 
       342 
342 
     | 
    
         
             
                    visit(node.call_node.arguments)
         
     | 
| 
         @@ -367,7 +367,7 @@ module Papercraft 
     | 
|
| 
       367 
367 
     | 
    
         
             
                    end
         
     | 
| 
       368 
368 
     | 
    
         
             
                    block_params = block_params.empty? ? '' : ", #{block_params.join(', ')}"
         
     | 
| 
       369 
369 
     | 
    
         | 
| 
       370 
     | 
    
         
            -
                    emit(", &(proc { |__buffer__#{block_params}| #{block_body} }). 
     | 
| 
      
 370 
     | 
    
         
            +
                    emit(", &(proc { |__buffer__#{block_params}| #{block_body} }).__compiled__!")
         
     | 
| 
       371 
371 
     | 
    
         
             
                  end
         
     | 
| 
       372 
372 
     | 
    
         
             
                  emit(")")
         
     | 
| 
       373 
373 
     | 
    
         
             
                end
         
     | 
| 
         @@ -382,7 +382,7 @@ module Papercraft 
     | 
|
| 
       382 
382 
     | 
    
         
             
                  guard = @render_yield_used ?
         
     | 
| 
       383 
383 
     | 
    
         
             
                    '' : "; raise(LocalJumpError, 'no block given (render_yield)') if !__block__"
         
     | 
| 
       384 
384 
     | 
    
         
             
                  @render_yield_used = true
         
     | 
| 
       385 
     | 
    
         
            -
                  emit("#{guard}; __block__. 
     | 
| 
      
 385 
     | 
    
         
            +
                  emit("#{guard}; __block__.__compiled_proc__.(__buffer__")
         
     | 
| 
       386 
386 
     | 
    
         
             
                  if node.call_node.arguments
         
     | 
| 
       387 
387 
     | 
    
         
             
                    emit(', ')
         
     | 
| 
       388 
388 
     | 
    
         
             
                    visit(node.call_node.arguments)
         
     | 
| 
         @@ -398,7 +398,7 @@ module Papercraft 
     | 
|
| 
       398 
398 
     | 
    
         
             
                  flush_html_parts!
         
     | 
| 
       399 
399 
     | 
    
         
             
                  adjust_whitespace(node.location)
         
     | 
| 
       400 
400 
     | 
    
         
             
                  @render_children_used = true
         
     | 
| 
       401 
     | 
    
         
            -
                  emit("; __block__&. 
     | 
| 
      
 401 
     | 
    
         
            +
                  emit("; __block__&.__compiled_proc__&.(__buffer__")
         
     | 
| 
       402 
402 
     | 
    
         
             
                  if node.call_node.arguments
         
     | 
| 
       403 
403 
     | 
    
         
             
                    emit(', ')
         
     | 
| 
       404 
404 
     | 
    
         
             
                    visit(node.call_node.arguments)
         
     | 
| 
         @@ -410,7 +410,7 @@ module Papercraft 
     | 
|
| 
       410 
410 
     | 
    
         
             
                  flush_html_parts!
         
     | 
| 
       411 
411 
     | 
    
         
             
                  adjust_whitespace(node.location)
         
     | 
| 
       412 
412 
     | 
    
         | 
| 
       413 
     | 
    
         
            -
                  emit("; #{node.call_node.receiver.name}. 
     | 
| 
      
 413 
     | 
    
         
            +
                  emit("; #{node.call_node.receiver.name}.__compiled_proc__.(__buffer__")
         
     | 
| 
       414 
414 
     | 
    
         
             
                  if node.call_node.arguments
         
     | 
| 
       415 
415 
     | 
    
         
             
                    emit(', ')
         
     | 
| 
       416 
416 
     | 
    
         
             
                    visit(node.call_node.arguments)
         
     | 
| 
         @@ -418,7 +418,7 @@ module Papercraft 
     | 
|
| 
       418 
418 
     | 
    
         
             
                  if node.call_node.block
         
     | 
| 
       419 
419 
     | 
    
         
             
                    emit(", &(->")
         
     | 
| 
       420 
420 
     | 
    
         
             
                    visit(node.call_node.block)
         
     | 
| 
       421 
     | 
    
         
            -
                    emit("). 
     | 
| 
      
 421 
     | 
    
         
            +
                    emit(").__compiled_proc__")
         
     | 
| 
       422 
422 
     | 
    
         
             
                  end
         
     | 
| 
       423 
423 
     | 
    
         
             
                  emit(")")
         
     | 
| 
       424 
424 
     | 
    
         
             
                end
         
     | 
    
        data/lib/papercraft/proc_ext.rb
    CHANGED
    
    | 
         @@ -5,42 +5,19 @@ require_relative './compiler' 
     | 
|
| 
       5 
5 
     | 
    
         
             
            module Papercraft
         
     | 
| 
       6 
6 
     | 
    
         
             
              # Extensions to the Proc class.
         
     | 
| 
       7 
7 
     | 
    
         
             
              module ProcExtensions
         
     | 
| 
       8 
     | 
    
         
            -
                # Returns the compiled form code for the proc.
         
     | 
| 
       9 
     | 
    
         
            -
                #
         
     | 
| 
       10 
     | 
    
         
            -
                # @return [String] compiled proc code
         
     | 
| 
       11 
     | 
    
         
            -
                def compiled_code
         
     | 
| 
       12 
     | 
    
         
            -
                  Papercraft::Compiler.compile_to_code(self).last
         
     | 
| 
       13 
     | 
    
         
            -
                end
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                # Returns the source map for the compiled proc.
         
     | 
| 
       16 
     | 
    
         
            -
                #
         
     | 
| 
       17 
     | 
    
         
            -
                # @return [Array<String>] source map
         
     | 
| 
       18 
     | 
    
         
            -
                def source_map
         
     | 
| 
       19 
     | 
    
         
            -
                  loc = source_location
         
     | 
| 
       20 
     | 
    
         
            -
                  fn = compiled? ? loc.first : Papercraft::Compiler.source_location_to_fn(loc)
         
     | 
| 
       21 
     | 
    
         
            -
                  Papercraft::Compiler.source_map_store[fn]
         
     | 
| 
       22 
     | 
    
         
            -
                end
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                # Returns the AST for the proc.
         
     | 
| 
       25 
     | 
    
         
            -
                #
         
     | 
| 
       26 
     | 
    
         
            -
                # @return [Prism::Node] AST root
         
     | 
| 
       27 
     | 
    
         
            -
                def ast
         
     | 
| 
       28 
     | 
    
         
            -
                  Sirop.to_ast(self)
         
     | 
| 
       29 
     | 
    
         
            -
                end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
8 
     | 
    
         
             
                # Returns true if proc is marked as compiled.
         
     | 
| 
       32 
9 
     | 
    
         
             
                #
         
     | 
| 
       33 
10 
     | 
    
         
             
                # @return [bool] is the proc marked as compiled
         
     | 
| 
       34 
     | 
    
         
            -
                def  
     | 
| 
       35 
     | 
    
         
            -
                  @ 
     | 
| 
      
 11 
     | 
    
         
            +
                def __compiled__?
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @__compiled__
         
     | 
| 
       36 
13 
     | 
    
         
             
                end
         
     | 
| 
       37 
14 
     | 
    
         | 
| 
       38 
15 
     | 
    
         
             
                # Marks the proc as compiled, i.e. can render directly and takes a string
         
     | 
| 
       39 
16 
     | 
    
         
             
                # buffer as first argument.
         
     | 
| 
       40 
17 
     | 
    
         
             
                #
         
     | 
| 
       41 
18 
     | 
    
         
             
                # @return [self]
         
     | 
| 
       42 
     | 
    
         
            -
                def  
     | 
| 
       43 
     | 
    
         
            -
                  @ 
     | 
| 
      
 19 
     | 
    
         
            +
                def __compiled__!
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @__compiled__ = true
         
     | 
| 
       44 
21 
     | 
    
         
             
                  self
         
     | 
| 
       45 
22 
     | 
    
         
             
                end
         
     | 
| 
       46 
23 
     | 
    
         | 
| 
         @@ -49,25 +26,15 @@ module Papercraft 
     | 
|
| 
       49 
26 
     | 
    
         
             
                #
         
     | 
| 
       50 
27 
     | 
    
         
             
                # @param mode [Symbol] compilation mode (:html, :xml)
         
     | 
| 
       51 
28 
     | 
    
         
             
                # @return [Proc] compiled proc or self
         
     | 
| 
       52 
     | 
    
         
            -
                def  
     | 
| 
       53 
     | 
    
         
            -
                  @ 
     | 
| 
       54 
     | 
    
         
            -
                end
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
                # Compiles the proc into the compiled form.
         
     | 
| 
       57 
     | 
    
         
            -
                #
         
     | 
| 
       58 
     | 
    
         
            -
                # @param mode [Symbol] compilation mode (:html, :xml)
         
     | 
| 
       59 
     | 
    
         
            -
                # @return [Proc] compiled proc
         
     | 
| 
       60 
     | 
    
         
            -
                def compile(mode: :html)
         
     | 
| 
       61 
     | 
    
         
            -
                  Papercraft::Compiler.compile(self, mode:).compiled!
         
     | 
| 
       62 
     | 
    
         
            -
                rescue Sirop::Error
         
     | 
| 
       63 
     | 
    
         
            -
                  raise Papercraft::Error, "Dynamically defined procs cannot be compiled"
         
     | 
| 
      
 29 
     | 
    
         
            +
                def __compiled_proc__(mode: :html)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @__compiled_proc__ ||= @__compiled__ ? self : Papercraft.compile(self, mode:)
         
     | 
| 
       64 
31 
     | 
    
         
             
                end
         
     | 
| 
       65 
32 
     | 
    
         | 
| 
       66 
33 
     | 
    
         
             
                # Renders the proc to HTML with the given arguments.
         
     | 
| 
       67 
34 
     | 
    
         
             
                #
         
     | 
| 
       68 
35 
     | 
    
         
             
                # @return [String] HTML string
         
     | 
| 
       69 
36 
     | 
    
         
             
                def render(*a, **b, &c)
         
     | 
| 
       70 
     | 
    
         
            -
                   
     | 
| 
      
 37 
     | 
    
         
            +
                  __compiled_proc__.(+'', *a, **b, &c)
         
     | 
| 
       71 
38 
     | 
    
         
             
                rescue Exception => e
         
     | 
| 
       72 
39 
     | 
    
         
             
                  e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
         
     | 
| 
       73 
40 
     | 
    
         
             
                end
         
     | 
| 
         @@ -76,21 +43,11 @@ module Papercraft 
     | 
|
| 
       76 
43 
     | 
    
         
             
                #
         
     | 
| 
       77 
44 
     | 
    
         
             
                # @return [String] XML string
         
     | 
| 
       78 
45 
     | 
    
         
             
                def render_xml(*a, **b, &c)
         
     | 
| 
       79 
     | 
    
         
            -
                   
     | 
| 
      
 46 
     | 
    
         
            +
                  __compiled_proc__(mode: :xml).(+'', *a, **b, &c)
         
     | 
| 
       80 
47 
     | 
    
         
             
                rescue Exception => e
         
     | 
| 
       81 
48 
     | 
    
         
             
                  e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
         
     | 
| 
       82 
49 
     | 
    
         
             
                end
         
     | 
| 
       83 
50 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
                # Renders the proc to HTML with the given arguments into the given buffer.
         
     | 
| 
       85 
     | 
    
         
            -
                #
         
     | 
| 
       86 
     | 
    
         
            -
                # @param buf [String] buffer
         
     | 
| 
       87 
     | 
    
         
            -
                # @return [String] HTML string
         
     | 
| 
       88 
     | 
    
         
            -
                def render_to_buffer(buf, *a, **b, &c)
         
     | 
| 
       89 
     | 
    
         
            -
                  compiled_proc.(buf, *a, **b, &c)
         
     | 
| 
       90 
     | 
    
         
            -
                rescue Exception => e
         
     | 
| 
       91 
     | 
    
         
            -
                  raise Papercraft.translate_backtrace(e)
         
     | 
| 
       92 
     | 
    
         
            -
                end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
51 
     | 
    
         
             
                # Returns a proc that applies the given arguments to the original proc. The
         
     | 
| 
       95 
52 
     | 
    
         
             
                # returned proc calls the *compiled* form of the proc, merging the
         
     | 
| 
       96 
53 
     | 
    
         
             
                # positional and keywords parameters passed to `#apply` with parameters
         
     | 
| 
         @@ -101,25 +58,25 @@ module Papercraft 
     | 
|
| 
       101 
58 
     | 
    
         
             
                # @param **kw1 [Hash<any, any] applied keyword parameters
         
     | 
| 
       102 
59 
     | 
    
         
             
                # @return [Proc] applied proc
         
     | 
| 
       103 
60 
     | 
    
         
             
                def apply(*pos1, **kw1, &block)
         
     | 
| 
       104 
     | 
    
         
            -
                  compiled =  
     | 
| 
       105 
     | 
    
         
            -
                  c_compiled = block&. 
     | 
| 
      
 61 
     | 
    
         
            +
                  compiled = __compiled_proc__
         
     | 
| 
      
 62 
     | 
    
         
            +
                  c_compiled = block&.__compiled_proc__
         
     | 
| 
       106 
63 
     | 
    
         | 
| 
       107 
64 
     | 
    
         
             
                  ->(__buffer__, *pos2, **kw2, &block2) {
         
     | 
| 
       108 
65 
     | 
    
         
             
                    c_proc = c_compiled && ->(__buffer__, *pos3, **kw3) {
         
     | 
| 
       109 
66 
     | 
    
         
             
                      c_compiled.(__buffer__, *pos3, **kw3, &block2)
         
     | 
| 
       110 
     | 
    
         
            -
                    }. 
     | 
| 
      
 67 
     | 
    
         
            +
                    }.__compiled__!
         
     | 
| 
       111 
68 
     | 
    
         | 
| 
       112 
69 
     | 
    
         
             
                    compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &c_proc)
         
     | 
| 
       113 
     | 
    
         
            -
                  }. 
     | 
| 
      
 70 
     | 
    
         
            +
                  }.__compiled__!
         
     | 
| 
       114 
71 
     | 
    
         
             
                end
         
     | 
| 
       115 
72 
     | 
    
         | 
| 
       116 
73 
     | 
    
         
             
                # Caches and returns the rendered HTML for the template with the given
         
     | 
| 
       117 
74 
     | 
    
         
             
                # arguments.
         
     | 
| 
       118 
75 
     | 
    
         
             
                #
         
     | 
| 
      
 76 
     | 
    
         
            +
                # @param key [any] Cache key
         
     | 
| 
       119 
77 
     | 
    
         
             
                # @return [String] HTML string
         
     | 
| 
       120 
     | 
    
         
            -
                def  
     | 
| 
      
 78 
     | 
    
         
            +
                def render_cache(key, *args, **kargs, &block)
         
     | 
| 
       121 
79 
     | 
    
         
             
                  @render_cache ||= {}
         
     | 
| 
       122 
     | 
    
         
            -
                  key = args.empty? && kargs.empty? && !block ? nil : [args, kargs, block&.source_location]
         
     | 
| 
       123 
80 
     | 
    
         
             
                  @render_cache[key] ||= render(*args, **kargs, &block)
         
     | 
| 
       124 
81 
     | 
    
         
             
                end
         
     | 
| 
       125 
82 
     | 
    
         
             
              end
         
     | 
    
        data/lib/papercraft/template.rb
    CHANGED
    
    
    
        data/lib/papercraft/version.rb
    CHANGED
    
    
    
        data/lib/papercraft.rb
    CHANGED
    
    | 
         @@ -153,4 +153,41 @@ module Papercraft 
     | 
|
| 
       153 
153 
     | 
    
         
             
              def default_kramdown_options=(opts)
         
     | 
| 
       154 
154 
     | 
    
         
             
                @default_kramdown_options = opts
         
     | 
| 
       155 
155 
     | 
    
         
             
              end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
              # Returns the compiled form code for the given proc.
         
     | 
| 
      
 158 
     | 
    
         
            +
              #
         
     | 
| 
      
 159 
     | 
    
         
            +
              # @param proc [Proc] template proc
         
     | 
| 
      
 160 
     | 
    
         
            +
              # @return [String] compiled proc code
         
     | 
| 
      
 161 
     | 
    
         
            +
              def compiled_code(proc)
         
     | 
| 
      
 162 
     | 
    
         
            +
                Papercraft::Compiler.compile_to_code(proc).last
         
     | 
| 
      
 163 
     | 
    
         
            +
              end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
              # Returns the source map for the given proc.
         
     | 
| 
      
 166 
     | 
    
         
            +
              #
         
     | 
| 
      
 167 
     | 
    
         
            +
              # @param proc [Proc] template proc
         
     | 
| 
      
 168 
     | 
    
         
            +
              # @return [Array<String>] source map
         
     | 
| 
      
 169 
     | 
    
         
            +
              def source_map(proc)
         
     | 
| 
      
 170 
     | 
    
         
            +
                loc = proc.source_location
         
     | 
| 
      
 171 
     | 
    
         
            +
                fn = proc.__compiled__? ? loc.first : Papercraft::Compiler.source_location_to_fn(loc)
         
     | 
| 
      
 172 
     | 
    
         
            +
                Papercraft::Compiler.source_map_store[fn]
         
     | 
| 
      
 173 
     | 
    
         
            +
              end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
              # Returns the AST for the given proc.
         
     | 
| 
      
 176 
     | 
    
         
            +
              #
         
     | 
| 
      
 177 
     | 
    
         
            +
              # @param proc [Proc] template proc
         
     | 
| 
      
 178 
     | 
    
         
            +
              # @return [Prism::Node] AST root
         
     | 
| 
      
 179 
     | 
    
         
            +
              def ast(proc)
         
     | 
| 
      
 180 
     | 
    
         
            +
                Sirop.to_ast(proc)
         
     | 
| 
      
 181 
     | 
    
         
            +
              end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
              # Compiles the given template.
         
     | 
| 
      
 184 
     | 
    
         
            +
              #
         
     | 
| 
      
 185 
     | 
    
         
            +
              # @param proc [Proc] template proc
         
     | 
| 
      
 186 
     | 
    
         
            +
              # @param mode [Symbol] compilation mode (:html, :xml)
         
     | 
| 
      
 187 
     | 
    
         
            +
              # @return [Proc] compiled proc
         
     | 
| 
      
 188 
     | 
    
         
            +
              def compile(proc, mode: :html)
         
     | 
| 
      
 189 
     | 
    
         
            +
                Papercraft::Compiler.compile(proc, mode:).__compiled__!
         
     | 
| 
      
 190 
     | 
    
         
            +
              rescue Sirop::Error
         
     | 
| 
      
 191 
     | 
    
         
            +
                raise Papercraft::Error, "Can't compile eval'd template"
         
     | 
| 
      
 192 
     | 
    
         
            +
              end
         
     | 
| 
       156 
193 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: papercraft
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: '2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: '2.24'
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Sharon Rosner
         
     | 
| 
         @@ -65,6 +65,20 @@ dependencies: 
     | 
|
| 
       65 
65 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       66 
66 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       67 
67 
     | 
    
         
             
                    version: 1.1.0
         
     | 
| 
      
 68 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 69 
     | 
    
         
            +
              name: erb
         
     | 
| 
      
 70 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 71 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 72 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 73 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 74 
     | 
    
         
            +
                    version: 5.1.1
         
     | 
| 
      
 75 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 76 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 77 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 78 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 79 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 80 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 81 
     | 
    
         
            +
                    version: 5.1.1
         
     | 
| 
       68 
82 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       69 
83 
     | 
    
         
             
              name: minitest
         
     | 
| 
       70 
84 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -128,8 +142,8 @@ homepage: http://github.com/digital-fabric/papercraft 
     | 
|
| 
       128 
142 
     | 
    
         
             
            licenses:
         
     | 
| 
       129 
143 
     | 
    
         
             
            - MIT
         
     | 
| 
       130 
144 
     | 
    
         
             
            metadata:
         
     | 
| 
       131 
     | 
    
         
            -
              homepage_uri: https:// 
     | 
| 
       132 
     | 
    
         
            -
               
     | 
| 
      
 145 
     | 
    
         
            +
              homepage_uri: https://papercraft.noteflakes.com/
         
     | 
| 
      
 146 
     | 
    
         
            +
              source_code_uri: https://github.com/digital-fabric/papercraft
         
     | 
| 
       133 
147 
     | 
    
         
             
              changelog_uri: https://github.com/digital-fabric/papercraft/blob/master/CHANGELOG.md
         
     | 
| 
       134 
148 
     | 
    
         
             
            rdoc_options:
         
     | 
| 
       135 
149 
     | 
    
         
             
            - "--title"
         
     |