releasehx 0.1.2 → 0.2.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 +363 -330
- data/build/docs/_config.yml +1 -0
- data/build/docs/_release_index.adoc +3 -2
- data/build/docs/config-reference.adoc +197 -10
- data/build/docs/config-reference.json +56 -7
- data/build/docs/index.adoc +315 -59
- data/build/docs/landing.adoc +1 -1
- data/build/docs/manpage.adoc +2 -2
- data/build/docs/release-procedure.adoc +365 -0
- data/build/docs/release-procedure.html +87 -0
- data/build/docs/releasehx.1 +17 -5
- data/build/docs/sample-config.yml +14 -7
- data/lib/releasehx/cli.rb +5 -2
- data/lib/releasehx/configuration.rb +0 -1
- data/lib/releasehx/generated.rb +1 -1
- data/lib/releasehx/mcp/assets/agent-config-guide.md +1 -1
- data/lib/releasehx/mcp/assets/config-def.yml +122 -6
- data/lib/releasehx/mcp/assets/config-reference.adoc +197 -10
- data/lib/releasehx/mcp/assets/config-reference.json +56 -7
- data/lib/releasehx/mcp/assets/sample-config.yml +14 -7
- data/lib/releasehx/mcp/server.rb +0 -1
- data/lib/releasehx/ops/enrich_ops.rb +161 -55
- data/lib/releasehx/ops/template_ops.rb +1 -1
- data/lib/releasehx/rhyml/adapter.rb +0 -3
- data/lib/releasehx/rhyml/templates/bootstrap-overrides.css +15 -0
- data/lib/releasehx/rhyml/templates/changelog.adoc.liquid +2 -0
- data/lib/releasehx/rhyml/templates/changelog.html.liquid +6 -4
- data/lib/releasehx/rhyml/templates/changelog.md.liquid +1 -0
- data/lib/releasehx/rhyml/templates/embedded.css.liquid +263 -0
- data/lib/releasehx/rhyml/templates/entry.adoc.liquid +1 -0
- data/lib/releasehx/rhyml/templates/entry.html.liquid +21 -20
- data/lib/releasehx/rhyml/templates/entry.md.liquid +15 -21
- data/lib/releasehx/rhyml/templates/head-parser.liquid +6 -2
- data/lib/releasehx/rhyml/templates/header.liquid +13 -4
- data/lib/releasehx/rhyml/templates/history.html.liquid +152 -33
- data/lib/releasehx/rhyml/templates/metadata-entry.adoc.liquid +83 -38
- data/lib/releasehx/rhyml/templates/metadata-entry.html.liquid +60 -1
- data/lib/releasehx/rhyml/templates/metadata-entry.md.liquid +65 -113
- data/lib/releasehx/rhyml/templates/metadata-note.adoc.liquid +83 -38
- data/lib/releasehx/rhyml/templates/metadata-note.html.liquid +59 -22
- data/lib/releasehx/rhyml/templates/metadata-note.md.liquid +68 -23
- data/lib/releasehx/rhyml/templates/note.html.liquid +25 -19
- data/lib/releasehx/rhyml/templates/note.md.liquid +44 -26
- data/lib/releasehx/rhyml/templates/release-notes.adoc.liquid +2 -0
- data/lib/releasehx/rhyml/templates/release-notes.html.liquid +6 -4
- data/lib/releasehx/rhyml/templates/release-notes.md.liquid +1 -0
- data/lib/releasehx/rhyml/templates/release.adoc.liquid +2 -0
- data/lib/releasehx/rhyml/templates/release.md.liquid +8 -7
- data/lib/releasehx/rhyml/templates/rhyml-change.yaml.liquid +36 -36
- data/lib/releasehx/rhyml/templates/wrapper.html.liquid +103 -0
- data/lib/releasehx/sgyml/helpers.rb +0 -2
- data/lib/releasehx/transforms/adf_to_markdown.rb +1 -1
- data/lib/releasehx/version.rb +0 -2
- data/lib/releasehx.rb +2 -2
- data/specs/data/config-def.yml +122 -6
- metadata +48 -25
- data/build/docs/schemagraphy_readme.html +0 -0
- data/build/docs/sourcerer_readme.html +0 -46
- data/lib/schemagraphy/attribute_resolver.rb +0 -48
- data/lib/schemagraphy/cfgyml/definition.rb +0 -90
- data/lib/schemagraphy/cfgyml/doc_builder.rb +0 -52
- data/lib/schemagraphy/cfgyml/path_reference.rb +0 -24
- data/lib/schemagraphy/data_query/json_pointer.rb +0 -42
- data/lib/schemagraphy/loader.rb +0 -59
- data/lib/schemagraphy/regexp_utils.rb +0 -235
- data/lib/schemagraphy/safe_expression.rb +0 -189
- data/lib/schemagraphy/schema_utils.rb +0 -124
- data/lib/schemagraphy/tag_utils.rb +0 -32
- data/lib/schemagraphy/templating.rb +0 -104
- data/lib/schemagraphy.rb +0 -17
- data/lib/sourcerer/builder.rb +0 -120
- data/lib/sourcerer/jekyll/bootstrapper.rb +0 -78
- data/lib/sourcerer/jekyll/liquid/file_system.rb +0 -74
- data/lib/sourcerer/jekyll/liquid/filters.rb +0 -215
- data/lib/sourcerer/jekyll/liquid/tags.rb +0 -44
- data/lib/sourcerer/jekyll/monkeypatches.rb +0 -73
- data/lib/sourcerer/jekyll.rb +0 -26
- data/lib/sourcerer/plaintext_converter.rb +0 -75
- data/lib/sourcerer/templating.rb +0 -190
- data/lib/sourcerer.rb +0 -322
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'liquid'
|
|
4
|
-
|
|
5
|
-
module Sourcerer
|
|
6
|
-
module Jekyll
|
|
7
|
-
module Liquid
|
|
8
|
-
# A custom Liquid file system that extends `Liquid::LocalFileSystem` to support
|
|
9
|
-
# multiple root paths for template lookups. This allows templates to be
|
|
10
|
-
# resolved from a prioritized list of directories.
|
|
11
|
-
class FileSystem < ::Liquid::LocalFileSystem
|
|
12
|
-
# Initializes the file system with one or more root paths.
|
|
13
|
-
#
|
|
14
|
-
# @param roots_or_root [String, Array<String>] A single root path or an array of root paths.
|
|
15
|
-
# rubocop:disable Lint/MissingSuper
|
|
16
|
-
# Intentional: Custom implementation that doesn't need parent's initialization
|
|
17
|
-
def initialize roots_or_root
|
|
18
|
-
if roots_or_root.is_a?(Array)
|
|
19
|
-
@roots = roots_or_root.map { |root| File.expand_path(root) }
|
|
20
|
-
@multi_root = true
|
|
21
|
-
else
|
|
22
|
-
@root = File.expand_path(roots_or_root)
|
|
23
|
-
@multi_root = false
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
# rubocop:enable Lint/MissingSuper
|
|
27
|
-
|
|
28
|
-
# Finds the full path of a template, searching through multiple roots if configured.
|
|
29
|
-
#
|
|
30
|
-
# @param template_path [String] The path to the template.
|
|
31
|
-
# @return [String] The full, validated path to the template.
|
|
32
|
-
# @raise [Liquid::FileSystemError] if the template is not found.
|
|
33
|
-
def full_path template_path
|
|
34
|
-
if @multi_root
|
|
35
|
-
@roots.each do |root|
|
|
36
|
-
full = File.expand_path(File.join(root, template_path))
|
|
37
|
-
return full if File.exist?(full) && full.start_with?(root)
|
|
38
|
-
end
|
|
39
|
-
raise ::Liquid::FileSystemError, "Template not found: '#{template_path}' in paths: #{@roots}"
|
|
40
|
-
else
|
|
41
|
-
full = File.expand_path(File.join(@root, template_path))
|
|
42
|
-
validate_path(full)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Reads the content of a template file.
|
|
47
|
-
#
|
|
48
|
-
# @param template_path [String] The path to the template.
|
|
49
|
-
# @return [String] The content of the template file.
|
|
50
|
-
def read_template_file template_path
|
|
51
|
-
path = full_path(template_path)
|
|
52
|
-
File.read(path)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
|
-
# Validates that the resolved path is within the allowed root(s).
|
|
58
|
-
def validate_path path
|
|
59
|
-
if @multi_root
|
|
60
|
-
# Check if path starts with any of the allowed roots
|
|
61
|
-
unless @roots.any? { |root| path.start_with?(root) }
|
|
62
|
-
raise ::Liquid::FileSystemError, "Illegal template path '#{path}'"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
else
|
|
66
|
-
raise ::Liquid::FileSystemError, "Illegal template path '#{path}'" unless path.start_with?(@root)
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
path
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'kramdown-asciidoc'
|
|
4
|
-
require 'base64'
|
|
5
|
-
require 'cgi'
|
|
6
|
-
|
|
7
|
-
module Sourcerer
|
|
8
|
-
module Jekyll
|
|
9
|
-
module Liquid
|
|
10
|
-
# This module provides a set of custom filters for use in Liquid templates.
|
|
11
|
-
module Filters
|
|
12
|
-
# Renders a Liquid template string with a given scope.
|
|
13
|
-
# @param input [String, Object] The Liquid template string or a pre-parsed template object.
|
|
14
|
-
# @param vars [Hash] A hash of variables to use as the scope.
|
|
15
|
-
# @return [String] The rendered output.
|
|
16
|
-
def render input, vars = nil
|
|
17
|
-
scope = if vars.is_a?(Hash)
|
|
18
|
-
vars.transform_keys(&:to_s)
|
|
19
|
-
else
|
|
20
|
-
{}
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
template =
|
|
24
|
-
if input.respond_to?(:render) && input.respond_to?(:templated?) && input.templated?
|
|
25
|
-
input
|
|
26
|
-
else
|
|
27
|
-
::Liquid::Template.parse(input.to_s)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
template.render(scope)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Converts a string into a slug.
|
|
34
|
-
# @param input [String] The string to convert.
|
|
35
|
-
# @param format [String] The desired format (`kebab`, `snake`, `camel`, `pascal`).
|
|
36
|
-
# @return [String] The sluggerized string.
|
|
37
|
-
def sluggerize input, format = 'kebab'
|
|
38
|
-
return input unless input.is_a? String
|
|
39
|
-
|
|
40
|
-
case format
|
|
41
|
-
when 'kebab' then input.downcase.gsub(/[\s\-_]/, '-')
|
|
42
|
-
when 'snake' then input.downcase.gsub(/[\s\-_]/, '_')
|
|
43
|
-
when 'camel' then input.downcase.gsub(/[\s\-_]/, '_').camelize(:lower)
|
|
44
|
-
when 'pascal' then input.downcase.gsub(/[\s\-_]/, '_').camelize(:upper)
|
|
45
|
-
else input
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Replaces double newlines with a newline and a plus sign.
|
|
50
|
-
# @param input [String] The input string.
|
|
51
|
-
# @return [String] The processed string.
|
|
52
|
-
def plusify input
|
|
53
|
-
input.gsub(/\n\n+/, "\n+\n")
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Converts a Markdown string to AsciiDoc.
|
|
57
|
-
# @param input [String] The Markdown string.
|
|
58
|
-
# @param wrap [String] The wrapping option for the converter.
|
|
59
|
-
# @return [String] The converted AsciiDoc string.
|
|
60
|
-
def md_to_adoc input, wrap = 'ventilate'
|
|
61
|
-
options = {}
|
|
62
|
-
options[:wrap] = wrap.to_sym if wrap
|
|
63
|
-
Kramdoc.convert(input, options)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Indents a string by a given number of spaces.
|
|
67
|
-
# @param input [String] The string to indent.
|
|
68
|
-
# @param spaces [Integer] The number of spaces for indentation.
|
|
69
|
-
# @param line1 [Boolean] Whether to indent the first line.
|
|
70
|
-
# @return [String] The indented string.
|
|
71
|
-
def indent input, spaces = 2, line1: false
|
|
72
|
-
indent = ' ' * spaces
|
|
73
|
-
lines = input.split("\n")
|
|
74
|
-
indented = if line1
|
|
75
|
-
lines.map { |line| indent + line }
|
|
76
|
-
else
|
|
77
|
-
lines.map.with_index { |line, i| i.zero? ? line : indent + line }
|
|
78
|
-
end
|
|
79
|
-
indented.join("\n")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Checks the type of a value in the context of SG-YML.
|
|
83
|
-
# @param input [Object] The value to check.
|
|
84
|
-
# @return [String] A string representing the type.
|
|
85
|
-
def sgyml_type_check input
|
|
86
|
-
if input.nil?
|
|
87
|
-
'Null:nil'
|
|
88
|
-
elsif input.is_a? Array
|
|
89
|
-
# if all items in Array are (integer, float, string, boolean)
|
|
90
|
-
if input.all? do |item|
|
|
91
|
-
item.is_a?(Integer) || item.is_a?(Float) || item.is_a?(String) ||
|
|
92
|
-
item.is_a?(TrueClass) || item.is_a?(FalseClass)
|
|
93
|
-
end
|
|
94
|
-
'Compound:ArrayList'
|
|
95
|
-
elsif input.all? { |item| item.is_a?(Hash) && (item.keys.length >= 2) }
|
|
96
|
-
'Compound:ArrayTable'
|
|
97
|
-
else
|
|
98
|
-
'Compound:Array'
|
|
99
|
-
end
|
|
100
|
-
elsif input.is_a? Hash
|
|
101
|
-
if input.values.all? { |value| value.is_a?(Hash) && (value.keys.length >= 2) }
|
|
102
|
-
'Compound:MapTable'
|
|
103
|
-
else
|
|
104
|
-
'Compound:Map'
|
|
105
|
-
end
|
|
106
|
-
elsif input.is_a? String
|
|
107
|
-
'Scalar:String'
|
|
108
|
-
elsif input.is_a? Integer
|
|
109
|
-
'Scalar:Number'
|
|
110
|
-
elsif input.is_a? Time
|
|
111
|
-
'Scalar:DateTime'
|
|
112
|
-
elsif input.is_a? Float
|
|
113
|
-
'Scalar:Float'
|
|
114
|
-
elsif input.is_a?(TrueClass) || input.is_a?(FalseClass)
|
|
115
|
-
'Scalar:Boolean'
|
|
116
|
-
else
|
|
117
|
-
'unknown:unknown'
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Returns the Ruby class name of a value.
|
|
122
|
-
# @param input [Object] The value.
|
|
123
|
-
# @return [String] The class name.
|
|
124
|
-
def ruby_class input
|
|
125
|
-
input.class.name
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Removes markup from a string.
|
|
129
|
-
# @param input [String] The string to demarkupify.
|
|
130
|
-
# @return [String] The demarkupified string.
|
|
131
|
-
def demarkupify input
|
|
132
|
-
return input unless input.is_a? String
|
|
133
|
-
|
|
134
|
-
input = input.gsub(/`"|"`/, '"')
|
|
135
|
-
input = input.gsub(/'`|`'/, "'")
|
|
136
|
-
input = input.gsub(/[*_`]/, '')
|
|
137
|
-
# change curly quotes to striaght quotes
|
|
138
|
-
input = input.gsub(/[“”]/, '"')
|
|
139
|
-
input.gsub(/[‘’]/, "'")
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Dumps a value to YAML format.
|
|
143
|
-
# @param input [Object] The value to dump.
|
|
144
|
-
# @return [String] The YAML representation.
|
|
145
|
-
def inspect_yaml input
|
|
146
|
-
require 'yaml'
|
|
147
|
-
YAML.dump(input)
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Base64 encodes a string.
|
|
151
|
-
# @param input [String] The string to encode.
|
|
152
|
-
# @return [String] The Base64-encoded string.
|
|
153
|
-
def base64 input
|
|
154
|
-
return input unless input.is_a? String
|
|
155
|
-
|
|
156
|
-
Base64.strict_encode64(input)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Decodes a Base64-encoded string.
|
|
160
|
-
# @param input [String] The string to decode.
|
|
161
|
-
# @return [String] The decoded string.
|
|
162
|
-
def base64_decode input
|
|
163
|
-
return input unless input.is_a? String
|
|
164
|
-
|
|
165
|
-
Base64.strict_decode64(input)
|
|
166
|
-
rescue ArgumentError
|
|
167
|
-
# Return original input if decoding fails
|
|
168
|
-
input
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# URL-encodes a string.
|
|
172
|
-
# @param input [String] The string to encode.
|
|
173
|
-
# @return [String] The URL-encoded string.
|
|
174
|
-
def url_encode input
|
|
175
|
-
return input unless input.is_a? String
|
|
176
|
-
|
|
177
|
-
CGI.escape(input)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Decodes a URL-encoded string.
|
|
181
|
-
# @param input [String] The string to decode.
|
|
182
|
-
# @return [String] The decoded string.
|
|
183
|
-
def url_decode input
|
|
184
|
-
return input unless input.is_a? String
|
|
185
|
-
|
|
186
|
-
CGI.unescape(input)
|
|
187
|
-
rescue ArgumentError
|
|
188
|
-
# Return original input if decoding fails
|
|
189
|
-
input
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# HTML-escapes a string.
|
|
193
|
-
# @param input [String] The string to escape.
|
|
194
|
-
# @return [String] The HTML-escaped string.
|
|
195
|
-
def html_escape input
|
|
196
|
-
return input unless input.is_a? String
|
|
197
|
-
|
|
198
|
-
CGI.escapeHTML(input)
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Unescapes an HTML-escaped string.
|
|
202
|
-
# @param input [String] The string to unescape.
|
|
203
|
-
# @return [String] The unescaped string.
|
|
204
|
-
def html_unescape input
|
|
205
|
-
return input unless input.is_a? String
|
|
206
|
-
|
|
207
|
-
CGI.unescapeHTML(input)
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# Register the filters automatically
|
|
215
|
-
Liquid::Template.register_filter(Sourcerer::Jekyll::Liquid::Filters)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sourcerer
|
|
4
|
-
module Jekyll
|
|
5
|
-
# This module contains custom Liquid tags for the Sourcerer templating environment.
|
|
6
|
-
module Tags
|
|
7
|
-
# A Liquid tag for embedding and rendering a file within a template.
|
|
8
|
-
# It searches for the file in the configured include paths.
|
|
9
|
-
class EmbedTag < ::Liquid::Tag
|
|
10
|
-
# @param tag_name [String] The name of the tag ('embed').
|
|
11
|
-
# @param markup [String] The name of the partial to embed.
|
|
12
|
-
# @param tokens [Array<String>] The list of tokens.
|
|
13
|
-
def initialize tag_name, markup, tokens
|
|
14
|
-
super
|
|
15
|
-
@partial_name = markup.strip
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Renders the embedded file.
|
|
19
|
-
#
|
|
20
|
-
# @param context [Liquid::Context] The Liquid context.
|
|
21
|
-
# @return [String] The rendered content of the embedded file.
|
|
22
|
-
# @raise [IOError] if the embed file is not found.
|
|
23
|
-
def render context
|
|
24
|
-
includes_paths = context.registers[:includes_load_paths] || []
|
|
25
|
-
|
|
26
|
-
found_path = includes_paths.find do |base|
|
|
27
|
-
candidate = File.expand_path(@partial_name, base)
|
|
28
|
-
File.exist?(candidate)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
raise "Embed file not found: #{@partial_name}" unless found_path
|
|
32
|
-
|
|
33
|
-
full_path = File.expand_path(@partial_name, found_path)
|
|
34
|
-
source = File.read(full_path)
|
|
35
|
-
|
|
36
|
-
partial = ::Liquid::Template.parse(source)
|
|
37
|
-
partial.render!(context)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
Liquid::Template.register_tag('embed', Sourcerer::Jekyll::Tags::EmbedTag)
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sourcerer
|
|
4
|
-
module Jekyll
|
|
5
|
-
# This module contains monkeypatches for Jekyll to modify or extend its behavior.
|
|
6
|
-
module Monkeypatches
|
|
7
|
-
# Patches Jekyll's `OptimizedIncludeTag` to modify its behavior.
|
|
8
|
-
# The patch enhances include path resolution and context handling to better
|
|
9
|
-
# suit the needs of Sourcerer's templating environment.
|
|
10
|
-
def self.patch_jekyll
|
|
11
|
-
return unless defined?(::Jekyll::Tags::OptimizedIncludeTag)
|
|
12
|
-
|
|
13
|
-
::Jekyll::Tags::OptimizedIncludeTag.class_eval do
|
|
14
|
-
define_method :render do |context|
|
|
15
|
-
site = context.registers[:site]
|
|
16
|
-
file = render_variable(context) || @file
|
|
17
|
-
|
|
18
|
-
context.stack do
|
|
19
|
-
context['include'] = parse_params(context) if @params
|
|
20
|
-
|
|
21
|
-
source = site.inclusions[file]
|
|
22
|
-
|
|
23
|
-
unless source
|
|
24
|
-
|
|
25
|
-
# Debug lines before attempting path resolution
|
|
26
|
-
|
|
27
|
-
# Safe resolution
|
|
28
|
-
paths = context.registers[:includes_load_paths] || []
|
|
29
|
-
path = paths
|
|
30
|
-
.map { |dir| File.join(dir, file) }
|
|
31
|
-
.find { |p| File.file?(p) }
|
|
32
|
-
|
|
33
|
-
raise IOError, "Include file not found: #{file}" unless path
|
|
34
|
-
|
|
35
|
-
source = File.read(path)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
partial = ::Liquid::Template.parse(source)
|
|
39
|
-
partial.registers[:site] = context.registers[:site]
|
|
40
|
-
partial.assigns['include'] = context['include']
|
|
41
|
-
|
|
42
|
-
::Liquid::Template.register_filter(::Jekyll::Filters)
|
|
43
|
-
::Liquid::Template.register_filter(::Sourcerer::Jekyll::Liquid::Filters)
|
|
44
|
-
|
|
45
|
-
# Use an isolated context so we can inspect and copy assigns
|
|
46
|
-
subcontext = ::Liquid::Context.new(
|
|
47
|
-
[{ 'include' => context['include'] }],
|
|
48
|
-
{}, # Environments
|
|
49
|
-
context.registers,
|
|
50
|
-
rethrow_errors: true)
|
|
51
|
-
|
|
52
|
-
rendered = partial.render!(subcontext)
|
|
53
|
-
|
|
54
|
-
# Copy assigns from subcontext to parent context
|
|
55
|
-
subcontext.environments.each do |env|
|
|
56
|
-
env.each do |k, v|
|
|
57
|
-
# Avoid clobbering outer include if reentrant
|
|
58
|
-
next if k == 'include'
|
|
59
|
-
|
|
60
|
-
context.environments.first[k] = v
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
rendered
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
::Liquid::Template.tags['include'] = ::Jekyll::Tags::OptimizedIncludeTag
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
data/lib/sourcerer/jekyll.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'jekyll/bootstrapper'
|
|
4
|
-
require_relative 'jekyll/monkeypatches'
|
|
5
|
-
require_relative 'jekyll/liquid/file_system'
|
|
6
|
-
require_relative 'jekyll/liquid/filters'
|
|
7
|
-
require_relative 'jekyll/liquid/tags'
|
|
8
|
-
require 'jekyll-asciidoc'
|
|
9
|
-
|
|
10
|
-
module Sourcerer
|
|
11
|
-
# This module encapsulates the logic for initializing a Jekyll-like Liquid
|
|
12
|
-
# templating environment. It loads necessary plugins, applies monkeypatches,
|
|
13
|
-
# and registers custom Liquid filters and tags.
|
|
14
|
-
module Jekyll
|
|
15
|
-
# Initializes the Liquid templating runtime by loading plugins,
|
|
16
|
-
# applying patches, and registering custom filters.
|
|
17
|
-
def self.initialize_liquid_runtime
|
|
18
|
-
Bootstrapper.load_plugins
|
|
19
|
-
Monkeypatches.patch_jekyll
|
|
20
|
-
# Ensure Sourcerer filters are registered
|
|
21
|
-
::Liquid::Template.register_filter(::Sourcerer::Jekyll::Liquid::Filters)
|
|
22
|
-
# Ensure jekyll-asciidoc filters are registered
|
|
23
|
-
# ::Liquid::Template.register_filter(Jekyll::AsciiDoc::Filters)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# This module will likely spin off into a gem
|
|
2
|
-
|
|
3
|
-
# frozen_string_literal: true
|
|
4
|
-
|
|
5
|
-
require 'asciidoctor'
|
|
6
|
-
|
|
7
|
-
module Sourcerer
|
|
8
|
-
# A custom Asciidoctor converter that outputs plain text.
|
|
9
|
-
# It is registered for the "plaintext" backend and can be used to extract
|
|
10
|
-
# the raw text content or attributes from an AsciiDoc document.
|
|
11
|
-
class PlainTextConverter < Asciidoctor::Converter::Base
|
|
12
|
-
# Identify ourselves as a converter for the "plaintext" backend
|
|
13
|
-
register_for 'plaintext'
|
|
14
|
-
|
|
15
|
-
# The main entry point for the converter.
|
|
16
|
-
# It is called by Asciidoctor to convert a node.
|
|
17
|
-
#
|
|
18
|
-
# @param node [Asciidoctor::AbstractNode] The node to convert.
|
|
19
|
-
# @param _transform [String] The transform to apply (unused).
|
|
20
|
-
# @param _opts [Hash] Options for the conversion (unused).
|
|
21
|
-
# @return [String] The converted plain text output.
|
|
22
|
-
def convert node, _transform = nil, _opts = {}
|
|
23
|
-
if respond_to?("convert_#{node.node_name}", true)
|
|
24
|
-
send("convert_#{node.node_name}", node)
|
|
25
|
-
elsif node.respond_to?(:content)
|
|
26
|
-
node.content.to_s
|
|
27
|
-
elsif node.respond_to?(:text)
|
|
28
|
-
node.text.to_s
|
|
29
|
-
else
|
|
30
|
-
''
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
# Converts the document node.
|
|
37
|
-
def convert_document node
|
|
38
|
-
emit_attrs = node.attr('sourcerer_mode') == 'emit_attrs'
|
|
39
|
-
|
|
40
|
-
if emit_attrs
|
|
41
|
-
# only emit attribute lines directly, nothing else
|
|
42
|
-
attrs = node.attributes.select do |k, v|
|
|
43
|
-
k.is_a?(String) && !v.nil? && !k.start_with?('backend-', 'safe-mode', 'doctype', 'sourcerer_mode')
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
formatted_attrs = attrs.map { |k, v| ":#{k}: #{v}" }
|
|
47
|
-
formatted_attrs.join("\n") # NO EXTRA SPACES OR LINES
|
|
48
|
-
else
|
|
49
|
-
node.blocks.map { |block| convert block }.join("\n")
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Converts a section node.
|
|
54
|
-
def convert_section node
|
|
55
|
-
title = node.title? ? node.title : ''
|
|
56
|
-
body = node.blocks.map { |block| convert block }.join("\n")
|
|
57
|
-
[title, body].reject(&:empty?).join("\n")
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Converts a paragraph node.
|
|
61
|
-
def convert_paragraph node
|
|
62
|
-
node.lines.join("\n")
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Converts a listing node.
|
|
66
|
-
def convert_listing node
|
|
67
|
-
node.content
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Converts a literal node.
|
|
71
|
-
def convert_literal node
|
|
72
|
-
node.content
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
data/lib/sourcerer/templating.rb
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'liquid'
|
|
4
|
-
|
|
5
|
-
module Sourcerer
|
|
6
|
-
# This module provides the core templating functionality for Sourcerer.
|
|
7
|
-
# It includes modules for template engines, and classes for representing
|
|
8
|
-
# templated fields and their context.
|
|
9
|
-
module Templating
|
|
10
|
-
# This module handles the compilation and rendering of templates.
|
|
11
|
-
module Engines
|
|
12
|
-
module_function
|
|
13
|
-
|
|
14
|
-
# A hash of supported template engines.
|
|
15
|
-
SUPPORTED_ENGINES = {
|
|
16
|
-
'liquid' => 'liquid',
|
|
17
|
-
'erb' => 'erb'
|
|
18
|
-
}.freeze
|
|
19
|
-
|
|
20
|
-
# Compiles a template string using the specified engine.
|
|
21
|
-
#
|
|
22
|
-
# @param str [String] The template string to compile.
|
|
23
|
-
# @param engine [String] The name of the template engine to use.
|
|
24
|
-
# @return [Object] The compiled template object.
|
|
25
|
-
# @raise [ArgumentError] if the engine is not supported.
|
|
26
|
-
def compile str, engine
|
|
27
|
-
case engine.to_s
|
|
28
|
-
when 'liquid'
|
|
29
|
-
Liquid::Template.parse(str)
|
|
30
|
-
when 'erb'
|
|
31
|
-
require 'erb'
|
|
32
|
-
ERB.new(str)
|
|
33
|
-
else
|
|
34
|
-
raise ArgumentError, "Unsupported engine: #{engine}"
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Renders a compiled template with the given variables.
|
|
39
|
-
#
|
|
40
|
-
# @param compiled [Object] The compiled template object.
|
|
41
|
-
# @param engine [String] The name of the template engine.
|
|
42
|
-
# @param vars [Hash] A hash of variables to use for rendering.
|
|
43
|
-
# @return [String] The rendered output.
|
|
44
|
-
def render compiled, engine, vars = {}
|
|
45
|
-
case engine.to_s
|
|
46
|
-
when 'liquid'
|
|
47
|
-
compiled.render(vars)
|
|
48
|
-
when 'erb'
|
|
49
|
-
compiled.result_with_hash(vars)
|
|
50
|
-
else
|
|
51
|
-
compiled.to_s
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Represents a field that will be rendered by a template engine.
|
|
57
|
-
class TemplatedField
|
|
58
|
-
# @return [String] The raw, un-rendered template string.
|
|
59
|
-
attr_reader :raw
|
|
60
|
-
# @return [Object] The compiled template object.
|
|
61
|
-
attr_reader :compiled
|
|
62
|
-
# @return [String] The name of the template engine.
|
|
63
|
-
attr_reader :engine
|
|
64
|
-
# @return [Boolean] Whether the template was explicitly tagged.
|
|
65
|
-
attr_reader :tagged
|
|
66
|
-
# @return [Boolean] Whether the template engine was inferred.
|
|
67
|
-
attr_reader :inferred
|
|
68
|
-
|
|
69
|
-
# @param raw [String] The raw template string.
|
|
70
|
-
# @param compiled [Object] The compiled template object.
|
|
71
|
-
# @param engine [String] The name of the template engine.
|
|
72
|
-
# @param tagged [Boolean] Whether the template was explicitly tagged.
|
|
73
|
-
# @param inferred [Boolean] Whether the template engine was inferred.
|
|
74
|
-
def initialize raw, compiled, engine, tagged, inferred
|
|
75
|
-
@raw = raw
|
|
76
|
-
@compiled = compiled
|
|
77
|
-
@engine = engine
|
|
78
|
-
@tagged = tagged
|
|
79
|
-
@inferred = inferred
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# @return [true] Always returns true to indicate this is a templated field.
|
|
83
|
-
def templated?
|
|
84
|
-
true
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# @return [Boolean] True if the field is deferred (not yet compiled).
|
|
88
|
-
def deferred?
|
|
89
|
-
compiled.nil?
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# @return [self] Returns self for Liquid compatibility.
|
|
93
|
-
def to_liquid
|
|
94
|
-
self
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Renders the template with the given context.
|
|
98
|
-
# @param context [Hash, Liquid::Context] The context for rendering.
|
|
99
|
-
# @return [String] The rendered output.
|
|
100
|
-
def render context = {}
|
|
101
|
-
scope = context.respond_to?(:environments) ? context.environments.first : context
|
|
102
|
-
Engines.render(compiled, engine, scope)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Renders the template with an empty context.
|
|
106
|
-
# @return [String] The rendered output.
|
|
107
|
-
def to_s
|
|
108
|
-
render({})
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Holds contextual information for templating.
|
|
113
|
-
class Context
|
|
114
|
-
# @return [Symbol] The rendering stage (e.g., `:load`).
|
|
115
|
-
attr_reader :stage
|
|
116
|
-
# @return [Boolean] Whether to use strict rendering.
|
|
117
|
-
attr_reader :strict
|
|
118
|
-
# @return [Hash] A hash of scopes for rendering.
|
|
119
|
-
attr_reader :scopes
|
|
120
|
-
|
|
121
|
-
# @param stage [Symbol] The rendering stage.
|
|
122
|
-
# @param strict [Boolean] Whether to use strict rendering.
|
|
123
|
-
# @param scopes [Hash] A hash of scopes.
|
|
124
|
-
def initialize stage: :load, strict: false, scopes: {}
|
|
125
|
-
@stage = stage.to_sym
|
|
126
|
-
@strict = strict
|
|
127
|
-
@scopes = scopes.transform_keys(&:to_sym)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Creates a new Context object from a schema fragment.
|
|
131
|
-
# @param schema_fragment [Hash] The schema fragment containing templating info.
|
|
132
|
-
# @return [Context] The new Context object.
|
|
133
|
-
def self.from_schema schema_fragment
|
|
134
|
-
render_conf = schema_fragment['templating'] || {}
|
|
135
|
-
|
|
136
|
-
stage = (render_conf['stage'] || :load).to_sym
|
|
137
|
-
strict = render_conf['strict'] == true
|
|
138
|
-
scopes = (render_conf['scopes'] || {}).transform_keys(&:to_sym)
|
|
139
|
-
|
|
140
|
-
new(stage: stage, strict: strict, scopes: scopes)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Merges all scopes into a single hash.
|
|
144
|
-
# @return [Hash] The merged scope.
|
|
145
|
-
def merged_scope
|
|
146
|
-
scopes.values.reduce({}) { |acc, s| acc.merge(s) }
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Compiles templated fields in a data structure.
|
|
151
|
-
# @param data [Hash] The data to process.
|
|
152
|
-
# @param schema [Hash] The schema defining the fields.
|
|
153
|
-
# @param fields [Array<Hash>] The fields to compile.
|
|
154
|
-
# @param scope [Hash] The scope for rendering.
|
|
155
|
-
def self.compile_templated_fields! data:, schema:, fields:, scope: {}
|
|
156
|
-
fields.each do |field_entry|
|
|
157
|
-
key = field_entry[:key]
|
|
158
|
-
path = field_entry[:path]
|
|
159
|
-
val = data[key]
|
|
160
|
-
|
|
161
|
-
next unless val.is_a?(String) || (val.is_a?(Hash) && val['__tag__'] && val['value'])
|
|
162
|
-
|
|
163
|
-
raw = val.is_a?(Hash) ? val['value'] : val
|
|
164
|
-
tagged = val.is_a?(Hash)
|
|
165
|
-
config = SchemaGraphy::SchemaUtils.templating_config_for(schema, path)
|
|
166
|
-
engine = tagged ? val['__tag__'] : (config['default'] || 'liquid')
|
|
167
|
-
|
|
168
|
-
compiled = Engines.compile(raw, engine)
|
|
169
|
-
|
|
170
|
-
data[key] = if config['delay']
|
|
171
|
-
TemplatedField.new(raw, compiled, engine, tagged, inferred: !tagged)
|
|
172
|
-
else
|
|
173
|
-
Engines.render(compiled, engine, scope)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# Renders a field if it is a template.
|
|
179
|
-
# @param val [Object] The value to render.
|
|
180
|
-
# @param context [Hash] The context for rendering.
|
|
181
|
-
# @return [Object] The rendered value, or the original value if not a template.
|
|
182
|
-
def self.render_field_if_template val, context = {}
|
|
183
|
-
if val.respond_to?(:templated?) && val.templated?
|
|
184
|
-
val.render(context)
|
|
185
|
-
else
|
|
186
|
-
val
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|