Class: Textus::Produce::Acquire::Projection
- Inherits:
-
Object
- Object
- Textus::Produce::Acquire::Projection
- Defined in:
- lib/textus/produce/acquire/projection.rb
Overview
Builds an entry’s DATA artifact (ADR 0094) by running the projection pipeline; rendering is a publish concern. External entries are NOT built here — they are generated by an out-of-band runner; Derived#publish_via filters them out before reaching this point.
Merges the former Write::DataBuilder wrapper and Builder::Pipeline module into one class (ADR 0100 produce/ topology refactor).
Defined Under Namespace
Modules: InjectMeta Classes: Deps
Class Method Summary collapse
- .pipeline_run(mentry:, deps:) ⇒ Object
- .renderers ⇒ Object
-
.write_if_changed(target_path, bytes, _format) ⇒ Object
Built artifacts are content-addressed (no volatile timestamp, ADR 0070), so identity is plain byte-equality: skip the write when nothing changed.
Instance Method Summary collapse
-
#initialize(container:, call:) ⇒ Projection
constructor
A new instance of Projection.
-
#run(mentry) ⇒ Object
Runs the projection pipeline for ‘mentry` and returns the on-disk target_path string.
Constructor Details
#initialize(container:, call:) ⇒ Projection
Returns a new instance of Projection.
47 48 49 50 51 52 53 54 |
# File 'lib/textus/produce/acquire/projection.rb', line 47 def initialize(container:, call:) @container = container @call = call @manifest = container.manifest @file_store = container.file_store @rpc = container.rpc @root = container.root end |
Class Method Details
.pipeline_run(mentry:, deps:) ⇒ Object
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/textus/produce/acquire/projection.rb', line 81 def self.pipeline_run(mentry:, deps:) # 1. Load sources + project + reduce. Only projection-derived entries are # buildable in-process; External entries are generated out-of-band and are # filtered out upstream (Derived#publish_via), so reaching here with a # non-projection source is a wiring bug — fail loudly rather than emit an # empty payload (and never re-stamp the volatile generated_at, ADR 0070). unless mentry.projection? raise UsageError.new( "builder: '#{mentry.key}' is not a projection-derived entry; only projections are buildable", ) end data = Textus::Projection.new( reader: deps.reader, spec: mentry.source.projection_spec, lister: deps.lister, rpc: deps.rpc, transform_context: deps.transform_context, ).run # 2. Serialize as DATA. Rendering through a template is a publish concern # (ADR 0094) — the build never consults a template. klass = renderers[mentry.format] or raise UsageError.new("builder: unsupported data format #{mentry.format.inspect} for '#{mentry.key}'") bytes = klass.new.call(mentry: mentry, data: data) # 3. Write (idempotent: skip if only generated_at would differ) target_path = Key::Path.resolve(deps.manifest.data, mentry) FileUtils.mkdir_p(File.dirname(target_path)) write_if_changed(target_path, bytes, mentry.format) target_path end |
.renderers ⇒ Object
39 40 41 42 43 44 45 |
# File 'lib/textus/produce/acquire/projection.rb', line 39 def self.renderers @renderers ||= { "text" => Produce::Acquire::Serializer::Text, "json" => Produce::Acquire::Serializer::Json, "yaml" => Produce::Acquire::Serializer::Yaml, } end |
.write_if_changed(target_path, bytes, _format) ⇒ Object
Built artifacts are content-addressed (no volatile timestamp, ADR 0070), so identity is plain byte-equality: skip the write when nothing changed. ‘format` is retained for signature stability across renderers.
119 120 121 122 123 |
# File 'lib/textus/produce/acquire/projection.rb', line 119 def self.write_if_changed(target_path, bytes, _format) return if File.exist?(target_path) && File.binread(target_path) == bytes File.binwrite(target_path, bytes) end |
Instance Method Details
#run(mentry) ⇒ Object
Runs the projection pipeline for ‘mentry` and returns the on-disk target_path string.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/textus/produce/acquire/projection.rb', line 58 def run(mentry) reader = Textus::Read::Get.new(container: @container, call: @call) # Projections must be able to read source data from any nested entry, # including keyless (publish_tree) ones like knowledge.decisions. # The `include_keyless: true` option makes the resolver walk those dirs # without exposing them on the public `list` / CLI surface (ADR 0047). resolver = @manifest.resolver lister = lambda do |prefix:| resolver.enumerate(prefix: prefix, include_keyless: true) .map { |row| { "key" => row[:key], "zone" => row[:manifest_entry].zone, "path" => row[:path] } } end self.class.pipeline_run( mentry: mentry, deps: Deps.new( manifest: @manifest, reader: reader.method(:call), lister: lister, rpc: @rpc, transform_context: @container, ), ) end |