Module: Papercraft

Extended by:
Papercraft
Included in:
Papercraft
Defined in:
lib/papercraft.rb,
lib/papercraft/proc.rb,
lib/papercraft/version.rb,
lib/papercraft/compiler.rb,
lib/papercraft/proc_ext.rb,
lib/papercraft/template.rb,
lib/papercraft/compiler/tag_translator.rb,
lib/papercraft/compiler/nodes.rb

Overview

Papercraft is a functional templating library. In Papercraft, templates are expressed as plain Ruby procs.

Defined Under Namespace

Modules: ProcAPI, ProcExtensions Classes: BlockInvocationNode, BuiltinNode, Compiler, ConstTagNode, DeferNode, Error, ExtensionTagNode, RawNode, RenderNode, TagNode, TagTranslator, Template, TextNode

Constant Summary collapse

Extensions =

Registry of Papercraft extensions

{
  link_stylesheet: ->(href, **atts) {
    link(rel: "stylesheet", href:, **atts)
  }
}
VERSION =
'3.2.0'

Instance Method Summary collapse

Instance Method Details

#__clear_extensions__self

Clears all registered extensions.

Returns:

  • (self)


34
35
36
37
# File 'lib/papercraft.rb', line 34

def __clear_extensions__
  Extensions.clear
  self
end

#apply(template, *pos1, **kw1, &block1) ⇒ Proc

Returns a proc that applies the given arguments to the original proc. The returned proc calls the compiled form of the proc, merging the positional and keywords parameters passed to ‘#apply` with parameters passed to the applied proc. If a block is given, it is wrapped in a proc that passed merged parameters to the block.

Parameters:

  • template (Proc)

    template proc

  • *pos1 (Array<any>)

    applied positional parameters

  • **kw1 (Hash<any, any] applied keyword parameters)

    *kw1 [Hash<any, any] applied keyword parameters

Returns:

  • (Proc)

    applied proc



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/papercraft.rb', line 244

def apply(template, *pos1, **kw1, &block1)
  template = template.proc if template.is_a?(Template)
  compiled = template.__papercraft_compiled_proc
  block1_compiled = block1&.__papercraft_compiled_proc

  ->(__buffer__, *pos2, **kw2, &block2) {
    if block2
      block2_compiled = block1_compiled ?
        ->(__buffer__, *pos3, **kw3) {
          block1_compiled.(__buffer__, *pos3, **kw3, &block2)
        }.__papercraft_compiled! :
        block2.__papercraft_compiled_proc
      compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &block2_compiled)
    else
      compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &block1_compiled)
    end
  }.__papercraft_compiled!
end

#ast(proc) ⇒ Prism::Node

Returns the AST for the given proc.

Parameters:

  • proc (Proc)

    template proc

Returns:

  • (Prism::Node)

    AST root



180
181
182
# File 'lib/papercraft.rb', line 180

def ast(proc)
  Sirop.to_ast(proc)
end

#cache_html(template, key) ⇒ String

Caches and returns the rendered HTML for the template with the given arguments.

Parameters:

  • template (Proc)

    template proc

  • key (any)

    Cache key

Returns:

  • (String)

    HTML string



269
270
271
# File 'lib/papercraft.rb', line 269

def cache_html(template, key, *, **, &)
  template.__papercraft_render_cache[key] ||= html(template, *, **, &)
end

#cache_xml(template, key) ⇒ String

Caches and returns the rendered XML for the template with the given arguments.

Parameters:

  • template (Proc)

    template proc

  • key (any)

    Cache key

Returns:

  • (String)

    XML string



279
280
281
# File 'lib/papercraft.rb', line 279

def cache_xml(template, key, *, **, &)
  template.__papercraft_render_cache[key] ||= xml(template, *, **, &)
end

#compile(proc, mode: :html) ⇒ Proc

Compiles the given template.

