Class: Sourcerer::Sync::Cast
- Inherits:
-
Object
- Object
- Sourcerer::Sync::Cast
- Defined in:
- lib/sourcerer/sync/cast.rb
Overview
Synchronizes canonical blocks from a prime template into one target file.
See Sourcerer::Sync for the high-level interface and the project README for usage examples and a full description of the Sync/Cast model.
Defined Under Namespace
Classes: CastResult
Class Method Summary collapse
-
.init(prime_path, target_path, data: {}, dry_run: false) ⇒ CastResult
Bootstrap a new target file from the prime template.
-
.parse_prime_segments(text) ⇒ Object
private
Parse a prime template using the default tag patterns.
- .render_liquid_string(content, data) ⇒ Object private
-
.strip_meta_blocks(text) ⇒ Object
private
Remove every underscore-prefixed meta block (
_skip,_liquid, etc.) from a prime text before it is written to a target during Cast.init. -
.sync(prime_path, target_path, data: {}, canonical_prefix: BlockParser::DEFAULT_CANONICAL_PREFIX, tag_syntax_start: BlockParser::DEFAULT_TAG_SYNTAX_START, tag_syntax_end: BlockParser::DEFAULT_TAG_SYNTAX_END, comment_syntax_patterns: BlockParser::DEFAULT_COMMENT_SYNTAX_PATTERNS, dry_run: false) ⇒ CastResult
Synchronize canonical blocks from ‘prime_path` into `target_path`.
Instance Method Summary collapse
-
#initialize(prime_path, target_path, data:, canonical_prefix:, tag_syntax_start:, tag_syntax_end:, comment_syntax_patterns:, dry_run:) ⇒ Cast
constructor
private
A new instance of Cast.
- #run_sync ⇒ Object private
Constructor Details
#initialize(prime_path, target_path, data:, canonical_prefix:, tag_syntax_start:, tag_syntax_end:, comment_syntax_patterns:, dry_run:) ⇒ Cast
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.
Returns a new instance of Cast.
112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/sourcerer/sync/cast.rb', line 112 def initialize prime_path, target_path, data:, canonical_prefix:, tag_syntax_start:, tag_syntax_end:, comment_syntax_patterns:, dry_run: @prime_path = prime_path @target_path = target_path @data = data @canonical_prefix = canonical_prefix @tag_syntax_start = tag_syntax_start @dry_run = dry_run @tag_patterns = BlockParser.build_tag_patterns( tag_syntax_start, tag_syntax_end, comment_syntax_patterns) end |
Class Method Details
.init(prime_path, target_path, data: {}, dry_run: false) ⇒ CastResult
Bootstrap a new target file from the prime template.
During init the entire prime is rendered through Liquid before writing;
during sync only canonical block content is rendered.
See the project README for a full description of init vs sync semantics.
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 |
# File 'lib/sourcerer/sync/cast.rb', line 73 def self.init prime_path, target_path, data: {}, dry_run: false segments = parse_prime_segments(File.read(prime_path)) liquid_preamble = segments .find { |s| s.is_a?(BlockParser::Block) && s.tag == '_liquid' } &.content.to_s clean_text = segments .reject { |s| s.is_a?(BlockParser::Block) && s.tag.start_with?('_') } .map { |s| s.is_a?(BlockParser::Block) ? "#{s.open_line}#{s.content}#{s.close_line}" : s.content } .join rendered = if data.empty? && liquid_preamble.empty? clean_text else # Wrap the preamble in a silent capture block so the assign statements # populate variables without emitting any whitespace into the output. full = if liquid_preamble.empty? clean_text else "{%- capture __preamble__ -%}#{liquid_preamble}{%- endcapture -%}#{clean_text}" end render_liquid_string(full, data) end unless dry_run FileUtils.mkdir_p(File.dirname(File.(target_path))) File.write(target_path, rendered) end CastResult.new( target_path: target_path, applied_changes: [], warnings: [], errors: [], diff: dry_run ? rendered : nil) end |
.parse_prime_segments(text) ⇒ Object
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.
Parse a prime template using the default tag patterns. Shared by init and strip_meta_blocks to avoid repeating the build_tag_patterns / parse boilerplate.
207 208 209 210 211 212 213 |
# File 'lib/sourcerer/sync/cast.rb', line 207 def self.parse_prime_segments text tag_patterns = BlockParser.build_tag_patterns( BlockParser::DEFAULT_TAG_SYNTAX_START, BlockParser::DEFAULT_TAG_SYNTAX_END, BlockParser::DEFAULT_COMMENT_SYNTAX_PATTERNS) BlockParser.parse(text, canonical_prefix: '', tag_patterns: tag_patterns) end |
.render_liquid_string(content, data) ⇒ Object
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.
180 181 182 183 184 185 186 187 188 189 |
# File 'lib/sourcerer/sync/cast.rb', line 180 def self.render_liquid_string content, data require_relative '../jekyll' require_relative '../jekyll/liquid/filters' require_relative '../jekyll/liquid/tags' require 'liquid' unless defined?(Liquid::Template) Sourcerer::Jekyll.initialize_liquid_runtime template = Liquid::Template.parse(content) template.render(data.transform_keys(&:to_s)) end |
.strip_meta_blocks(text) ⇒ Object
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.
Remove every underscore-prefixed meta block (_skip, _liquid, etc.) from a prime text before it is written to a target during init. These blocks carry template instructions or Liquid context that are only meaningful during the prime→target rendering pass, not in the output file.
196 197 198 199 200 201 |
# File 'lib/sourcerer/sync/cast.rb', line 196 def self. text parse_prime_segments(text) .reject { |s| s.is_a?(BlockParser::Block) && s.tag.start_with?('_') } .map { |s| s.is_a?(BlockParser::Block) ? "#{s.open_line}#{s.content}#{s.close_line}" : s.content } .join end |
.sync(prime_path, target_path, data: {}, canonical_prefix: BlockParser::DEFAULT_CANONICAL_PREFIX, tag_syntax_start: BlockParser::DEFAULT_TAG_SYNTAX_START, tag_syntax_end: BlockParser::DEFAULT_TAG_SYNTAX_END, comment_syntax_patterns: BlockParser::DEFAULT_COMMENT_SYNTAX_PATTERNS, dry_run: false) ⇒ CastResult
Synchronize canonical blocks from ‘prime_path` into `target_path`.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/sourcerer/sync/cast.rb', line 44 def self.sync prime_path, target_path, data: {}, canonical_prefix: BlockParser::DEFAULT_CANONICAL_PREFIX, tag_syntax_start: BlockParser::DEFAULT_TAG_SYNTAX_START, tag_syntax_end: BlockParser::DEFAULT_TAG_SYNTAX_END, comment_syntax_patterns: BlockParser::DEFAULT_COMMENT_SYNTAX_PATTERNS, dry_run: false new( prime_path, target_path, data: data, canonical_prefix: canonical_prefix, tag_syntax_start: tag_syntax_start, tag_syntax_end: tag_syntax_end, comment_syntax_patterns: comment_syntax_patterns, dry_run: dry_run).run_sync end |
Instance Method Details
#run_sync ⇒ Object
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.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/sourcerer/sync/cast.rb', line 127 def run_sync prime_text = File.read(@prime_path) target_text = File.read(@target_path) # Parse with canonical_prefix: '' so that ALL tagged regions -- including # the non-canonical _liquid preamble block -- surface as Block objects # rather than being swallowed into TextSegments. prime_segments = BlockParser.parse( prime_text, canonical_prefix: '', tag_patterns: @tag_patterns) target_segments = BlockParser.parse( target_text, canonical_prefix: '', tag_patterns: @tag_patterns) # Extract the _liquid preamble from the prime (non-canonical; not synced as a # canonical block but used to carry Liquid variable context to all rendered content). prime_liquid_block = prime_segments.find { |s| s.is_a?(BlockParser::Block) && s.tag == '_liquid' } liquid_preamble = prime_liquid_block&.content.to_s prime_blocks = BlockParser.extract_canonical(prime_segments, canonical_prefix: @canonical_prefix) target_blocks, errors = validate_target_canonical(target_segments) if errors.any? return CastResult.new( target_path: @target_path, applied_changes: [], warnings: [], errors: errors, diff: nil) end warnings = collect_warnings(prime_blocks, target_blocks, target_text) new_segments, applied_changes = apply_prime_blocks( target_segments, prime_blocks, prime_liquid_block: prime_liquid_block, liquid_preamble: liquid_preamble) new_text = reconstruct(new_segments) diff = generate_diff(target_text, new_text) if applied_changes.any? || @dry_run File.write(@target_path, new_text) unless @dry_run CastResult.new( target_path: @target_path, applied_changes: @dry_run ? [] : applied_changes, warnings: warnings, errors: [], diff: diff) end |