Module: Sourcerer
- Defined in:
- lib/sourcerer.rb,
lib/sourcerer/jekyll.rb,
lib/sourcerer/builder.rb,
lib/sourcerer/templating.rb,
lib/sourcerer/jekyll/liquid/tags.rb,
lib/sourcerer/jekyll/bootstrapper.rb,
lib/sourcerer/plaintext_converter.rb,
lib/sourcerer/jekyll/monkeypatches.rb,
lib/sourcerer/jekyll/liquid/filters.rb,
lib/sourcerer/jekyll/liquid/file_system.rb
Overview
A build-time code generator that creates assets such as new data, documentation, and even Ruby files from data extracted from AsciiDoc files, such as attributes and tagged regions.
Defined Under Namespace
Modules: Builder, Jekyll, Templating Classes: PlainTextConverter
Class Method Summary collapse
-
.extract_commands(file_path, role: 'testable') ⇒ Array<String>
Extracts commands from listing and literal blocks with a specific role.
-
.extract_tagged_content(path_to_tagged_adoc, tag: nil, tags: [], comment_prefix: '// ', comment_suffix: '', skip_comments: false) ⇒ String
Extracts tagged content from a file.
-
.generate_manpage(source_adoc, target_manpage) ⇒ Object
Generates a manpage from an AsciiDoc source file.
-
.load_attributes(path) ⇒ Hash
Loads AsciiDoc attributes from a document header as a Hash.
-
.load_include(path_to_main_adoc, tag: nil, tags: [], leveloffset: nil) ⇒ String
Loads a snippet from an AsciiDoc file using an
include::directive. - .load_render_data(data_file, attrs_source) ⇒ Object
-
.process_block_content(content) ⇒ Array<String>
private
Processes the content of a block to extract commands.
- .render_erb(template_content, context) ⇒ Object
- .render_liquid(template_file, template_content, context, includes_load_paths) ⇒ Object
-
.render_outputs(render_config) ⇒ Object
Renders templates or converter outputs based on a configuration.
-
.render_template(template_file, data_file, out_file, data_object: 'data', includes_load_paths: [], attrs_source: nil, engine: 'liquid') ⇒ Object
Renders a single template with data.
-
.render_templates(templates_config) ⇒ Object
Renders a set of templates based on a configuration.
- .render_with_converter(render_entry) ⇒ Object
- .resolve_converter(converter) ⇒ Object
Class Method Details
.extract_commands(file_path, role: 'testable') ⇒ Array<String>
Extracts commands from listing and literal blocks with a specific role.
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/sourcerer.rb', line 276 def self.extract_commands file_path, role: 'testable' doc = Asciidoctor.load_file(file_path, safe: :unsafe) command_groups = [] current_group = [] blocks = doc.find_by(context: :listing) + doc.find_by(context: :literal) blocks.each do |block| next unless block.has_role?(role) commands = process_block_content(block.content) if block.has_role?('testable-newshell') command_groups << current_group.join("\n") unless current_group.empty? command_groups << commands.join("\n") unless commands.empty? current_group = [] else current_group.concat(commands) end end command_groups << current_group.join("\n") unless current_group.empty? command_groups end |
.extract_tagged_content(path_to_tagged_adoc, tag: nil, tags: [], comment_prefix: '// ', comment_suffix: '', skip_comments: false) ⇒ String
Extracts tagged content from a file.
rubocop:disable Lint/UnusedMethodArgument
69 70 71 72 73 74 75 76 77 78 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 109 110 111 112 113 114 |
# File 'lib/sourcerer.rb', line 69 def self.extract_tagged_content path_to_tagged_adoc, tag: nil, tags: [], comment_prefix: '// ', comment_suffix: '', skip_comments: false # rubocop:enable Lint/UnusedMethodArgument # NOTE: comment_suffix parameter is currently unused but kept for future functionality raise ArgumentError, 'tag and tags cannot coexist' if tag && !.empty? = [tag] if tag raise ArgumentError, 'at least one tag must be specified' if .empty? raise ArgumentError, 'tags must all be strings' unless .is_a?(Array) && .all? { |t| t.is_a?(String) } tagged_content = [] = {} tag_comment_prefix = comment_prefix.strip || '//' tag_pattern = /^#{Regexp.escape(tag_comment_prefix)}\s*tag::([\w-]+)\[\]/ end_pattern = /^#{Regexp.escape(tag_comment_prefix)}\s*end::([\w-]+)\[\]/ comment_line_init_pattern = /^#{Regexp.escape(tag_comment_prefix)}+/ collecting = false File.open(path_to_tagged_adoc, 'r') do |file| file.each_line do |line| # check for tag:: line if line =~ tag_pattern tag_name = Regexp.last_match(1) if .include?(tag_name) collecting = true [tag_name] = true end elsif line =~ end_pattern tag_name = Regexp.last_match(1) if [tag_name] .delete(tag_name) collecting = false if .empty? end elsif collecting tagged_content << line unless skip_comments && line =~ comment_line_init_pattern end end tagged_content = if tagged_content.empty? '' else # return a string of concatenated lines tagged_content.join end end tagged_content end |
.generate_manpage(source_adoc, target_manpage) ⇒ Object
Generates a manpage from an AsciiDoc source file.
120 121 122 123 124 125 126 127 128 |
# File 'lib/sourcerer.rb', line 120 def self.generate_manpage source_adoc, target_manpage FileUtils.mkdir_p File.dirname(target_manpage) Asciidoctor.convert_file( source_adoc, backend: 'manpage', safe: :unsafe, standalone: true, to_file: target_manpage) end |
.load_attributes(path) ⇒ Hash
Loads AsciiDoc attributes from a document header as a Hash.
26 27 28 29 |
# File 'lib/sourcerer.rb', line 26 def self.load_attributes path doc = Asciidoctor.load_file(path, safe: :unsafe) doc.attributes end |
.load_include(path_to_main_adoc, tag: nil, tags: [], leveloffset: nil) ⇒ String
Loads a snippet from an AsciiDoc file using an include:: directive.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/sourcerer.rb', line 38 def self.load_include path_to_main_adoc, tag: nil, tags: [], leveloffset: nil opts = [] opts << "tag=#{tag}" if tag opts << "tags=#{tags.join(',')}" if .any? opts << "leveloffset=#{leveloffset}" if leveloffset snippet_doc = " include::\#{path_to_main_adoc}[\#{opts.join(', ')}]\n ADOC\n\n doc = Asciidoctor.load(\n snippet_doc,\n safe: :unsafe,\n base_dir: File.expand_path('.'),\n header_footer: false,\n attributes: { 'source-highlighter' => nil }) # disable extras\n\n # Get raw text from all top-level blocks\n doc.blocks.map(&:content).join(\"\\n\")\nend\n" |
.load_render_data(data_file, attrs_source) ⇒ Object
212 213 214 215 216 217 218 219 |
# File 'lib/sourcerer.rb', line 212 def self.load_render_data data_file, attrs_source if attrs_source attrs = load_attributes(attrs_source) SchemaGraphy::Loader.load_yaml_with_attributes(data_file, attrs) else SchemaGraphy::Loader.(data_file) end end |
.process_block_content(content) ⇒ Array<String>
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Processes the content of a block to extract commands. It handles line continuations and skips comments.
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/sourcerer.rb', line 305 def self.process_block_content content processed_commands = [] current_command = '' content.each_line do |line| stripped_line = line.strip next if stripped_line.start_with?('#') # Skip comments if stripped_line.end_with?('\\') current_command += "#{stripped_line.chomp('\\')} " else current_command += stripped_line processed_commands << current_command unless current_command.empty? current_command = '' end end processed_commands end |
.render_erb(template_content, context) ⇒ Object
228 229 230 231 |
# File 'lib/sourcerer.rb', line 228 def self.render_erb template_content, context require 'erb' ERB.new(template_content, trim_mode: '-').result_with_hash(context) end |
.render_liquid(template_file, template_content, context, includes_load_paths) ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/sourcerer.rb', line 233 def self.render_liquid template_file, template_content, context, includes_load_paths require_relative 'sourcerer/jekyll' require_relative 'sourcerer/jekyll/liquid/filters' require_relative 'sourcerer/jekyll/liquid/tags' require 'liquid' unless defined?(Liquid::Template) Sourcerer::Jekyll.initialize_liquid_runtime # Determine includes root; add template directory to search paths fallback_templates_dir = File.('.', Dir.pwd) template_dir = File.dirname(File.(template_file)) # For templates that use includes like cfgyml/config-property.adoc.liquid, # we need the parent directory of the template's directory as well template_parent_dir = File.dirname(template_dir) paths = if includes_load_paths.any? includes_load_paths else [template_parent_dir, template_dir, fallback_templates_dir] end # Create a fake Jekyll site site = Sourcerer::Jekyll::Bootstrapper.fake_site( includes_load_paths: paths, plugin_dirs: []) # Setup file system for includes with multiple paths file_system = Sourcerer::Jekyll::Liquid::FileSystem.new(paths) template = Liquid::Template.parse(template_content) = { registers: { site: site, file_system: file_system } } template.render(context, ) end |
.render_outputs(render_config) ⇒ Object
Renders templates or converter outputs based on a configuration.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/sourcerer.rb', line 140 def self.render_outputs render_config return if render_config.nil? || render_config.empty? render_config.each do |render_entry| if render_entry[:converter] render_with_converter(render_entry) next end data_obj = render_entry[:key] || 'data' attrs_source = render_entry[:attrs] engine = render_entry[:engine] || 'liquid' render_template( render_entry[:template], render_entry[:data], render_entry[:out], data_object: data_obj, attrs_source: attrs_source, engine: engine) end end |
.render_template(template_file, data_file, out_file, data_object: 'data', includes_load_paths: [], attrs_source: nil, engine: 'liquid') ⇒ Object
Renders a single template with data.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/sourcerer.rb', line 172 def self.render_template template_file, data_file, out_file, data_object: 'data', includes_load_paths: [], attrs_source: nil, engine: 'liquid' data = load_render_data(data_file, attrs_source) out_file = File.(out_file) FileUtils.mkdir_p(File.dirname(out_file)) template_path = File.(template_file) template_content = File.read(template_path) # Prepare context context = { data_object => data, 'include' => { data_object => data } # for compatibility with {% include ... %} expecting include.var } rendered = case engine.to_s when 'erb' then render_erb(template_content, context) when 'liquid' then render_liquid(template_file, template_content, context, includes_load_paths) else raise ArgumentError, "Unsupported template engine: #{engine}" end File.write(out_file, rendered) end |
.render_templates(templates_config) ⇒ Object
Renders a set of templates based on a configuration.
133 134 135 |
# File 'lib/sourcerer.rb', line 133 def self.render_templates templates_config render_outputs(templates_config) end |
.render_with_converter(render_entry) ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/sourcerer.rb', line 196 def self.render_with_converter render_entry data_file = render_entry[:data] out_file = render_entry[:out] raise ArgumentError, 'render entry missing :data' unless data_file raise ArgumentError, 'render entry missing :out' unless out_file data = load_render_data(data_file, render_entry[:attrs]) converter = resolve_converter(render_entry[:converter]) rendered = converter.call(data, render_entry) raise ArgumentError, 'converter returned non-string output' unless rendered.is_a?(String) out_file = File.(out_file) FileUtils.mkdir_p(File.dirname(out_file)) File.write(out_file, rendered) end |
.resolve_converter(converter) ⇒ Object
221 222 223 224 225 226 |
# File 'lib/sourcerer.rb', line 221 def self.resolve_converter converter return converter if converter.respond_to?(:call) return Object.const_get(converter) if converter.is_a?(String) raise ArgumentError, "Unsupported converter: #{converter.inspect}" end |