{"id":13878743,"url":"https://github.com/digital-fabric/papercraft","last_synced_at":"2025-10-09T02:45:08.417Z","repository":{"id":43861057,"uuid":"164271418","full_name":"digital-fabric/papercraft","owner":"digital-fabric","description":"Composable templating for Ruby","archived":false,"fork":false,"pushed_at":"2025-10-08T12:56:16.000Z","size":360,"stargazers_count":228,"open_issues_count":0,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-08T14:38:18.758Z","etag":null,"topics":["component-based","html","ruby","template-engine"],"latest_commit_sha":null,"homepage":"https://papercraft.noteflakes.com/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/digital-fabric.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"noteflakes"}},"created_at":"2019-01-06T03:19:10.000Z","updated_at":"2025-10-08T12:56:19.000Z","dependencies_parsed_at":"2024-05-08T07:29:05.148Z","dependency_job_id":"d7c67596-38a9-4e10-b8ff-57b09ab24f2a","html_url":"https://github.com/digital-fabric/papercraft","commit_stats":{"total_commits":165,"total_committers":6,"mean_commits":27.5,"dds":"0.18787878787878787","last_synced_commit":"d7e15c415972cb70432bc1308252c28728d0a582"},"previous_names":["digital-fabric/rubyoshka"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/digital-fabric/papercraft","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digital-fabric%2Fpapercraft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digital-fabric%2Fpapercraft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digital-fabric%2Fpapercraft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digital-fabric%2Fpapercraft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/digital-fabric","download_url":"https://codeload.github.com/digital-fabric/papercraft/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digital-fabric%2Fpapercraft/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000770,"owners_count":26082906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["component-based","html","ruby","template-engine"],"created_at":"2024-08-06T08:01:58.525Z","updated_at":"2025-10-09T02:45:08.411Z","avatar_url":"https://github.com/digital-fabric.png","language":"Ruby","funding_links":["https://github.com/sponsors/noteflakes"],"categories":["Ruby"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cimg src=\"papercraft.png\"\u003e\n  \u003cbr\u003e\n  Papercraft\n\u003c/h1\u003e\n\n\u003ch4 align=\"center\"\u003eFunctional HTML templating for Ruby\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"http://rubygems.org/gems/papercraft\"\u003e\n    \u003cimg src=\"https://badge.fury.io/rb/papercraft.svg\" alt=\"Ruby gem\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/digital-fabric/papercraft/actions/workflows/test.yml\"\u003e\n    \u003cimg src=\"https://github.com/digital-fabric/papercraft/actions/workflows/test.yml/badge.svg\" alt=\"Tests\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/digital-fabric/papercraft/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"MIT License\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.rubydoc.info/gems/papercraft\"\u003eAPI reference\u003c/a\u003e\n\u003c/p\u003e\n\n## What is Papercraft?\n\n```ruby\nrequire 'papercraft'\n\npage = -\u003e(**props) {\n  html {\n    head { title 'My Title' }\n    body { render_children **props }\n  }\n}\npage.render {\n  p 'foo'\n}\n#=\u003e \"\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eTitle\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e\u003cp\u003efoo\u003c/p\u003e\u003c/body\u003e\u003c/html\u003e\"\n```\n\nPapercraft is a templating engine for dynamically producing HTML in Ruby apps. Papercraft\ntemplates are expressed as Ruby procs, leading to easier debugging, better\nprotection against HTML injection attacks, and better code reuse.\n\nPapercraft templates can be composed in a variety of ways, facilitating the usage of\nlayout templates, and enabling a component-oriented approach to building web\ninterfaces of arbitrary complexity.\n\nIn Papercraft, dynamic data is passed explicitly to the template as block/lambda\narguments, making the data flow easy to follow and understand. Papercraft also lets\ndevelopers create derivative templates using full or partial parameter\napplication.\n\n```ruby\nrequire 'papercraft'\n\npage = -\u003e(**props) {\n  html {\n    head { title 'My Title' }\n    body { render_children **props }\n  }\n}\npage.render {\n  p(class: 'big') 'foo'\n}\n#=\u003e \"\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eTitle\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e\u003cp class=\"big\"\u003efoo\u003c/p\u003e\u003c/body\u003e\u003c/html\u003e\"\n\nhello_page = page.apply -\u003e(name:, **) {\n  h1 \"Hello, #{name}!\"\n}\nhello.render(name: 'world')\n#=\u003e \"\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eTitle\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e\u003ch1\u003eHello, world!\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\"\n```\n\nPapercraft features:\n\n- Express HTML using plain Ruby procs.\n- Automatic compilation for super-fast execution (about as\n  [fast](https://github.com/digital-fabric/papercraft/blob/master/examples/perf.rb) as\n  compiled ERB/ERubi).\n- Deferred rendering using `defer`.\n- Simple and easy template composition (for uses such as layouts, or modular\n  templates).\n- Markdown rendering using [Kramdown](https://github.com/gettalong/kramdown/).\n- Rudimentary support for generating XML.\n- Support for extensions.\n- Simple caching API for caching the rendering result.\n\n## Table of Content\n\n- [Getting Started](#getting-started)\n- [Basic Markup](#basic-markup)\n- [Builtin Methods](#builtin-methods)\n- [Template Parameters](#template-parameters)\n- [Template Logic](#template-logic)\n- [Template Blocks](#template-blocks)\n- [Template Composition](#template-composition)\n- [Parameter and Block Application](#parameter-and-block-application)\n- [Higher-Order Templates](#higher-order-templates)\n- [Layout Template Composition](#layout-template-composition)\n- [Rendering Markdown](#rendering-markdown)\n- [Deferred Evaluation](#deferred-evaluation)\n- [Cached Rendering](#cached-rendering)\n\nA typical example for a dashboard-type app markup can be found here:\nhttps://github.com/digital-fabric/papercraft/blob/master/examples/dashboard.rb\n\n## Getting Started\n\nIn Papercraft, an HTML template is expressed as a proc:\n\n```ruby\nhtml = -\u003e {\n  div(id: 'greeter') { p 'Hello!' }\n}\n```\n\nRendering a template is done using `Proc#render`:\n\n```ruby\nrequire 'papercraft'\n\nhtml.render #=\u003e \"\u003cdiv id=\"greeter\"\u003e\u003cp\u003eHello!\u003c/p\u003e\u003c/div\u003e\"\n```\n\n## Basic Markup\n\nTags are added using unqualified method calls, and can be nested using blocks:\n\n```ruby\n-\u003e {\n  html {\n    head {\n      title 'page title'\n    }\n    body {\n      article {\n        h1 'article title'\n      }\n    }\n  }\n}\n```\n\nTag methods accept a string argument, a block, or no argument at all:\n\n```ruby\n-\u003e { p 'hello' }.render #=\u003e \"\u003cp\u003ehello\u003c/p\u003e\"\n\n-\u003e { p { span '1'; span '2' } }.render #=\u003e \"\u003cp\u003e\u003cspan\u003e1\u003c/span\u003e\u003cspan\u003e2\u003c/span\u003e\u003c/p\u003e\"\n\n-\u003e { hr() }.render #=\u003e \"\u003chr/\u003e\"\n```\n\nTag methods also accept tag attributes, given as a hash:\n\n```ruby\n-\u003e { img src: '/my.gif' }.render #=\u003e \"\u003cimg src=\\\"/my.gif\\\"/\u003e\"\n\n-\u003e { p \"foobar\", class: 'important' }.render #=\u003e \"\u003cp class=\\\"important\\\"\u003efoobar\u003c/p\u003e\"\n```\n\nA `true` attribute value will emit a valueless attribute. A `nil` or `false`\nattribute value will emit nothing:\n\n```ruby\n-\u003e { button disabled: nil }.render #=\u003e \"\u003cbutton\u003e\u003c/button\u003e\"\n-\u003e { button disabled: true }.render #=\u003e \"\u003cbutton disabled\u003e\u003c/button\u003e\"\n```\n\nAn attribute value given as an array will be joined by space characters:\n\n```ruby\n-\u003e { div class: [:foo, :bar] }.render #=\u003e \"\u003cdiv class=\\\"foo bar\\\"\u003e\u003c/div\u003e\"\n```\n\n### Tag and Attribute Formatting\n\nPapercraft does not make any assumption about what tags and attributes you can use. You\ncan mix upper and lower case letters, and you can include arbitrary characters\nin tag and attribute names. However, in order to best adhere to the HTML specs\nand common practices, tag names and attributes will be formatted according to\nthe following rules, depending on the template type:\n\n- HTML: underscores are converted to dashes:\n\n  ```ruby\n  -\u003e {\n    foo_bar { p 'Hello', data_name: 'world' }\n  }.render #=\u003e '\u003cfoo-bar\u003e\u003cp data-name=\"world\"\u003eHello\u003c/p\u003e\u003c/foo-bar\u003e'\n  ```\n\nIf you need more precise control over tag names, you can use the `#tag` method,\nwhich takes the tag name as its first parameter, then the rest of the parameters\nnormally used for tags:\n\n```ruby\n-\u003e {\n  tag 'cra_zy__:!tag', 'foo'\n}.render #=\u003e '\u003ccra_zy__:!tag\u003efoo\u003c/cra_zy__:!tag\u003e'\n```\n\n### Escaping Content\n\nPapercraft automatically escapes all text content emitted in a template. The specific\nescaping algorithm depends on the template type. To emit raw HTML, use the\n`#raw` method as [described below](#builtin-methods).\n\n## Builtin Methods\n\nIn addition to normal tags, Papercraft provides the following method calls for templates:\n\n### `#text` - emit escaped text\n\n`#text` is used for emitting text that will be escaped. This method can be used\nto emit text not directly inside an enclosing tag:\n\n```ruby\n-\u003e {\n  p {\n    text 'The time is: '\n    span(Time.now, id: 'clock')\n  }\n}.render #=\u003e \u003cp\u003eThe time is: \u003cspan id=\"clock\"\u003eXX:XX:XX\u003c/span\u003e\u003c/p\u003e\n```\n\n### `#raw` - emit raw HTML\n\n`#raw` is used for emitting raw HTML, i.e. without escaping. You can use this to\nemit an HTML snippet:\n\n```ruby\nTITLE_HTML = '\u003ch1\u003ehi\u003c/h1\u003e'\n-\u003e {\n  div {\n    raw TITLE_HTML\n  }\n}.render #=\u003e \u003cdiv\u003e\u003ch1\u003ehi\u003c/h1\u003e\u003c/div\u003e\n```\n\n### `#render_children` - render the given block\n\n`#render_children` is used to emit a given block. If no block is given, a\n`LocalJumpError` exception is raised:\n\n```ruby\nCard = -\u003e(**props) {\n  card { render_children(**props) }\n}\n\nCard.render(foo: 'bar') { |foo|\n  h1 foo\n} #=\u003e \u003ccard\u003e\u003ch1\u003ebar\u003c/h1\u003e\u003c/card\u003e\n```\n\n`render_children` can be called with or without arguments, which are passed to the\ngiven block.\n\n### `#defer` - emit deferred HTML\n\n`#defer` is used to emit HTML in a deferred fashion - the deferred part will be\nevaluated only after processing the entire template:\n\n```ruby\nLayout = -\u003e {\n  head {\n    defer {\n      title @title\n    }\n  }\n  body {\n    render_children\n  }\n}\n\nLayout.render {\n  @title = 'Foobar'\n  h1 'hi'\n} #=\u003e \u003chead\u003e\u003ctitle\u003eFoobar\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e\u003ch1\u003ehi\u003c/h1\u003e\u003c/body\u003e\n```\n\n### `#render` - render the given template inline\n\n`#render` is used to emit the given template. This can be used to compose\ntemplates:\n\n```ruby\npartial = -\u003e { p 'foo' }\n-\u003e {\n  div {\n    render partial\n  }\n}.render #=\u003e \u003cdiv\u003e\u003cp\u003efoo\u003c/p\u003e\u003c/div\u003e\n```\n\nAny argument following the given template is passed to the template for\nrendering:\n\n```ruby\nlarge_button = -\u003e(title) { button(title, class: 'large') }\n\n-\u003e {\n  render large_button, 'foo'\n}.render #=\u003e \u003cbutton class=\"large\"\u003efoo\u003c/button\u003e\n```\n\n### `#html`/`#html5` - emit an HTML5 document type declaration and html tag\n\n```ruby\n-\u003e {\n  html5 {\n    p 'hi'\n  }\n} #=\u003e \u003c!DOCTYPE html\u003e\u003chtml\u003e\u003cp\u003ehi\u003c/p\u003e\u003c/html\u003e\n```\n\n### `#markdown` emit markdown content\n\n`#markdown` is used for rendering markdown content. The call converts the given\nmarkdown to HTML and emits it into the rendered HTML:\n\n```ruby\n-\u003e {\n  div {\n    markdown 'This is *markdown*'\n  }\n}.render #=\u003e \u003cp\u003eThis is \u003cem\u003emarkdown\u003c/em\u003e\u003c/p\u003e\n```\n\n## Template Parameters\n\nIn Papercraft, parameters are always passed explicitly. This means that template\nparameters are specified as block parameters, and are passed to the template on\nrendering:\n\n```ruby\ngreeting = -\u003e(name) { h1 \"Hello, #{name}!\" }\ngreeting.render('world') #=\u003e \"\u003ch1\u003eHello, world!\u003c/h1\u003e\"\n```\n\nTemplates can also accept named parameters:\n\n```ruby\ngreeting = -\u003e(name:) { h1 \"Hello, #{name}!\" }\ngreeting.render(name: 'world') #=\u003e \"\u003ch1\u003eHello, world!\u003c/h1\u003e\"\n```\n\n## Template Logic\n\nSince Papercraft templates are just a bunch of Ruby, you can easily embed your view\nlogic right in the template:\n\n```ruby\n-\u003e(user = nil) {\n  if user\n    span \"Hello, #{user.name}!\"\n  else\n    span \"Hello, guest!\"\n  end\n}\n```\n\n## Template Blocks\n\nTemplates can also accept and render blocks by using `render_children`:\n\n```ruby\npage = -\u003e {\n  html {\n    body { render_children }\n  }\n}\n\n# we pass the inner HTML\npage.render { h1 'hi' }\n```\n\n## Template Composition\n\nPapercraft makes it easy to compose multiple templates into a whole HTML document. A Papercraft\ntemplate can contain other templates, as the following example shows.\n\n```ruby\nTitle = -\u003e(title) { h1 title }\n\nItem = -\u003e(id:, text:, checked:) {\n  li {\n    input name: id, type: 'checkbox', checked: checked\n    label text, for: id\n  }\n}\n\nItemList = -\u003e(items) {\n  ul {\n    items.each { |i|\n      Item(**i)\n    }\n  }\n}\n\npage = -\u003e(title, items) {\n  html5 {\n    head { Title(title) }\n    body { ItemList(items) }\n  }\n}\n\npage.render('Hello from composed templates', [\n  { id: 1, text: 'foo', checked: false },\n  { id: 2, text: 'bar', checked: true }\n])\n```\n\nIn addition to using templates defined as constants, you can also use\nnon-constant templates by invoking the `#render` method:\n\n```ruby\ngreeting = -\u003e { span \"Hello, world\" }\n\n-\u003e {\n  div {\n    render greeting\n  }\n}\n```\n\n## Parameter and Block Application\n\nParameters and blocks can be applied to a template without it being rendered, by\nusing `#apply`. This mechanism is what allows template composition and the\ncreation of higher-order templates.\n\nThe `#apply` method returns a new template which applies the given parameters\nand or block to the original template:\n\n```ruby\n# parameter application\nhello = -\u003e { |name| h1 \"Hello, #{name}!\" }\nhello_world = hello.apply('world')\nhello_world.render #=\u003e \"\u003ch1\u003eHello, world!\u003c/h1\u003e\"\n\n# block application\ndiv_wrap = -\u003e { div { render_children } }\nwrapped_h1 = div_wrap.apply { h1 'hi' }\nwrapped_h1.render #=\u003e \"\u003cdiv\u003e\u003ch1\u003ehi\u003c/h1\u003e\u003c/div\u003e\"\n\n# wrap a template\nwrapped_hello_world = div_wrap.apply(\u0026hello_world)\nwrapped_hello_world.render #=\u003e \"\u003cdiv\u003e\u003ch1\u003eHello, world!\u003c/h1\u003e\u003c/div\u003e\"\n```\n\n## Higher-Order Templates\n\nPapercraft also lets you create higher-order templates, that is, templates that take\nother templates as parameters, or as blocks. Higher-order templates are handy\nfor creating layouts, wrapping templates in arbitrary markup, enhancing\ntemplates or injecting template parameters.\n\nHere is a higher-order template that takes a template as parameter:\n\n```ruby\ndiv_wrap = -\u003e { |inner| div { render inner } }\ngreeter = -\u003e { h1 'hi' }\nwrapped_greeter = div_wrap.apply(greeter)\nwrapped_greeter.render #=\u003e \"\u003cdiv\u003e\u003ch1\u003ehi\u003c/h1\u003e\u003c/div\u003e\"\n```\n\nThe inner template can also be passed as a block, as shown above:\n\n```ruby\ndiv_wrap = -\u003e { div { render_children } }\nwrapped_greeter = div_wrap.apply { h1 'hi' }\nwrapped_greeter.render #=\u003e \"\u003cdiv\u003e\u003ch1\u003ehi\u003c/h1\u003e\u003c/div\u003e\"\n```\n\n## Layout Template Composition\n\nOne of the principal uses of higher-order templates is the creation of nested\nlayouts. Suppose we have a website with a number of different layouts, and we'd\nlike to avoid having to repeat the same code in the different layouts. We can do\nthis by creating a `default` page template that takes a block, then use `#apply`\nto create the other templates:\n\n```ruby\ndefault_layout = -\u003e { |**params|\n  html5 {\n    head {\n      title: params[:title]\n    }\n    body {\n      render_children(**params)\n    }\n  }\n}\n\narticle_layout = default_layout.apply { |title:, body:|\n  article {\n    h1 title\n    markdown body\n  }\n}\n\narticle_layout.render(\n  title: 'This is a title',\n  body: 'Hello from *markdown body*'\n)\n```\n\n## Rendering Markdown\n\nMarkdown is rendered using the\n[Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use\n`#markdown`:\n\n```ruby\ntemplate = -\u003e { |md| div { markdown md } }\ntemplate.render(\"Here's some *Markdown*\") #=\u003e \"\u003cdiv\u003e\u003cp\u003eHere's some \u003cem\u003eMarkdown\u003c/em\u003e\u003cp\u003e\\n\u003c/div\u003e\"\n```\n\n[Kramdown\noptions](https://kramdown.gettalong.org/options.html#available-options) can be\nspecified by adding them to the `#markdown` call:\n\n```ruby\ntemplate = -\u003e { |md| div { markdown md, auto_ids: false } }\ntemplate.render(\"# title\") #=\u003e \"\u003cdiv\u003e\u003ch1\u003etitle\u003c/h1\u003e\u003c/div\u003e\"\n```\n\nYou can also use `Papercraft.markdown` directly:\n\n```ruby\nPapercraft.markdown('# title') #=\u003e \"\u003ch1\u003etitle\u003c/h1\u003e\"\n```\n\nThe default Kramdown options are:\n\n```ruby\n{\n  entity_output: :numeric,\n  syntax_highlighter: :rouge,\n  input: 'GFM',\n  hard_wrap: false\n}\n```\n\nThe deafult options can be configured by accessing\n`Papercraft.default_kramdown_options`, e.g.:\n\n```ruby\nPapercraft.default_kramdown_options[:auto_ids] = false\n```\n\n## Deferred Evaluation\n\nDeferred evaluation allows deferring the rendering of parts of a template until\nthe last moment, thus allowing an inner template to manipulate the state of the\nouter template. To in order to defer a part of a template, use `#defer`, and\ninclude any markup in the provided block. This technique, in in conjunction with\nholding state in instance variables, is an alternative to passing parameters,\nwhich can be limiting in some situations.\n\nA few use cases for deferred evaulation come to mind:\n\n- Setting the page title.\n- Adding a flash message to a page.\n- Using templates that dynamically add static dependencies (JS and CSS) to the\n  page.\n\nThe last use case is particularly interesting. Imagine a `DependencyMananger`\nclass that can collect JS and CSS dependencies from the different templates\nintegrated into the page, and adds them to the page's `\u003chead\u003e` element:\n\n```ruby\ndeps = DependencyMananger.new\n\ndefault_layout = -\u003e { |**args|\n  head {\n    defer { render deps.head_markup }\n  }\n  body { render_children **args }\n}\n\nbutton = proc { |text, onclick|\n  deps.js '/static/js/button.js'\n  deps.css '/static/css/button.css'\n\n  button text, onclick: onclick\n}\n\nheading = proc { |text|\n  deps.js '/static/js/heading.js'\n  deps.css '/static/css/heading.css'\n\n  h1 text\n}\n\npage = default_layout.apply {\n  render heading, \"What's your favorite cheese?\"\n\n  render button, 'Beaufort', 'eat_beaufort()'\n  render button, 'Mont d''or', 'eat_montdor()'\n  render button, 'Époisses', 'eat_epoisses()'\n}\n```\n\n## Cached Rendering\n\nPapercraft provides a simple API for caching the result of a rendering. The cache stores\nrenderings of a template respective to the given arguments. To automatically\nretrieve the cached rendered HTML, or generate it for the first time, use\n`Proc#render_cached`:\n\n```ruby\ntemplate = -\u003e(title) { div { h1 title } }\ntemplate.render_cached('foo') #=\u003e \u003cdiv\u003e\u003ch1\u003efoo\u003c/h1\u003e\u003c/div\u003e\ntemplate.render_cached('foo') #=\u003e \u003cdiv\u003e\u003ch1\u003efoo\u003c/h1\u003e\u003c/div\u003e (from cache)\ntemplate.render_cached('bar') #=\u003e \u003cdiv\u003e\u003ch1\u003ebar\u003c/h1\u003e\u003c/div\u003e\ntemplate.render_cached('bar') #=\u003e \u003cdiv\u003e\u003ch1\u003ebar\u003c/h1\u003e\u003c/div\u003e (from cache)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdigital-fabric%2Fpapercraft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdigital-fabric%2Fpapercraft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdigital-fabric%2Fpapercraft/lists"}