Parameters:

  • proc (Proc)

    template proc

  • mode (Symbol) (defaults to: :html)

    compilation mode (:html, :xml)

Returns:

  • (Proc)

    compiled proc



189
190
191
192
193
# File 'lib/papercraft.rb', line 189

def compile(proc, mode: :html)
  Papercraft::Compiler.compile(proc, mode:).__papercraft_compiled!
rescue Sirop::Error
  raise Papercraft::Error, "Can't compile eval'd template"
end

#compiled_code(proc) ⇒ String

Returns the compiled form code for the given proc.

Parameters:

  • proc (Proc)

    template proc

Returns:

  • (String)

    compiled proc code



162
163
164
# File 'lib/papercraft.rb', line 162

def compiled_code(proc)
  Papercraft::Compiler.compile_to_code(proc).last
end

#compute_backtrace_entry(entry, cache) ⇒ Object

Computes a backtrace entry with caching.

Parameters:

  • entry (String)

    backtrace entry

  • cache (Hash)

    cache store mapping compiled filename to source_map



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/papercraft.rb', line 85

def compute_backtrace_entry(entry, cache)
  m = entry.match(/^((\:\:\(.+\:.+\))\:(\d+))/)
  return entry if !m

  fn = m[2]
  line = m[3].to_i
  source_map = cache[fn] ||= Compiler.source_map_store[fn]
  return entry if !source_map

  ref = source_map[line] || "?(#{line})"
  entry.sub(m[1], ref)
end

#default_kramdown_optionsHash

Returns the default Kramdown options used for rendering Markdown.

Returns:

  • (Hash)

    Kramdown options



141
142
143
144
145
146
147
148
# File 'lib/papercraft.rb', line 141

def default_kramdown_options
  @default_kramdown_options ||= {
    entity_output: :numeric,
    syntax_highlighter: :rouge,
    input: 'GFM',
    hard_wrap: false
  }
end

#default_kramdown_options=(opts) ⇒ Hash

Sets the default Kramdown options used for rendering Markdown.

Parameters:

  • opts (Hash)

    Kramdown options

Returns:

  • (Hash)

    Kramdown options



154
155
156
# File 'lib/papercraft.rb', line 154

def default_kramdown_options=(opts)
  @default_kramdown_options = opts
end

#extension(spec) ⇒ self

Registers extensions to the Papercraft syntax.

Parameters:

  • spec (Hash)

    hash mapping symbols to procs

Returns:

  • (self)


26
27
28
29
# File 'lib/papercraft.rb', line 26

def extension(spec)
  Extensions.merge!(spec)
  self
end

#format_tag_attrs(attrs) ⇒ String

Formats the given hash as tag attributes.

Parameters:

  • attrs (Hash)

    input hash

Returns:

  • (String)

    formatted attributes



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/papercraft.rb', line 51

def format_tag_attrs(attrs)
  attrs.each_with_object(+'') do |(k, v), html|
    case v
    when nil, false
    when true
      html << ' ' if !html.empty?
      html << underscores_to_dashes(k)
    else
      html << ' ' if !html.empty?
      v = v.join(' ') if v.is_a?(Array)
      html << "#{underscores_to_dashes(k)}=\"#{v}\""
    end
  end
end

#html(template = nil, *pos, **kw, &block) ⇒ String

Renders the given template to HTML with the given arguments. The template can be passed either as the first parameter, or as a block, if no parameter is given.

Parameters:

  • template (Proc) (defaults to: nil)

    template proc

Returns:

  • (String)

    HTML string



201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/papercraft.rb', line 201

def html(template = nil, *pos, **kw, &block)
  if !template
    template = block
  elsif template.is_a?(Template)
    template = template.proc
  end
  raise ArgumentError, "No template given" if !template

  template.__papercraft_compiled_proc.(+'', *pos, **kw, &block)
rescue Exception => e
  e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
end

#make_argument_error(err, backtrace) ⇒ Object



