Class: Clickwrap::DocumentRenderers::Markdown

Inherits:
Object
  • Object
show all
Defined in:
lib/clickwrap/document_renderers/markdown.rb

Overview

Optional Markdown rendering for the representation people are offered.

The reference renderer deliberately escapes Markdown into a

 block:
faithful, dependency-free, and ugly for a forty-page Terms document. This
renderer produces real HTML instead, using whichever Markdown library the
application already bundles — it is opt-in and it adds no dependency:</p>
<pre><code>Clickwrap.configure do |config|
config.document_renderer = :markdown
end

Three engines are recognized, in order: Commonmarker (1.x and the older CommonMarker 0.x), Redcarpet, and Kramdown. An application whose Markdown dialect needs specific options can inject its own rendering instead and must name it, because the receipt records which renderer produced the offered bytes:

config.document_renderer = Clickwrap::DocumentRenderers::Markdown.new(
render_markdown_with: ->(markdown_text) { MyMarkdown.render(markdown_text) },
engine_name: "my_markdown",
engine_version: MyMarkdown::VERSION
)

A leading YAML front-matter block (the Jekyll/Sitepress convention) is stripped from the RENDERED representation only — the source digest still covers the exact file bytes, front matter included. Bundled engines' output always passes through the same safe-list sanitizer as the reference renderer, so a Markdown library's raw-HTML passthrough cannot smuggle markup into an offer. A host-supplied render_markdown_with: may additionally pass sanitize_rendered_html: false when its own pages serve the renderer's output verbatim: re-sanitizing reparses the HTML and changes its bytes, and a stored digest must describe exactly what readers were shown. The provenance records which choice was made. Non-Markdown documents delegate to the reference renderer unchanged.

Constant Summary collapse

NAME =
"clickwrap_markdown_document_renderer"
VERSION =
"1"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(render_markdown_with: nil, engine_name: nil, engine_version: nil, sanitize_rendered_html: true) ⇒ Markdown

Returns a new instance of Markdown.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/clickwrap/document_renderers/markdown.rb', line 43

def initialize(render_markdown_with: nil, engine_name: nil, engine_version: nil,
               sanitize_rendered_html: true)
  @sanitize_rendered_html = sanitize_rendered_html != false

  if !@sanitize_rendered_html && render_markdown_with.nil?
    raise ConfigurationError,
          "sanitize_rendered_html: false is only available with your own " \
          "render_markdown_with. Clickwrap's bundled engines always sanitize; skipping " \
          "sanitization is a promise about a rendering pipeline YOU own — that its " \
          "output is exactly what your own pages already serve to readers."
  end

  if render_markdown_with
    unless render_markdown_with.respond_to?(:call)
      raise ConfigurationError,
            "render_markdown_with must respond to call(markdown_text) and return HTML."
    end
    if engine_name.to_s.strip.empty?
      raise ConfigurationError,
            "A custom render_markdown_with needs an engine_name (and ideally an " \
            "engine_version), because receipts record which renderer produced the " \
            "bytes a person was offered."
    end

    @render_markdown = render_markdown_with
    @engine_name = engine_name.to_s
    @engine_version = engine_version.to_s.strip.empty? ? "unstated" : engine_version.to_s
  else
    @render_markdown, @engine_name, @engine_version = resolve_bundled_engine
  end

  @fallback = DocumentRenderer.new
end

Instance Attribute Details

#engine_nameObject (readonly)

Returns the value of attribute engine_name.



77
78
79
# File 'lib/clickwrap/document_renderers/markdown.rb', line 77

def engine_name
  @engine_name
end

#engine_versionObject (readonly)

Returns the value of attribute engine_version.



77
78
79
# File 'lib/clickwrap/document_renderers/markdown.rb', line 77

def engine_version
  @engine_version
end

Instance Method Details

#call(bytes, definition) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/clickwrap/document_renderers/markdown.rb', line 79

def call(bytes, definition)
  return @fallback.call(bytes, definition) unless definition.media_type == "text/markdown"

  text = utf8_text!(bytes, definition)
  html = @render_markdown.call(FrontMatter.strip(text))

  if @sanitize_rendered_html
    {
      bytes: DocumentRenderer.sanitize_html(html),
      media_type: "text/html; charset=utf-8",
      renderer_name: NAME,
      renderer_version: "#{VERSION} (#{@engine_name} #{@engine_version})",
      sanitizer_name: DocumentRenderer::SANITIZER_NAME,
      sanitizer_version: DocumentRenderer::SANITIZER_VERSION
    }
  else
    # Byte parity with the host's own pages is the point: re-sanitizing
    # reparses and re-serializes the HTML, so the stored digest would
    # describe bytes nobody was ever shown. The provenance says plainly
    # that no sanitizer ran — the host's rendering pipeline owns safety.
    {
      bytes: html.to_s.dup.force_encoding(Encoding::UTF_8),
      media_type: "text/html; charset=utf-8",
      renderer_name: NAME,
      renderer_version: "#{VERSION} (#{@engine_name} #{@engine_version})",
      sanitizer_name: "none",
      sanitizer_version: "host_renderer_output_stored_verbatim"
    }
  end
end