asciisourcerer 0.5.0 → 0.6.0
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/README.adoc +27 -7
- data/lib/sourcerer/jekyll/bootstrapper.rb +1 -2
- data/lib/sourcerer/jekyll/liquid/preserve_missing_variables.rb +148 -0
- data/lib/sourcerer/jekyll/liquid/tags.rb +31 -3
- data/lib/sourcerer/jekyll.rb +1 -0
- data/lib/sourcerer/rendering.rb +65 -42
- data/lib/sourcerer/source_skim/skimmer.rb +1 -2
- data/lib/sourcerer/version.rb +1 -1
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7b6d7c78acf65b0a4c2b7aef397ea65a254fc486cc616a5977794cdd51eb67f2
|
|
4
|
+
data.tar.gz: 66de4e674a882d87dc919aa2eab680a10cd9fae2d8df5033b32f6b79d27b9a61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86e2ef0fc8684ab20d29832f1a78d4d8171bd5f4a2de36659ae4cb96ddde8f37f42e35ed446afe466245e0ee4bbe596a9095549cf0f864fbea0ff86de588e54f
|
|
7
|
+
data.tar.gz: 3fc489d1777101bfbb178cbca588e0988828aa682fe4a763a9efd9cf4775cf2fe9bf7f1e887d0fba6c9a149353fa266747b44f30a4fad4a19791501c64e906e0
|
data/README.adoc
CHANGED
|
@@ -37,11 +37,11 @@ endif::[]
|
|
|
37
37
|
:this_prod_name: {this_proj_name}
|
|
38
38
|
// end::universal-settings[]
|
|
39
39
|
:this_prod_vrsn_major: 0
|
|
40
|
-
:this_prod_vrsn_minor:
|
|
40
|
+
:this_prod_vrsn_minor: 6
|
|
41
41
|
:this_prod_vrsn_majmin: {this_prod_vrsn_major}.{this_prod_vrsn_minor}
|
|
42
42
|
:this_prod_vrsn_patch: 0
|
|
43
43
|
:this_prod_vrsn: {this_prod_vrsn_majmin}.{this_prod_vrsn_patch}
|
|
44
|
-
:next_prod_vrsn: 0.
|
|
44
|
+
:next_prod_vrsn: 0.7.0
|
|
45
45
|
// end::global-settings[]
|
|
46
46
|
// end::ai-prompt[]
|
|
47
47
|
:toc: macro
|
|
@@ -823,7 +823,9 @@ Avoid exporting helper methods as accidental public API.
|
|
|
823
823
|
=== Generated Reference Docs
|
|
824
824
|
|
|
825
825
|
Some reference documentation is generated from data rather than hand-written, so it can't drift from what the code actually does.
|
|
826
|
-
`specs/data/docs-manifest.yml` lists each generation job: a Liquid
|
|
826
|
+
A manifest (`specs/data/docs-manifest.yml`) lists each generation job: a Liquid template, a YAML data source, an output path, and optional `vars` to parameterize a shared template into multiple outputs.
|
|
827
|
+
Each entry also accepts the Liquid-only `preserve_missing` and `preserve_empty` booleans (both default `false`), forwarded straight through to `Sourcerer::Rendering.render_template`.
|
|
828
|
+
See the API docs on `Sourcerer::Rendering.render_template` for exactly what each one preserves.
|
|
827
829
|
|
|
828
830
|
For example, `specs/data/liquid-filters.yml` (the same source of truth used by the Liquid filter test suite; see <<tests>>) is rendered through `docs/templates/liquid-filters-ref.adoc.liquid` twice -- once grouped by filter category, once by filter source -- producing the partials committed at `lib/sourcerer/_docs/partials/liquid-filters-by-kind.adoc` and `liquid-filters-by-source.adoc`.
|
|
829
831
|
These partials ship with the gem so downstream tools can `include::` them directly.
|
|
@@ -833,6 +835,15 @@ Regenerate all manifest entries with:
|
|
|
833
835
|
[.prompt]
|
|
834
836
|
bundle exec rake generate:docs
|
|
835
837
|
|
|
838
|
+
These same partials are also published to the `documentation` branch, under `partials/`.
|
|
839
|
+
That branch holds only generated output (no source code) so it can be pulled directly (ex: via raw.githubusercontent.com) by consumers who don't install the gem.
|
|
840
|
+
Regenerate and locally commit an update to that branch with:
|
|
841
|
+
|
|
842
|
+
[.prompt]
|
|
843
|
+
bundle exec rake generate:documentation
|
|
844
|
+
|
|
845
|
+
This only commits locally; push with `git push -f origin documentation` after reviewing the result.
|
|
846
|
+
|
|
836
847
|
[[tests]]
|
|
837
848
|
=== Tests
|
|
838
849
|
|
|
@@ -857,19 +868,28 @@ Run the PR/CI test suite:
|
|
|
857
868
|
=== Release Process and History
|
|
858
869
|
|
|
859
870
|
AsciiSourcerer is intended to be the most primal gem in the DocOps Lab pool.
|
|
860
|
-
As such, its
|
|
871
|
+
As such, its release procedure is lighter and less formal.
|
|
861
872
|
|
|
862
|
-
Each release is tested against all downstream gems and tools, but we are
|
|
873
|
+
Each release is tested against all downstream gems and tools, but we are less concerned with issue tracking and development branch management, at least in these early stages.
|
|
863
874
|
|
|
864
|
-
Standard release flow
|
|
875
|
+
Standard release flow differs from the general DocOps Lab conventions.
|
|
865
876
|
|
|
866
877
|
. Update version in `lib/sourcerer/version.rb` and `README.adoc` attributes.
|
|
867
878
|
|
|
879
|
+
. Test all changes using local clones of the latest released versions of these downstream gems:
|
|
880
|
+
|
|
881
|
+
* link:{docopslab_src_www_url}/schemagraphy[DocOps/schemagraphy]
|
|
882
|
+
* link:{docopslab_src_www_url}/lab[DocOps/lab]
|
|
883
|
+
* link:{docopslab_src_www_url}/releasehx[DocOps/releasehx]
|
|
884
|
+
|
|
868
885
|
. Commit to Git.
|
|
869
886
|
|
|
870
887
|
. Push to GitHub.
|
|
871
888
|
|
|
872
|
-
. Run `./scripts/build.sh` to
|
|
889
|
+
. Run `./scripts/build.sh` to:
|
|
890
|
+
.. validate the environment
|
|
891
|
+
.. run tests, and
|
|
892
|
+
.. build the gem file to `pkg/`
|
|
873
893
|
|
|
874
894
|
. Tag the release in Git:
|
|
875
895
|
+
|
|
@@ -37,7 +37,7 @@ module Sourcerer
|
|
|
37
37
|
# @param includes_load_paths [Array<String>] Paths to load includes from.
|
|
38
38
|
# @param plugin_dirs [Array<String>] Paths to load plugins from.
|
|
39
39
|
# @return [Jekyll::Site] The initialized fake Jekyll site object.
|
|
40
|
-
# rubocop:disable Lint/UnusedMethodArgument
|
|
40
|
+
# rubocop:disable-next Lint/UnusedMethodArgument
|
|
41
41
|
def self.fake_site includes_load_paths: [], plugin_dirs: []
|
|
42
42
|
# NOTE: plugin_dirs parameter is accepted but not yet implemented; reserved for future plugin loading
|
|
43
43
|
::Jekyll.logger.log_level = :error if ::Jekyll.logger.respond_to?(:log_level=)
|
|
@@ -72,7 +72,6 @@ module Sourcerer
|
|
|
72
72
|
|
|
73
73
|
site
|
|
74
74
|
end
|
|
75
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
|
76
75
|
end
|
|
77
76
|
end
|
|
78
77
|
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'liquid'
|
|
4
|
+
|
|
5
|
+
module Sourcerer
|
|
6
|
+
module Jekyll
|
|
7
|
+
module Liquid
|
|
8
|
+
# Opt-in alternative to Liquid's default "render nil/empty as empty
|
|
9
|
+
# string" behavior: when active, a `{{ ... }}` expression that
|
|
10
|
+
# evaluates to a missing or empty value renders as its own original
|
|
11
|
+
# source text (e.g. `{{ data.foo | capitalize }}`) instead of
|
|
12
|
+
# vanishing. Useful for document templates where an unfilled field
|
|
13
|
+
# should stay visible for the preparer to fill in by hand, rather
|
|
14
|
+
# than silently disappearing.
|
|
15
|
+
#
|
|
16
|
+
# Two independent toggles, each off by default:
|
|
17
|
+
#
|
|
18
|
+
# - `preserve_missing`: an outright `nil` result is preserved.
|
|
19
|
+
# - `preserve_empty`: a defined-but-empty String/Array/Hash result
|
|
20
|
+
# (`""`, `[]`, `{}`) is preserved.
|
|
21
|
+
#
|
|
22
|
+
# Neither affects the other's target, and neither affects `0` or
|
|
23
|
+
# `false`. Both checks run against the fully-filtered result (i.e.
|
|
24
|
+
# any `| filters` in the tag have already run), matching what Liquid
|
|
25
|
+
# itself would otherwise render -- we never re-run the filters, we
|
|
26
|
+
# just restore the tag's own pre-render source text once a render
|
|
27
|
+
# has been judged missing/empty. `raw` is used verbatim (no
|
|
28
|
+
# whitespace trimming), so the reconstructed tag matches the
|
|
29
|
+
# original byte-for-byte -- except for `{{-`/`-}}` whitespace
|
|
30
|
+
# control markers, which Liquid's tokenizer strips before the
|
|
31
|
+
# markup ever reaches a `Variable` instance and so cannot be
|
|
32
|
+
# recovered here.
|
|
33
|
+
#
|
|
34
|
+
# Substitution only ever applies to a *top-level output* `{{ }}`
|
|
35
|
+
# tag -- never to a `Liquid::Variable` some other tag builds
|
|
36
|
+
# privately to parse its own syntax. `Assign`, for example, does
|
|
37
|
+
# exactly that (`lib/liquid/tags/assign.rb`: `@from = Variable.new(
|
|
38
|
+
# $2, options)`, then `@from.render(context)` directly), and that
|
|
39
|
+
# Variable's `#render` is the same patched method, with the same
|
|
40
|
+
# global toggle active. Without this distinction,
|
|
41
|
+
# `{% assign name = data.missing %}` under `preserve_missing: true`
|
|
42
|
+
# would assign the *reconstructed source text of the assign's own
|
|
43
|
+
# right-hand side* (a String) to `name`, rather than the real `nil`
|
|
44
|
+
# -- corrupting every later `{{ name }}` reference instead of
|
|
45
|
+
# leaving it to render its own clean placeholder. `BlockBodyPatch`
|
|
46
|
+
# below marks the one call site (`BlockBody#render_node_to_output`,
|
|
47
|
+
# when the node being rendered is itself a `Variable`) that
|
|
48
|
+
# corresponds to a literal `{{ }}` sitting directly in a template
|
|
49
|
+
# body's nodelist -- the only place a Variable's rendered text
|
|
50
|
+
# actually becomes document output. Any other tag's internal use of
|
|
51
|
+
# a Variable to compute a value (Assign's right-hand side, or
|
|
52
|
+
# anything similar) renders outside that marker and is left with
|
|
53
|
+
# Liquid's normal nil/empty behavior, exactly as if this feature
|
|
54
|
+
# were off.
|
|
55
|
+
#
|
|
56
|
+
# Liquid 4 has no built-in hook for this -- there is no Environment or
|
|
57
|
+
# parser-injection point (introduced only in Liquid 5) to substitute a
|
|
58
|
+
# custom Variable class per template -- so this prepends onto
|
|
59
|
+
# `::Liquid::Variable` globally and gates the actual behavior change
|
|
60
|
+
# with toggles scoped to one render call. See
|
|
61
|
+
# `Sourcerer::Rendering.render_template`'s `preserve_missing:`/
|
|
62
|
+
# `preserve_empty:` options, which flip these toggles for the
|
|
63
|
+
# duration of that one render.
|
|
64
|
+
module PreserveMissingVariables
|
|
65
|
+
def self.preserve_missing?
|
|
66
|
+
Thread.current[:sourcerer_preserve_missing] == true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.preserve_empty?
|
|
70
|
+
Thread.current[:sourcerer_preserve_empty] == true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.top_level_output?
|
|
74
|
+
Thread.current[:sourcerer_rendering_output_variable] == true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Activates preserve-missing/-empty rendering for the duration of
|
|
78
|
+
# the block. Restores the prior values afterward so nested/
|
|
79
|
+
# sequential renders that don't ask for it are unaffected.
|
|
80
|
+
#
|
|
81
|
+
# @param preserve_missing [Boolean] See {PreserveMissingVariables}.
|
|
82
|
+
# @param preserve_empty [Boolean] See {PreserveMissingVariables}.
|
|
83
|
+
def self.with_active preserve_missing: false, preserve_empty: false
|
|
84
|
+
previous_missing = Thread.current[:sourcerer_preserve_missing]
|
|
85
|
+
previous_empty = Thread.current[:sourcerer_preserve_empty]
|
|
86
|
+
Thread.current[:sourcerer_preserve_missing] = preserve_missing
|
|
87
|
+
Thread.current[:sourcerer_preserve_empty] = preserve_empty
|
|
88
|
+
yield
|
|
89
|
+
ensure
|
|
90
|
+
Thread.current[:sourcerer_preserve_missing] = previous_missing
|
|
91
|
+
Thread.current[:sourcerer_preserve_empty] = previous_empty
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Prepended onto ::Liquid::Variable. Falls through to Liquid's own
|
|
95
|
+
# `render` untouched unless: this Variable is currently being
|
|
96
|
+
# rendered as a top-level output node (see `BlockBodyPatch`
|
|
97
|
+
# below), preserve-missing/-empty is active for this thread, and
|
|
98
|
+
# the fully-filtered result counts as missing (nil) or empty (a
|
|
99
|
+
# defined-but-empty String/Array/Hash).
|
|
100
|
+
module VariablePatch
|
|
101
|
+
def render context
|
|
102
|
+
result = super
|
|
103
|
+
return result unless PreserveMissingVariables.top_level_output?
|
|
104
|
+
return "{{#{raw}}}" if PreserveMissingVariables.preserve_missing? && result.nil?
|
|
105
|
+
return "{{#{raw}}}" if PreserveMissingVariables.preserve_empty? && empty_result?(result)
|
|
106
|
+
|
|
107
|
+
result
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# Only the documented value types count as "empty" -- not any
|
|
113
|
+
# arbitrary object/Drop that happens to implement `empty?` with
|
|
114
|
+
# its own, possibly unrelated, meaning.
|
|
115
|
+
def empty_result? value
|
|
116
|
+
(value.is_a?(String) || value.is_a?(Array) || value.is_a?(Hash)) && value.empty?
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Prepended onto ::Liquid::BlockBody. `render_node_to_output` is
|
|
121
|
+
# the one call site where a node from a body's own nodelist --
|
|
122
|
+
# i.e. a literal `{{ }}` or `{% tag %}` written directly in
|
|
123
|
+
# template source -- gets rendered and appended to output. We
|
|
124
|
+
# only care about the `Variable` case: that's a genuine top-level
|
|
125
|
+
# `{{ }}` output tag, as opposed to a `Variable` some other tag
|
|
126
|
+
# (Assign, etc.) builds privately and renders itself, bypassing
|
|
127
|
+
# BlockBody entirely. See {PreserveMissingVariables} above for why
|
|
128
|
+
# this distinction matters.
|
|
129
|
+
module BlockBodyPatch
|
|
130
|
+
# rubocop:disable-next Style/OptionalBooleanParameter -- must match
|
|
131
|
+
# ::Liquid::BlockBody#render_node_to_output's own positional signature
|
|
132
|
+
def render_node_to_output node, output, context, skip_output = false
|
|
133
|
+
return super unless node.is_a?(::Liquid::Variable)
|
|
134
|
+
|
|
135
|
+
previous = Thread.current[:sourcerer_rendering_output_variable]
|
|
136
|
+
Thread.current[:sourcerer_rendering_output_variable] = true
|
|
137
|
+
super
|
|
138
|
+
ensure
|
|
139
|
+
Thread.current[:sourcerer_rendering_output_variable] = previous if node.is_a?(::Liquid::Variable)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
Liquid::Variable.prepend(Sourcerer::Jekyll::Liquid::PreserveMissingVariables::VariablePatch)
|
|
148
|
+
Liquid::BlockBody.prepend(Sourcerer::Jekyll::Liquid::PreserveMissingVariables::BlockBodyPatch)
|
|
@@ -8,12 +8,25 @@ module Sourcerer
|
|
|
8
8
|
# A Liquid tag for embedding and rendering a file within a template.
|
|
9
9
|
# It searches for the file in the configured include paths.
|
|
10
10
|
class EmbedTag < ::Liquid::Tag
|
|
11
|
+
# Matches an optional single- or double-quoted string, capturing the
|
|
12
|
+
# inner content; falls back to the whole (trimmed) markup as a bareword.
|
|
13
|
+
# Requires at least one character inside the quotes: an empty quoted
|
|
14
|
+
# name (`""`/`''`) would otherwise expand to the includes directory
|
|
15
|
+
# itself, pass File.exist?, and raise Errno::EISDIR from File.read
|
|
16
|
+
# instead of the intended missing-file error. Left unmatched, it
|
|
17
|
+
# falls through to the bareword branch instead, where the literal
|
|
18
|
+
# (quoted) name is looked up and not found -- reported normally.
|
|
19
|
+
PARTIAL_NAME_PATTERN = /\A(?:"([^"]+)"|'([^']+)')\z/
|
|
20
|
+
|
|
11
21
|
# @param tag_name [String] The name of the tag ('embed').
|
|
12
|
-
# @param markup [String] The name of the partial to embed
|
|
22
|
+
# @param markup [String] The name of the partial to embed, quoted
|
|
23
|
+
# (+"foo.liquid"+ / +'foo.liquid'+) or bareword (+foo.liquid+).
|
|
13
24
|
# @param tokens [Array<String>] The list of tokens.
|
|
14
25
|
def initialize tag_name, markup, tokens
|
|
15
26
|
super
|
|
16
|
-
|
|
27
|
+
trimmed = markup.strip
|
|
28
|
+
match = trimmed.match(PARTIAL_NAME_PATTERN)
|
|
29
|
+
@partial_name = match ? (match[1] || match[2]) : trimmed
|
|
17
30
|
end
|
|
18
31
|
|
|
19
32
|
# Renders the embedded file.
|
|
@@ -22,7 +35,9 @@ module Sourcerer
|
|
|
22
35
|
# @return [String] The rendered content of the embedded file.
|
|
23
36
|
# @raise [IOError] if the embed file is not found.
|
|
24
37
|
def render context
|
|
25
|
-
includes_paths = context.registers[:includes_load_paths]
|
|
38
|
+
includes_paths = context.registers[:includes_load_paths]
|
|
39
|
+
includes_paths = site_includes_load_paths(context) if includes_paths.nil? || includes_paths.empty?
|
|
40
|
+
includes_paths ||= []
|
|
26
41
|
|
|
27
42
|
found_path = includes_paths.find do |base|
|
|
28
43
|
candidate = File.expand_path(@partial_name, base)
|
|
@@ -37,6 +52,19 @@ module Sourcerer
|
|
|
37
52
|
partial = ::Liquid::Template.parse(source)
|
|
38
53
|
partial.render!(context)
|
|
39
54
|
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Fallback for callers that register a fake/real Jekyll +:site+ but
|
|
59
|
+
# forget the separate +:includes_load_paths+ register (e.g. any
|
|
60
|
+
# future caller mirroring {Sourcerer::Rendering.render_liquid}).
|
|
61
|
+
# Reads +site.config['includes_load_paths']+ directly rather than
|
|
62
|
+
# +site.includes_load_paths+: the latter is Jekyll's own attribute,
|
|
63
|
+
# derived only from +config['includes_dir']+ (effectively just the
|
|
64
|
+
# first path), not the full list Sourcerer stores in config.
|
|
65
|
+
def site_includes_load_paths context
|
|
66
|
+
context.registers[:site]&.config&.[]('includes_load_paths')
|
|
67
|
+
end
|
|
40
68
|
end
|
|
41
69
|
end
|
|
42
70
|
end
|
data/lib/sourcerer/jekyll.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative 'jekyll/monkeypatches'
|
|
|
5
5
|
require_relative 'jekyll/liquid/file_system'
|
|
6
6
|
require_relative 'jekyll/liquid/filters'
|
|
7
7
|
require_relative 'jekyll/liquid/tags'
|
|
8
|
+
require_relative 'jekyll/liquid/preserve_missing_variables'
|
|
8
9
|
require 'jekyll-asciidoc'
|
|
9
10
|
|
|
10
11
|
module Sourcerer
|
data/lib/sourcerer/rendering.rb
CHANGED
|
@@ -37,7 +37,9 @@ module Sourcerer
|
|
|
37
37
|
data_object: data_obj,
|
|
38
38
|
attrs_source: attrs_source,
|
|
39
39
|
engine: engine,
|
|
40
|
-
vars: render_entry[:vars] || {}
|
|
40
|
+
vars: render_entry[:vars] || {},
|
|
41
|
+
preserve_missing: render_entry[:preserve_missing] || false,
|
|
42
|
+
preserve_empty: render_entry[:preserve_empty] || false)
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -53,39 +55,53 @@ module Sourcerer
|
|
|
53
55
|
# @param vars [Hash] Arbitrary caller-supplied variables, exposed to the
|
|
54
56
|
# template as `vars` (e.g. to parameterize a shared template between
|
|
55
57
|
# multiple render entries in a manifest).
|
|
58
|
+
# @param preserve_missing [Boolean] Liquid-only, independent of
|
|
59
|
+
# `preserve_empty`. When true, a `{{ ... }}` expression that evaluates
|
|
60
|
+
# to nil renders as its own original source text instead of an empty
|
|
61
|
+
# string, so an unfilled field stays visible for manual completion
|
|
62
|
+
# rather than silently disappearing. Has no effect on a defined
|
|
63
|
+
# empty value (`""`, `[]`, `{}`).
|
|
64
|
+
# @param preserve_empty [Boolean] Liquid-only, independent of
|
|
65
|
+
# `preserve_missing`. When true, a `{{ ... }}` expression that
|
|
66
|
+
# evaluates to a defined-but-empty String/Array/Hash (`""`, `[]`,
|
|
67
|
+
# `{}`) is preserved as its own original source text the same way.
|
|
68
|
+
# Has no effect on nil. `0` and `false` are never affected by either
|
|
69
|
+
# option.
|
|
56
70
|
def self.render_template template_file, data_file, out_file, **options
|
|
57
|
-
supported_option_keys = %i[data_object includes_load_paths attrs_source engine vars
|
|
71
|
+
supported_option_keys = %i[data_object includes_load_paths attrs_source engine vars
|
|
72
|
+
preserve_missing preserve_empty]
|
|
58
73
|
unknown_option_keys = options.keys - supported_option_keys
|
|
59
74
|
raise ArgumentError, "unknown option(s): #{unknown_option_keys.join(', ')}" unless unknown_option_keys.empty?
|
|
60
75
|
|
|
61
76
|
data_object = options.fetch(:data_object, 'data')
|
|
62
|
-
|
|
63
|
-
attrs_source = options[:attrs_source]
|
|
64
|
-
engine = options.fetch(:engine, 'liquid')
|
|
77
|
+
data = load_render_data(data_file, options[:attrs_source])
|
|
65
78
|
vars = (options[:vars] || {}).transform_keys(&:to_s)
|
|
79
|
+
context = { data_object => data, 'include' => { data_object => data }, 'vars' => vars }
|
|
80
|
+
|
|
81
|
+
preserve = { missing: options.fetch(:preserve_missing, false), empty: options.fetch(:preserve_empty, false) }
|
|
82
|
+
rendered = render_in_engine(
|
|
83
|
+
options.fetch(:engine, 'liquid'), template_file, context, options.fetch(:includes_load_paths, []), preserve)
|
|
66
84
|
|
|
67
|
-
data = load_render_data(data_file, attrs_source)
|
|
68
85
|
out_file = File.expand_path(out_file)
|
|
69
86
|
FileUtils.mkdir_p(File.dirname(out_file))
|
|
70
|
-
|
|
71
|
-
template_path = File.expand_path(template_file)
|
|
72
|
-
template_content = File.read(template_path)
|
|
73
|
-
|
|
74
|
-
context = {
|
|
75
|
-
data_object => data,
|
|
76
|
-
'include' => { data_object => data },
|
|
77
|
-
'vars' => vars
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
rendered = case engine.to_s
|
|
81
|
-
when 'erb' then render_erb(template_content, context)
|
|
82
|
-
when 'liquid' then render_liquid(template_file, template_content, context, includes_load_paths)
|
|
83
|
-
else raise ArgumentError, "Unsupported template engine: #{engine}"
|
|
84
|
-
end
|
|
85
|
-
|
|
86
87
|
File.write(out_file, rendered)
|
|
87
88
|
end
|
|
88
89
|
|
|
90
|
+
# @api private
|
|
91
|
+
# Dispatches rendering to the requested template engine.
|
|
92
|
+
#
|
|
93
|
+
# @param preserve [Hash] `{missing:, empty:}` -- see {.render_template}'s
|
|
94
|
+
# `preserve_missing`/`preserve_empty` options.
|
|
95
|
+
# @return [String]
|
|
96
|
+
def self.render_in_engine engine, template_file, context, includes_load_paths, preserve
|
|
97
|
+
template_content = File.read(File.expand_path(template_file))
|
|
98
|
+
case engine.to_s
|
|
99
|
+
when 'erb' then render_erb(template_content, context)
|
|
100
|
+
when 'liquid' then render_liquid(template_file, template_content, context, includes_load_paths, preserve)
|
|
101
|
+
else raise ArgumentError, "Unsupported template engine: #{engine}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
89
105
|
# Renders output using a converter callable or converter constant name.
|
|
90
106
|
#
|
|
91
107
|
# @param render_entry [Hash] Render entry containing converter config.
|
|
@@ -151,38 +167,43 @@ module Sourcerer
|
|
|
151
167
|
# @param template_content [String]
|
|
152
168
|
# @param context [Hash]
|
|
153
169
|
# @param includes_load_paths [Array<String>]
|
|
170
|
+
# @param preserve [Hash] `{missing:, empty:}` -- see {.render_template}'s
|
|
171
|
+
# `preserve_missing`/`preserve_empty` options.
|
|
154
172
|
# @return [String]
|
|
155
|
-
def self.render_liquid template_file, template_content, context, includes_load_paths
|
|
173
|
+
def self.render_liquid template_file, template_content, context, includes_load_paths, preserve
|
|
156
174
|
require_relative 'jekyll'
|
|
157
175
|
require_relative 'jekyll/liquid/filters'
|
|
158
176
|
require_relative 'jekyll/liquid/tags'
|
|
177
|
+
require_relative 'jekyll/liquid/preserve_missing_variables'
|
|
159
178
|
require 'liquid' unless defined?(Liquid::Template)
|
|
160
179
|
Sourcerer::Jekyll.initialize_liquid_runtime
|
|
161
180
|
|
|
181
|
+
registers = liquid_registers(template_file, includes_load_paths)
|
|
182
|
+
template = Liquid::Template.parse(template_content)
|
|
183
|
+
render = -> { template.render(context, registers: registers) }
|
|
184
|
+
|
|
185
|
+
return render.call unless preserve[:missing] || preserve[:empty]
|
|
186
|
+
|
|
187
|
+
Sourcerer::Jekyll::Liquid::PreserveMissingVariables.with_active(
|
|
188
|
+
preserve_missing: preserve[:missing], preserve_empty: preserve[:empty], &render)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# @api private
|
|
192
|
+
# Builds the Liquid registers hash (fake site + file system) a template
|
|
193
|
+
# render needs to resolve `{% embed %}`/include partials.
|
|
194
|
+
#
|
|
195
|
+
# @return [Hash]
|
|
196
|
+
def self.liquid_registers template_file, includes_load_paths
|
|
162
197
|
fallback_templates_dir = File.expand_path('.', Dir.pwd)
|
|
163
198
|
template_dir = File.dirname(File.expand_path(template_file))
|
|
164
199
|
template_parent_dir = File.dirname(template_dir)
|
|
165
200
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
[template_parent_dir, template_dir, fallback_templates_dir]
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
site = Sourcerer::Jekyll::Bootstrapper.fake_site(
|
|
173
|
-
includes_load_paths: paths,
|
|
174
|
-
plugin_dirs: [])
|
|
175
|
-
|
|
201
|
+
default_paths = [template_parent_dir, template_dir, fallback_templates_dir]
|
|
202
|
+
paths = includes_load_paths.any? ? includes_load_paths : default_paths
|
|
203
|
+
site = Sourcerer::Jekyll::Bootstrapper.fake_site(includes_load_paths: paths, plugin_dirs: [])
|
|
176
204
|
file_system = Sourcerer::Jekyll::Liquid::FileSystem.new(paths)
|
|
177
205
|
|
|
178
|
-
|
|
179
|
-
options = {
|
|
180
|
-
registers: {
|
|
181
|
-
site: site,
|
|
182
|
-
file_system: file_system
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
template.render(context, options)
|
|
206
|
+
{ site: site, file_system: file_system, includes_load_paths: paths }
|
|
186
207
|
end
|
|
187
208
|
|
|
188
209
|
# Render a Liquid template string directly with a data hash.
|
|
@@ -216,6 +237,8 @@ module Sourcerer
|
|
|
216
237
|
private_class_method :load_render_data,
|
|
217
238
|
:resolve_converter,
|
|
218
239
|
:render_erb,
|
|
219
|
-
:render_liquid
|
|
240
|
+
:render_liquid,
|
|
241
|
+
:render_in_engine,
|
|
242
|
+
:liquid_registers
|
|
220
243
|
end
|
|
221
244
|
end
|
|
@@ -229,7 +229,7 @@ module Sourcerer
|
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
def process_blocks blocks, level, section_id
|
|
232
|
-
# rubocop:disable Metrics/BlockLength
|
|
232
|
+
# rubocop:disable-next Metrics/BlockLength
|
|
233
233
|
blocks.each do |block|
|
|
234
234
|
case block.context
|
|
235
235
|
when :section
|
|
@@ -384,7 +384,6 @@ module Sourcerer
|
|
|
384
384
|
process_blocks(block.blocks, level, section_id) if block.respond_to?(:blocks) && block.blocks.any?
|
|
385
385
|
end
|
|
386
386
|
end
|
|
387
|
-
# rubocop:enable Metrics/BlockLength
|
|
388
387
|
end
|
|
389
388
|
end
|
|
390
389
|
end
|
data/lib/sourcerer/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciisourcerer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- DocOps Lab
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: asciidoctor
|
|
@@ -145,6 +144,7 @@ files:
|
|
|
145
144
|
- lib/sourcerer/jekyll/bootstrapper.rb
|
|
146
145
|
- lib/sourcerer/jekyll/liquid/file_system.rb
|
|
147
146
|
- lib/sourcerer/jekyll/liquid/filters.rb
|
|
147
|
+
- lib/sourcerer/jekyll/liquid/preserve_missing_variables.rb
|
|
148
148
|
- lib/sourcerer/jekyll/liquid/tags.rb
|
|
149
149
|
- lib/sourcerer/jekyll/monkeypatches.rb
|
|
150
150
|
- lib/sourcerer/mark_down_grade.rb
|
|
@@ -172,7 +172,6 @@ licenses:
|
|
|
172
172
|
metadata:
|
|
173
173
|
allowed_push_host: https://rubygems.org
|
|
174
174
|
rubygems_mfa_required: 'true'
|
|
175
|
-
post_install_message:
|
|
176
175
|
rdoc_options: []
|
|
177
176
|
require_paths:
|
|
178
177
|
- lib
|
|
@@ -187,8 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
187
186
|
- !ruby/object:Gem::Version
|
|
188
187
|
version: '0'
|
|
189
188
|
requirements: []
|
|
190
|
-
rubygems_version: 3.
|
|
191
|
-
signing_key:
|
|
189
|
+
rubygems_version: 3.7.2
|
|
192
190
|
specification_version: 4
|
|
193
191
|
summary: APIs for specialized handling of AsciiDoc, YAML, and Liquid documents.
|
|
194
192
|
test_files: []
|