papercraft 0.10.1 → 0.14
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 +18 -0
 - data/README.md +55 -5
 - data/lib/papercraft/component.rb +6 -14
 - data/lib/papercraft/html.rb +57 -4
 - data/lib/papercraft/renderer.rb +156 -16
 - data/lib/papercraft/version.rb +1 -1
 - data/lib/papercraft.rb +6 -0
 - metadata +3 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 8ecbcde7e33f35958acc4ca0c5c0623468563f14322675a3c7315ed92e0b289c
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5f6bdcdee15ef9ca04bc982405bd07ea3da49c4a43fbd8fd95a70494e0c43dab
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7496b82113125d5f20287cb8b2f6607acfe7c16dda2a4670152867a175c877c33865f85ad155e733509b55e36067d0c3ea60578bf55f041e95bb46e3c1fa84f9
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 91d5a7059eaf4b760412d9440f8ae08b48486c6ca6fc423a1ce32eb61a9a376385b4830b88b8a2ca1721971c48e4525a99d732d7650522fa71a55654c63aae86
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ## 0.14 2022-01-19
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            - Add support for #emit_yield in applied component (#4)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ## 0.13 2022-01-19
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - Add support for partial parameter application (#3)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## 0.12 2022-01-06
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            - Improve documentation
         
     | 
| 
      
 12 
     | 
    
         
            +
            - Add `Renderer#tag` method
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Add `HTML#style`, `HTML#script` methods
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ## 0.11 2022-01-04
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            - Add deferred evaluation
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       1 
19 
     | 
    
         
             
            ## 0.10.1 2021-12-25
         
     | 
| 
       2 
20 
     | 
    
         | 
| 
       3 
21 
     | 
    
         
             
            - Fix tag rendering with empty text in Ruby 3.0
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -20,8 +20,6 @@ 
     | 
|
| 
       20 
20 
     | 
    
         
             
              <a href="https://www.rubydoc.info/gems/papercraft">API reference</a>
         
     | 
| 
       21 
21 
     | 
    
         
             
            </p>
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
            # Papercraft - Composable HTML templating for Ruby
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
23 
     | 
    
         
             
            ## What is Papercraft?
         
     | 
| 
       26 
24 
     | 
    
         | 
| 
       27 
25 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -368,17 +366,69 @@ The default Kramdown options are: 
     | 
|
| 
       368 
366 
     | 
    
         
             
            ```
         
     | 
| 
       369 
367 
     | 
    
         | 
| 
       370 
368 
     | 
    
         
             
            The deafult options can be configured by accessing
         
     | 
| 
       371 
     | 
    
         
            -
            `Papercraft::HTML.kramdown_options 
     | 
| 
      
 369 
     | 
    
         
            +
            `Papercraft::HTML.kramdown_options`, e.g.:
         
     | 
| 
       372 
370 
     | 
    
         | 
| 
       373 
371 
     | 
    
         
             
            ```ruby
         
     | 
| 
       374 
372 
     | 
    
         
             
            Papercraft::HTML.kramdown_options[:auto_ids] = false
         
     | 
| 
       375 
373 
     | 
    
         
             
            ```
         
     | 
| 
       376 
374 
     | 
    
         | 
| 
      
 375 
     | 
    
         
            +
            ## Deferred evaluation
         
     | 
| 
      
 376 
     | 
    
         
            +
             
     | 
| 
      
 377 
     | 
    
         
            +
            Deferred evaluation allows deferring the rendering of parts of a template until
         
     | 
| 
      
 378 
     | 
    
         
            +
            the last moment, thus allowing an inner component to manipulate the state of the
         
     | 
| 
      
 379 
     | 
    
         
            +
            outer component. To in order to defer a part of a template, use `#defer`, and
         
     | 
| 
      
 380 
     | 
    
         
            +
            include any markup in the provided block. This technique, in in conjunction with
         
     | 
| 
      
 381 
     | 
    
         
            +
            holding state in instance variables, is an alternative to passing parameters,
         
     | 
| 
      
 382 
     | 
    
         
            +
            which can be limiting in some situations.
         
     | 
| 
      
 383 
     | 
    
         
            +
             
     | 
| 
      
 384 
     | 
    
         
            +
            A few use cases for deferred evaulation come to mind:
         
     | 
| 
      
 385 
     | 
    
         
            +
             
     | 
| 
      
 386 
     | 
    
         
            +
            - Setting the page title.
         
     | 
| 
      
 387 
     | 
    
         
            +
            - Adding a flash message to a page.
         
     | 
| 
      
 388 
     | 
    
         
            +
            - Using components that dynamically add static dependencies (JS and CSS) to the
         
     | 
| 
      
 389 
     | 
    
         
            +
              page.
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
      
 391 
     | 
    
         
            +
            The last use case is particularly interesting. Imagine a `DependencyMananger`
         
     | 
| 
      
 392 
     | 
    
         
            +
            class that can collect JS and CSS dependencies from the different components
         
     | 
| 
      
 393 
     | 
    
         
            +
            integrated into the page, and adds them to the page's `<head>` element:
         
     | 
| 
      
 394 
     | 
    
         
            +
             
     | 
| 
      
 395 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 396 
     | 
    
         
            +
            default_layout = H { |**args|
         
     | 
| 
      
 397 
     | 
    
         
            +
              @dependencies = DependencyMananger.new
         
     | 
| 
      
 398 
     | 
    
         
            +
              head {
         
     | 
| 
      
 399 
     | 
    
         
            +
                defer { emit @dependencies.head_markup }
         
     | 
| 
      
 400 
     | 
    
         
            +
              }
         
     | 
| 
      
 401 
     | 
    
         
            +
              body { emit_yield **args }
         
     | 
| 
      
 402 
     | 
    
         
            +
            }
         
     | 
| 
      
 403 
     | 
    
         
            +
             
     | 
| 
      
 404 
     | 
    
         
            +
            button = proc { |text, onclick|
         
     | 
| 
      
 405 
     | 
    
         
            +
              @dependencies.js '/static/js/button.js'
         
     | 
| 
      
 406 
     | 
    
         
            +
              @dependencies.css '/static/css/button.css'
         
     | 
| 
      
 407 
     | 
    
         
            +
             
     | 
| 
      
 408 
     | 
    
         
            +
              button text, onclick: onclick
         
     | 
| 
      
 409 
     | 
    
         
            +
            }
         
     | 
| 
      
 410 
     | 
    
         
            +
             
     | 
| 
      
 411 
     | 
    
         
            +
            heading = proc { |text|
         
     | 
| 
      
 412 
     | 
    
         
            +
              @dependencies.js '/static/js/heading.js'
         
     | 
| 
      
 413 
     | 
    
         
            +
              @dependencies.css '/static/css/heading.css'
         
     | 
| 
      
 414 
     | 
    
         
            +
             
     | 
| 
      
 415 
     | 
    
         
            +
              h1 text
         
     | 
| 
      
 416 
     | 
    
         
            +
            }
         
     | 
| 
      
 417 
     | 
    
         
            +
             
     | 
| 
      
 418 
     | 
    
         
            +
            page = default_layout.apply {
         
     | 
| 
      
 419 
     | 
    
         
            +
              emit heading, "What's your favorite cheese?"
         
     | 
| 
      
 420 
     | 
    
         
            +
             
     | 
| 
      
 421 
     | 
    
         
            +
              emit button, 'Beaufort', 'eat_beaufort()'
         
     | 
| 
      
 422 
     | 
    
         
            +
              emit button, 'Mont d''or', 'eat_montdor()'
         
     | 
| 
      
 423 
     | 
    
         
            +
              emit button, 'Époisses', 'eat_epoisses()'
         
     | 
| 
      
 424 
     | 
    
         
            +
            }
         
     | 
| 
      
 425 
     | 
    
         
            +
            ```
         
     | 
| 
      
 426 
     | 
    
         
            +
             
     | 
| 
       377 
427 
     | 
    
         
             
            ## Papercraft extensions
         
     | 
| 
       378 
428 
     | 
    
         | 
| 
       379 
429 
     | 
    
         
             
            Papercraft extensions are modules that contain one or more methods that can be
         
     | 
| 
       380 
430 
     | 
    
         
             
            used to render complex HTML components. Extension modules can be used by
         
     | 
| 
       381 
     | 
    
         
            -
            installing them as a namespaced extension using `Papercraft 
     | 
| 
      
 431 
     | 
    
         
            +
            installing them as a namespaced extension using `Papercraft::extension`.
         
     | 
| 
       382 
432 
     | 
    
         
             
            Extensions are particularly useful when you work with CSS frameworks such as
         
     | 
| 
       383 
433 
     | 
    
         
             
            [Bootstrap](https://getbootstrap.com/), [Tailwind](https://tailwindui.com/) or
         
     | 
| 
       384 
434 
     | 
    
         
             
            [Primer](https://primer.style/).
         
     | 
| 
         @@ -424,7 +474,7 @@ end 
     | 
|
| 
       424 
474 
     | 
    
         
             
            Papercraft.extension(bootstrap: BootstrapComponents)
         
     | 
| 
       425 
475 
     | 
    
         
             
            ```
         
     | 
| 
       426 
476 
     | 
    
         | 
| 
       427 
     | 
    
         
            -
            The call to `Papercraft 
     | 
| 
      
 477 
     | 
    
         
            +
            The call to `Papercraft::extension` lets us access the different methods of
         
     | 
| 
       428 
478 
     | 
    
         
             
            `BootstrapComponents` by calling `#bootstrap` inside a template. With this,
         
     | 
| 
       429 
479 
     | 
    
         
             
            we'll be able to express the above markup as follows:
         
     | 
| 
       430 
480 
     | 
    
         | 
    
        data/lib/papercraft/component.rb
    CHANGED
    
    | 
         @@ -106,11 +106,8 @@ module Papercraft 
     | 
|
| 
       106 
106 
     | 
    
         
             
                  template = self
         
     | 
| 
       107 
107 
     | 
    
         
             
                  Renderer.verify_proc_parameters(template, a, b)
         
     | 
| 
       108 
108 
     | 
    
         
             
                  renderer_class.new do
         
     | 
| 
       109 
     | 
    
         
            -
                    if block
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                    else
         
     | 
| 
       112 
     | 
    
         
            -
                      instance_exec(*a, **b, &template)
         
     | 
| 
       113 
     | 
    
         
            -
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                    push_emit_yield_block(block) if block
         
     | 
| 
      
 110 
     | 
    
         
            +
                    instance_exec(*a, **b, &template)
         
     | 
| 
       114 
111 
     | 
    
         
             
                  end.to_s
         
     | 
| 
       115 
112 
     | 
    
         
             
                rescue ArgumentError => e
         
     | 
| 
       116 
113 
     | 
    
         
             
                  raise Papercraft::Error, e.message
         
     | 
| 
         @@ -136,15 +133,10 @@ module Papercraft 
     | 
|
| 
       136 
133 
     | 
    
         
             
                # @return [Papercraft::Component] applied component
         
     | 
| 
       137 
134 
     | 
    
         
             
                def apply(*a, **b, &block)
         
     | 
| 
       138 
135 
     | 
    
         
             
                  template = self
         
     | 
| 
       139 
     | 
    
         
            -
                   
     | 
| 
       140 
     | 
    
         
            -
                     
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
                  else
         
     | 
| 
       144 
     | 
    
         
            -
                    Component.new(&proc do |*x, **y|
         
     | 
| 
       145 
     | 
    
         
            -
                      instance_exec(*a, **b, &template)
         
     | 
| 
       146 
     | 
    
         
            -
                    end)
         
     | 
| 
       147 
     | 
    
         
            -
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
                  Component.new(&proc do |*x, **y|
         
     | 
| 
      
 137 
     | 
    
         
            +
                    push_emit_yield_block(block) if block
         
     | 
| 
      
 138 
     | 
    
         
            +
                    instance_exec(*a, *x, **b, **y, &template)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end)
         
     | 
| 
       148 
140 
     | 
    
         
             
                end
         
     | 
| 
       149 
141 
     | 
    
         | 
| 
       150 
142 
     | 
    
         
             
                # Returns the Renderer class used for rendering the templates, according to
         
     | 
    
        data/lib/papercraft/html.rb
    CHANGED
    
    | 
         @@ -44,15 +44,53 @@ module Papercraft 
     | 
|
| 
       44 
44 
     | 
    
         
             
                  link(**attributes)
         
     | 
| 
       45 
45 
     | 
    
         
             
                end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                 
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
                # Emits an inline CSS style element.
         
     | 
| 
      
 48 
     | 
    
         
            +
                #
         
     | 
| 
      
 49 
     | 
    
         
            +
                # @param css [String] CSS code
         
     | 
| 
      
 50 
     | 
    
         
            +
                # @param **props [Hash] optional element attributes
         
     | 
| 
      
 51 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 52 
     | 
    
         
            +
                def style(css, **props, &block)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @buffer << '<style'
         
     | 
| 
      
 54 
     | 
    
         
            +
                  emit_props(props) unless props.empty?
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  @buffer << '>' << css << '</style>'
         
     | 
| 
       49 
57 
     | 
    
         
             
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
              
         
     | 
| 
      
 59 
     | 
    
         
            +
                # Emits an inline JS script element.
         
     | 
| 
      
 60 
     | 
    
         
            +
                #
         
     | 
| 
      
 61 
     | 
    
         
            +
                # @param js [String, nil] Javascript code
         
     | 
| 
      
 62 
     | 
    
         
            +
                # @param **props [Hash] optional element attributes
         
     | 
| 
      
 63 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 64 
     | 
    
         
            +
                def script(js = nil, **props, &block)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @buffer << '<script'
         
     | 
| 
      
 66 
     | 
    
         
            +
                  emit_props(props) unless props.empty?
         
     | 
| 
       50 
67 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 68 
     | 
    
         
            +
                  if js
         
     | 
| 
      
 69 
     | 
    
         
            +
                    @buffer << '>' << js << '</script>'
         
     | 
| 
      
 70 
     | 
    
         
            +
                  else
         
     | 
| 
      
 71 
     | 
    
         
            +
                    @buffer << '></script>'
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              
         
     | 
| 
      
 75 
     | 
    
         
            +
                # Converts and emits the given markdown. Papercraft uses
         
     | 
| 
      
 76 
     | 
    
         
            +
                # [Kramdown](https://github.com/gettalong/kramdown/) to do the Markdown to
         
     | 
| 
      
 77 
     | 
    
         
            +
                # HTML conversion. Optional Kramdown settings can be provided in order to
         
     | 
| 
      
 78 
     | 
    
         
            +
                # control the conversion. Those are merged with the default Kramdown
         
     | 
| 
      
 79 
     | 
    
         
            +
                # settings, which can be controlled using
         
     | 
| 
      
 80 
     | 
    
         
            +
                # `Papercraft::HTML.kramdown_options`.
         
     | 
| 
      
 81 
     | 
    
         
            +
                #
         
     | 
| 
      
 82 
     | 
    
         
            +
                # @param markdown [String] Markdown content
         
     | 
| 
      
 83 
     | 
    
         
            +
                # @param **opts [Hash] Kramdown options
         
     | 
| 
      
 84 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 85 
     | 
    
         
            +
                def emit_markdown(markdown, **opts)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  emit Kramdown::Document.new(markdown, **kramdown_options(opts)).to_html
         
     | 
| 
       53 
87 
     | 
    
         
             
                end
         
     | 
| 
       54 
88 
     | 
    
         | 
| 
       55 
89 
     | 
    
         
             
                class << self
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # Returns the default Kramdown options used for converting Markdown to
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # HTML.
         
     | 
| 
      
 92 
     | 
    
         
            +
                  #
         
     | 
| 
      
 93 
     | 
    
         
            +
                  # @return [Hash] Default Kramdown options
         
     | 
| 
       56 
94 
     | 
    
         
             
                  def kramdown_options
         
     | 
| 
       57 
95 
     | 
    
         
             
                    @kramdown_options ||= {
         
     | 
| 
       58 
96 
     | 
    
         
             
                      entity_output: :numeric,
         
     | 
| 
         @@ -62,9 +100,24 @@ module Papercraft 
     | 
|
| 
       62 
100 
     | 
    
         
             
                    }
         
     | 
| 
       63 
101 
     | 
    
         
             
                  end
         
     | 
| 
       64 
102 
     | 
    
         | 
| 
      
 103 
     | 
    
         
            +
                  # Sets the default Kramdown options used for converting Markdown to
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # HTML.
         
     | 
| 
      
 105 
     | 
    
         
            +
                  #
         
     | 
| 
      
 106 
     | 
    
         
            +
                  # @param opts [Hash] New deafult Kramdown options
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # @return [Hash] New default Kramdown options
         
     | 
| 
       65 
108 
     | 
    
         
             
                  def kramdown_options=(opts)
         
     | 
| 
       66 
109 
     | 
    
         
             
                    @kramdown_options = opts
         
     | 
| 
       67 
110 
     | 
    
         
             
                  end
         
     | 
| 
       68 
111 
     | 
    
         
             
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                private
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                # Returns the default Kramdown options, merged with the given overrides.
         
     | 
| 
      
 116 
     | 
    
         
            +
                # 
         
     | 
| 
      
 117 
     | 
    
         
            +
                # @param opts [Hash] Kramdown option overrides
         
     | 
| 
      
 118 
     | 
    
         
            +
                # @return [Hash] Merged Kramdown options
         
     | 
| 
      
 119 
     | 
    
         
            +
                def kramdown_options(opts)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  HTML.kramdown_options.merge(**opts)
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
       69 
122 
     | 
    
         
             
              end
         
     | 
| 
       70 
123 
     | 
    
         
             
            end
         
     | 
    
        data/lib/papercraft/renderer.rb
    CHANGED
    
    | 
         @@ -34,8 +34,29 @@ module Papercraft 
     | 
|
| 
       34 
34 
     | 
    
         
             
                    end
         
     | 
| 
       35 
35 
     | 
    
         
             
                  end
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                  #  
     | 
| 
       38 
     | 
    
         
            -
                  #  
     | 
| 
      
 37 
     | 
    
         
            +
                  # call_seq:
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #   Papercraft::Renderer.extension(name => mod, ...)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #   Papercraft.extension(name => mod, ...)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # Installs the given extensions, passed in the form of a Ruby hash mapping
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # methods to extension modules. The methods will be available to all
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # Papercraft components. Extension methods are executed in the context of
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # the the renderer instance, so they can look just like normal proc
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # components. In cases where method names in the module clash with HTML
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # tag names, you can use the `#tag` method to emit the relevant tag.
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # 
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # module ComponentLibrary
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #   def card(title, content)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  #     div(class: 'card') {
         
     | 
| 
      
 51 
     | 
    
         
            +
                  #       h3 title
         
     | 
| 
      
 52 
     | 
    
         
            +
                  #       div(class: 'card-content') { emit_markdown content }
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #     }
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # Papercraft.extension(components: ComponentLibrary)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # H { components.card('Foo', '**Bar**') }
         
     | 
| 
      
 59 
     | 
    
         
            +
                  #
         
     | 
| 
       39 
60 
     | 
    
         
             
                  # @param map [Hash] hash mapping methods to extension modules
         
     | 
| 
       40 
61 
     | 
    
         
             
                  # @return [void]
         
     | 
| 
       41 
62 
     | 
    
         
             
                  def extension(map)
         
     | 
| 
         @@ -57,12 +78,14 @@ module Papercraft 
     | 
|
| 
       57 
78 
     | 
    
         
             
                  end
         
     | 
| 
       58 
79 
     | 
    
         
             
                end
         
     | 
| 
       59 
80 
     | 
    
         | 
| 
      
 81 
     | 
    
         
            +
                INITIAL_BUFFER_CAPACITY = 8192
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
       60 
83 
     | 
    
         
             
                # Initializes the renderer and evaulates the given template in the
         
     | 
| 
       61 
84 
     | 
    
         
             
                # renderer's scope.
         
     | 
| 
       62 
85 
     | 
    
         
             
                #
         
     | 
| 
       63 
86 
     | 
    
         
             
                # @param &template [Proc] template block
         
     | 
| 
       64 
87 
     | 
    
         
             
                def initialize(&template)
         
     | 
| 
       65 
     | 
    
         
            -
                  @buffer =  
     | 
| 
      
 88 
     | 
    
         
            +
                  @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
         
     | 
| 
       66 
89 
     | 
    
         
             
                  instance_eval(&template)
         
     | 
| 
       67 
90 
     | 
    
         
             
                end
         
     | 
| 
       68 
91 
     | 
    
         | 
| 
         @@ -70,13 +93,29 @@ module Papercraft 
     | 
|
| 
       70 
93 
     | 
    
         
             
                #
         
     | 
| 
       71 
94 
     | 
    
         
             
                # @return [String]
         
     | 
| 
       72 
95 
     | 
    
         
             
                def to_s
         
     | 
| 
      
 96 
     | 
    
         
            +
                  if @parts
         
     | 
| 
      
 97 
     | 
    
         
            +
                    last = @buffer
         
     | 
| 
      
 98 
     | 
    
         
            +
                    @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    parts = @parts
         
     | 
| 
      
 100 
     | 
    
         
            +
                    @parts = nil
         
     | 
| 
      
 101 
     | 
    
         
            +
                    parts.each do |p|
         
     | 
| 
      
 102 
     | 
    
         
            +
                      if Proc === p
         
     | 
| 
      
 103 
     | 
    
         
            +
                        render_deferred_proc(&p)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      else
         
     | 
| 
      
 105 
     | 
    
         
            +
                        @buffer << p
         
     | 
| 
      
 106 
     | 
    
         
            +
                      end
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    @buffer << last unless last.empty?
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
       73 
110 
     | 
    
         
             
                  @buffer
         
     | 
| 
       74 
111 
     | 
    
         
             
                end
         
     | 
| 
       75 
112 
     | 
    
         | 
| 
      
 113 
     | 
    
         
            +
                # The tag method template below is optimized for performance. Do not touch!
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
       76 
115 
     | 
    
         
             
                S_TAG_METHOD_LINE = __LINE__ + 1
         
     | 
| 
       77 
116 
     | 
    
         
             
                S_TAG_METHOD = <<~EOF
         
     | 
| 
       78 
     | 
    
         
            -
                  S_TAG_%<TAG>s_PRE =  
     | 
| 
       79 
     | 
    
         
            -
                  S_TAG_%<TAG>s_CLOSE =  
     | 
| 
      
 117 
     | 
    
         
            +
                  S_TAG_%<TAG>s_PRE = %<tag_pre>s
         
     | 
| 
      
 118 
     | 
    
         
            +
                  S_TAG_%<TAG>s_CLOSE = %<tag_close>s
         
     | 
| 
       80 
119 
     | 
    
         | 
| 
       81 
120 
     | 
    
         
             
                  def %<tag>s(text = nil, **props, &block)
         
     | 
| 
       82 
121 
     | 
    
         
             
                    if text.is_a?(Hash) && props.empty?
         
     | 
| 
         @@ -103,6 +142,48 @@ module Papercraft 
     | 
|
| 
       103 
142 
     | 
    
         
             
                  end
         
     | 
| 
       104 
143 
     | 
    
         
             
                EOF
         
     | 
| 
       105 
144 
     | 
    
         | 
| 
      
 145 
     | 
    
         
            +
                # Emits an HTML tag with the given content, properties and optional block.
         
     | 
| 
      
 146 
     | 
    
         
            +
                # This method is an alternative to emitting HTML tags using dynamically
         
     | 
| 
      
 147 
     | 
    
         
            +
                # created methods. This is particularly useful when using extensions that
         
     | 
| 
      
 148 
     | 
    
         
            +
                # have method names that clash with HTML tags, such as `button` or `a`, or
         
     | 
| 
      
 149 
     | 
    
         
            +
                # when you need to override the behaviour of a particular HTML tag.
         
     | 
| 
      
 150 
     | 
    
         
            +
                #
         
     | 
| 
      
 151 
     | 
    
         
            +
                # The following two method calls have the same effect:
         
     | 
| 
      
 152 
     | 
    
         
            +
                #
         
     | 
| 
      
 153 
     | 
    
         
            +
                # button 'text', id: 'button1'
         
     | 
| 
      
 154 
     | 
    
         
            +
                # tag :button, 'text', id: 'button1'
         
     | 
| 
      
 155 
     | 
    
         
            +
                #
         
     | 
| 
      
 156 
     | 
    
         
            +
                # @param sym [Symbol, String] HTML tag
         
     | 
| 
      
 157 
     | 
    
         
            +
                # @param text [String, nil] tag content
         
     | 
| 
      
 158 
     | 
    
         
            +
                # @param **props [Hash] tag attributes
         
     | 
| 
      
 159 
     | 
    
         
            +
                # @param &block [Proc] optional inner HTML
         
     | 
| 
      
 160 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 161 
     | 
    
         
            +
                def tag(sym, text = nil, **props, &block)
         
     | 
| 
      
 162 
     | 
    
         
            +
                  if text.is_a?(Hash) && props.empty?
         
     | 
| 
      
 163 
     | 
    
         
            +
                    props = text
         
     | 
| 
      
 164 
     | 
    
         
            +
                    text = nil
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  tag = sym.to_s.tr('_', '-')
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                  @buffer << S_LT << tag
         
     | 
| 
      
 170 
     | 
    
         
            +
                  emit_props(props) unless props.empty?
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                  if block
         
     | 
| 
      
 173 
     | 
    
         
            +
                    @buffer << S_GT
         
     | 
| 
      
 174 
     | 
    
         
            +
                    instance_eval(&block)
         
     | 
| 
      
 175 
     | 
    
         
            +
                    @buffer << S_LT_SLASH << tag << S_GT
         
     | 
| 
      
 176 
     | 
    
         
            +
                  elsif Proc === text
         
     | 
| 
      
 177 
     | 
    
         
            +
                    @buffer << S_GT
         
     | 
| 
      
 178 
     | 
    
         
            +
                    emit(text)
         
     | 
| 
      
 179 
     | 
    
         
            +
                    @buffer << S_LT_SLASH << tag << S_GT
         
     | 
| 
      
 180 
     | 
    
         
            +
                  elsif text
         
     | 
| 
      
 181 
     | 
    
         
            +
                    @buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
         
     | 
| 
      
 182 
     | 
    
         
            +
                  else
         
     | 
| 
      
 183 
     | 
    
         
            +
                    @buffer << S_SLASH_GT
         
     | 
| 
      
 184 
     | 
    
         
            +
                  end
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
       106 
187 
     | 
    
         
             
                # Catches undefined tag method call and handles it by defining the method.
         
     | 
| 
       107 
188 
     | 
    
         
             
                #
         
     | 
| 
       108 
189 
     | 
    
         
             
                # @param sym [Symbol] HTML tag or component identifier
         
     | 
| 
         @@ -112,7 +193,12 @@ module Papercraft 
     | 
|
| 
       112 
193 
     | 
    
         
             
                # @return [void]
         
     | 
| 
       113 
194 
     | 
    
         
             
                def method_missing(sym, *args, **opts, &block)
         
     | 
| 
       114 
195 
     | 
    
         
             
                  tag = sym.to_s
         
     | 
| 
       115 
     | 
    
         
            -
                  code = S_TAG_METHOD % { 
     | 
| 
      
 196 
     | 
    
         
            +
                  code = S_TAG_METHOD % {
         
     | 
| 
      
 197 
     | 
    
         
            +
                    tag: tag,
         
     | 
| 
      
 198 
     | 
    
         
            +
                    TAG: tag.upcase,
         
     | 
| 
      
 199 
     | 
    
         
            +
                    tag_pre: "<#{tag.tr('_', '-')}".inspect,
         
     | 
| 
      
 200 
     | 
    
         
            +
                    tag_close: "</#{tag.tr('_', '-')}>".inspect
         
     | 
| 
      
 201 
     | 
    
         
            +
                  }
         
     | 
| 
       116 
202 
     | 
    
         
             
                  self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
         
     | 
| 
       117 
203 
     | 
    
         
             
                  send(sym, *args, **opts, &block)
         
     | 
| 
       118 
204 
     | 
    
         
             
                end
         
     | 
| 
         @@ -156,9 +242,46 @@ module Papercraft 
     | 
|
| 
       156 
242 
     | 
    
         
             
                # @param **b [Hash] named arguments to pass to a proc
         
     | 
| 
       157 
243 
     | 
    
         
             
                # @return [void]
         
     | 
| 
       158 
244 
     | 
    
         
             
                def emit_yield(*a, **b)
         
     | 
| 
       159 
     | 
    
         
            -
                   
     | 
| 
      
 245 
     | 
    
         
            +
                  block = @emit_yield_stack&.pop
         
     | 
| 
      
 246 
     | 
    
         
            +
                  raise Papercraft::Error, "No block given" unless block
         
     | 
| 
       160 
247 
     | 
    
         | 
| 
       161 
     | 
    
         
            -
                  instance_exec(*a, **b,  
     | 
| 
      
 248 
     | 
    
         
            +
                  instance_exec(*a, **b, &block)
         
     | 
| 
      
 249 
     | 
    
         
            +
                end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                # Defers the given block to be evaluated later. Deferred evaluation allows
         
     | 
| 
      
 252 
     | 
    
         
            +
                # Papercraft components to inject state into sibling components, regardless
         
     | 
| 
      
 253 
     | 
    
         
            +
                # of the component's order in the container component. For example, a nested
         
     | 
| 
      
 254 
     | 
    
         
            +
                # component may set an instance variable used by another component. This is
         
     | 
| 
      
 255 
     | 
    
         
            +
                # an elegant solution to the problem of setting the HTML page's title, or
         
     | 
| 
      
 256 
     | 
    
         
            +
                # adding elements to the `<head>` section. Here's how a title can be
         
     | 
| 
      
 257 
     | 
    
         
            +
                # controlled from a nested component:
         
     | 
| 
      
 258 
     | 
    
         
            +
                #
         
     | 
| 
      
 259 
     | 
    
         
            +
                #   layout = H {
         
     | 
| 
      
 260 
     | 
    
         
            +
                #     html {
         
     | 
| 
      
 261 
     | 
    
         
            +
                #       head {
         
     | 
| 
      
 262 
     | 
    
         
            +
                #         defer { title @title }
         
     | 
| 
      
 263 
     | 
    
         
            +
                #       }
         
     | 
| 
      
 264 
     | 
    
         
            +
                #       body {
         
     | 
| 
      
 265 
     | 
    
         
            +
                #         emit_yield
         
     | 
| 
      
 266 
     | 
    
         
            +
                #       }
         
     | 
| 
      
 267 
     | 
    
         
            +
                #     }
         
     | 
| 
      
 268 
     | 
    
         
            +
                #   }
         
     | 
| 
      
 269 
     | 
    
         
            +
                #
         
     | 
| 
      
 270 
     | 
    
         
            +
                #   html.render {
         
     | 
| 
      
 271 
     | 
    
         
            +
                #     @title = 'My super page'
         
     | 
| 
      
 272 
     | 
    
         
            +
                #     h1 'content'
         
     | 
| 
      
 273 
     | 
    
         
            +
                #   }
         
     | 
| 
      
 274 
     | 
    
         
            +
                #
         
     | 
| 
      
 275 
     | 
    
         
            +
                # @param &block [Proc] Deferred block to be emitted
         
     | 
| 
      
 276 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 277 
     | 
    
         
            +
                def defer(&block)
         
     | 
| 
      
 278 
     | 
    
         
            +
                  if !@parts
         
     | 
| 
      
 279 
     | 
    
         
            +
                    @parts = [@buffer, block]
         
     | 
| 
      
 280 
     | 
    
         
            +
                  else
         
     | 
| 
      
 281 
     | 
    
         
            +
                    @parts << @buffer unless @buffer.empty?
         
     | 
| 
      
 282 
     | 
    
         
            +
                    @parts << block
         
     | 
| 
      
 283 
     | 
    
         
            +
                  end
         
     | 
| 
      
 284 
     | 
    
         
            +
                  @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
         
     | 
| 
       162 
285 
     | 
    
         
             
                end
         
     | 
| 
       163 
286 
     | 
    
         | 
| 
       164 
287 
     | 
    
         
             
                S_LT              = '<'
         
     | 
| 
         @@ -182,19 +305,19 @@ module Papercraft 
     | 
|
| 
       182 
305 
     | 
    
         
             
                private
         
     | 
| 
       183 
306 
     | 
    
         | 
| 
       184 
307 
     | 
    
         
             
                # Escapes text. This method must be overriden in descendant classes.
         
     | 
| 
      
 308 
     | 
    
         
            +
                #
         
     | 
| 
      
 309 
     | 
    
         
            +
                # @param text [String] text to be escaped
         
     | 
| 
       185 
310 
     | 
    
         
             
                def escape_text(text)
         
     | 
| 
       186 
311 
     | 
    
         
             
                  raise NotImplementedError
         
     | 
| 
       187 
312 
     | 
    
         
             
                end
         
     | 
| 
       188 
313 
     | 
    
         | 
| 
       189 
     | 
    
         
            -
                #  
     | 
| 
       190 
     | 
    
         
            -
                 
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
     | 
    
         
            -
                   
     | 
| 
       194 
     | 
    
         
            -
                ensure
         
     | 
| 
       195 
     | 
    
         
            -
                  @inner_block = old_block
         
     | 
| 
      
 314 
     | 
    
         
            +
                # Pushes the given block onto the emit_yield stack.
         
     | 
| 
      
 315 
     | 
    
         
            +
                #
         
     | 
| 
      
 316 
     | 
    
         
            +
                # @param block [Proc] block
         
     | 
| 
      
 317 
     | 
    
         
            +
                def push_emit_yield_block(block)
         
     | 
| 
      
 318 
     | 
    
         
            +
                  (@emit_yield_stack ||= []) << block
         
     | 
| 
       196 
319 
     | 
    
         
             
                end
         
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
      
 320 
     | 
    
         
            +
             
     | 
| 
       198 
321 
     | 
    
         
             
                # Emits tag attributes into the rendering buffer
         
     | 
| 
       199 
322 
     | 
    
         
             
                # @param props [Hash] tag attributes
         
     | 
| 
       200 
323 
     | 
    
         
             
                # @return [void]
         
     | 
| 
         @@ -217,6 +340,23 @@ module Papercraft 
     | 
|
| 
       217 
340 
     | 
    
         
             
                    end
         
     | 
| 
       218 
341 
     | 
    
         
             
                  }
         
     | 
| 
       219 
342 
     | 
    
         
             
                end
         
     | 
| 
      
 343 
     | 
    
         
            +
             
     | 
| 
      
 344 
     | 
    
         
            +
                # Renders a deferred proc by evaluating it, then adding the rendered result
         
     | 
| 
      
 345 
     | 
    
         
            +
                # to the buffer.
         
     | 
| 
      
 346 
     | 
    
         
            +
                #
         
     | 
| 
      
 347 
     | 
    
         
            +
                # @param &block [Proc] deferred proc
         
     | 
| 
      
 348 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 349 
     | 
    
         
            +
                def render_deferred_proc(&block)
         
     | 
| 
      
 350 
     | 
    
         
            +
                  old_buffer = @buffer
         
     | 
| 
      
 351 
     | 
    
         
            +
                  
         
     | 
| 
      
 352 
     | 
    
         
            +
                  @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
         
     | 
| 
      
 353 
     | 
    
         
            +
                  @parts = nil
         
     | 
| 
      
 354 
     | 
    
         
            +
             
     | 
| 
      
 355 
     | 
    
         
            +
                  instance_eval(&block)
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
                  old_buffer << to_s
         
     | 
| 
      
 358 
     | 
    
         
            +
                  @buffer = old_buffer
         
     | 
| 
      
 359 
     | 
    
         
            +
                end
         
     | 
| 
       220 
360 
     | 
    
         
             
              end
         
     | 
| 
       221 
361 
     | 
    
         | 
| 
       222 
362 
     | 
    
         
             
              # Implements an HTML renderer
         
     | 
    
        data/lib/papercraft/version.rb
    CHANGED
    
    
    
        data/lib/papercraft.rb
    CHANGED
    
    | 
         @@ -16,6 +16,12 @@ module Papercraft 
     | 
|
| 
       16 
16 
     | 
    
         
             
              # by adding namespaced methods to emplates. An extension is implemented as a
         
     | 
| 
       17 
17 
     | 
    
         
             
              # Ruby module containing one or more methods. Each method in the extension
         
     | 
| 
       18 
18 
     | 
    
         
             
              # module can be used to render a specific HTML element or a set of elements.
         
     | 
| 
      
 19 
     | 
    
         
            +
              #
         
     | 
| 
      
 20 
     | 
    
         
            +
              # This is a convenience method. For more information on using Papercraft
         
     | 
| 
      
 21 
     | 
    
         
            +
              # extensions, see `Papercraft::Renderer::extension`
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              # @param map [Hash] hash mapping methods to extension modules
         
     | 
| 
      
 24 
     | 
    
         
            +
              # @return [void]
         
     | 
| 
       19 
25 
     | 
    
         
             
              def self.extension(map)
         
     | 
| 
       20 
26 
     | 
    
         
             
                Renderer.extension(map)
         
     | 
| 
       21 
27 
     | 
    
         
             
              end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: papercraft
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: '0.14'
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Sharon Rosner
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire:
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-01-19 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: escape_utils
         
     | 
| 
         @@ -166,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       166 
166 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       167 
167 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       168 
168 
     | 
    
         
             
            requirements: []
         
     | 
| 
       169 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 169 
     | 
    
         
            +
            rubygems_version: 3.3.3
         
     | 
| 
       170 
170 
     | 
    
         
             
            signing_key:
         
     | 
| 
       171 
171 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       172 
172 
     | 
    
         
             
            summary: 'Papercraft: component-based HTML templating for Ruby'
         
     |