98
99
100
101
102
103
104
105
106
107
# File 'lib/papercraft.rb', line 98

def make_argument_error(err, backtrace)
  m = err.message.match(/(given (\d+), expected (\d+))/)
  if m
    rectified = format('given %d, expected %d', m[2].to_i - 1, m[3].to_i - 1)
    message = err.message.gsub(m[1], rectified)
  else
    message = err.message
  end
  ArgumentError.new(message).tap { it.set_backtrace(backtrace) }
end

#markdown(markdown, **opts) ⇒ String

Renders Markdown into HTML. The ‘opts` argument will be merged with the default Kramdown options in order to change the rendering behaviour.

Parameters:

  • markdown (String)

    Markdown

  • opts (Hash)

    Kramdown option overrides

Returns:

  • (String)

    HTML



134
135
136
# File 'lib/papercraft.rb', line 134

def markdown(markdown, **opts)
  markdown_doc(markdown, **opts).to_html
end

#markdown_doc(markdown, **opts) ⇒ Kramdown::Document

Returns a Kramdown doc for the given markdown. The ‘opts` argument will be merged with the default Kramdown options in order to change the rendering behaviour.

Parameters:

  • markdown (String)

    Markdown

  • opts (Hash)

    Kramdown option overrides

Returns:

  • (Kramdown::Document)

    Kramdown document



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/papercraft.rb', line 116

def markdown_doc(markdown, **opts)
  @markdown_deps_loaded ||= begin
    require 'kramdown'
    require 'rouge'
    require 'kramdown-parser-gfm'
    true
  end

  opts = default_kramdown_options.merge(opts)
  Kramdown::Document.new(markdown, **opts)
end

#source_map(proc) ⇒ Array<String>

Returns the source map for the given proc.

Parameters:

  • proc (Proc)

    template proc

Returns:

  • (Array<String>)

    source map



170
171
172
173
174
# File 'lib/papercraft.rb', line 170

def source_map(proc)
  loc = proc.source_location
  fn = proc.__papercraft_compiled? ? loc.first : Papercraft::Compiler.source_location_to_fn(loc)
  Papercraft::Compiler.source_map_store[fn]
end

#translate_backtrace(err) ⇒ Exception

Translates entries in exception’s backtrace to point to original source code.

Parameters:

  • err (Exception)

    raised exception

Returns:

  • (Exception)

    raised exception



70
71
72
73
74
75
76
77
78
79
# File 'lib/papercraft.rb', line 70

def translate_backtrace(err)
  cache = {}
  is_argument_error = err.is_a?(ArgumentError) && err.backtrace[0] =~ /^\:\:/
  backtrace = err.backtrace.map { |e| compute_backtrace_entry(e, cache) }

  return make_argument_error(err, backtrace) if is_argument_error

  err.set_backtrace(backtrace)
  err
end

#underscores_to_dashes(tag) ⇒ String

Formats the given string, converting underscores to dashes.

Parameters:

  • tag (String, Symbol)

    input string

Returns:

  • (String)

    output string



43
44
45
# File 'lib/papercraft.rb', line 43

def underscores_to_dashes(tag)
  tag.to_s.gsub('_', '-')
end

#xml(template = nil, *pos, **kw, &block) ⇒ String

Renders the given template to XML with the given arguments. The template can be passed either as the first parameter, or as a block, if no parameter is given.

Parameters:

  • template (Proc) (defaults to: nil)

    template proc

Returns:

  • (String)

    XML string



220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/papercraft.rb', line 220

def xml(template = nil, *pos, **kw, &block)
  if !template
    template = block
  elsif template.is_a?(Template)
    template = template.proc
  end
  raise ArgumentError, "No template given" if !template

  template = template.proc if template.is_a?(Template)
  template.__papercraft_compiled_proc(mode: :xml).(+'', *pos, **kw, &block)
rescue Exception => e
  e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
end