Module: Pray::Render
- Defined in:
- lib/pray/render.rb
Defined Under Namespace
Classes: ContentBuilder, PlannedProvisionedFile
Class Method Summary collapse
- .append_header(builder, project, output) ⇒ Object
- .append_managed_export(builder, managed_spans, package, export, target, output, symbols) ⇒ Object
- .append_scoped_local(builder, project, path, symbols) ⇒ Object
- .append_scoped_package(builder, managed_spans, project, target, output, package_name, symbols) ⇒ Object
- .collect_exact_file_bindings(project, planned) ⇒ Object
- .collect_legacy_skill_files(project, package, destination_root, planned) ⇒ Object
- .collect_selected_export_files(project, package, destination_root, planned) ⇒ Object
- .collect_tree_files(project, source_root, destination_root, relative_files, only, except, planned) ⇒ Object
- .folder_destination_name(export_name, export_path) ⇒ Object
- .legacy_skill_covered_by_export?(package, skill) ⇒ Boolean
- .materialize_provisioned_exports(project) ⇒ Object
- .planned_provisioned_files(project) ⇒ Object
- .relative_project_path(project, absolute) ⇒ Object
- .render_legacy_compose(project, target, output) ⇒ Object
- .render_project(project) ⇒ Object
- .render_scoped_compose(project, target, output) ⇒ Object
- .render_target(project, target, output) ⇒ Object
- .should_inline_export?(package, export_name) ⇒ Boolean
- .write_provisioned_file(source, destination, symbols) ⇒ Object
- .write_rendered_targets(project, rendered) ⇒ Object
Class Method Details
.append_header(builder, project, output) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/pray/render.rb', line 140 def append_header(builder, project, output) return unless project.manifest.render.header output_name = File.basename(output) builder.append_line("<!-- pray:0 ignore-comments -->") builder.append_empty_line builder.append_line("# Agent context") builder.append_empty_line builder.append_line("Do not edit managed blocks in `#{output_name}` or provisioned files under `.agents/`.") builder.append_line("To change shared guidance, update `Prayfile` and run `pray install`.") builder.append_empty_line end |
.append_managed_export(builder, managed_spans, package, export, target, output, symbols) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/pray/render.rb', line 174 def append_managed_export(builder, managed_spans, package, export, target, output, symbols) body = package.export_bodies[export] raise Error.render("package #{package.declaration.name} is missing cached export #{export}") unless body body = Substitute.substitute_pray_symbols(body, symbols) identifier = Hashing.marker_id("#{package.declaration.name}:#{export}:#{target.name}") open_line = builder.next_line_number builder.append_line("<!-- pray:#{identifier} -->") builder.append_body(body) close_line = builder.next_line_number builder.append_line("<!-- pray:#{identifier} -->") managed_spans << ManagedSpanRecord.new( id: identifier, target: output, open_line: open_line, close_line: close_line, ideal_checksum: Hashing.checksum_managed_span_content(body), package: package.declaration.name, export: export, source_checksum: package.source_checksum, silenced: false ) builder.append_empty_line end |
.append_scoped_local(builder, project, path, symbols) ⇒ Object
153 154 155 156 157 158 159 160 |
# File 'lib/pray/render.rb', line 153 def append_scoped_local(builder, project, path, symbols) local = project.local_files.find { |candidate| candidate.manifest_path == path } return unless local return if local.content.empty? && local.optional builder.append_body(Substitute.substitute_pray_symbols(local.content, symbols)) builder.append_empty_line end |
.append_scoped_package(builder, managed_spans, project, target, output, package_name, symbols) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/pray/render.rb', line 162 def append_scoped_package(builder, managed_spans, project, target, output, package_name, symbols) package = project.packages.find { |candidate| candidate.declaration.name == package_name } return unless package return unless Environment.package_matches_environment?(package.declaration.groups, project.environment) package.selected_exports.each do |export| next unless should_inline_export?(package, export) append_managed_export(builder, managed_spans, package, export, target, output, symbols) end end |
.collect_exact_file_bindings(project, planned) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/pray/render.rb', line 204 def collect_exact_file_bindings(project, planned) project.packages.each do |package| destination = package.declaration.file next unless destination next unless Environment.package_matches_environment?(package.declaration.groups, project.environment) matched = false package.selected_exports.each do |export_name| export = package.spec.exports[export_name] next unless export&.kind == "file" source = File.join(package.root, export.path) raise Error.render("file export source missing: #{source}") unless File.file?(source) planned << PlannedProvisionedFile.new(path: destination, source: source) matched = true break end unless matched raise Error.render( "package #{package.declaration.name} has file: \"#{destination}\" but no selected file export" ) end end end |
.collect_legacy_skill_files(project, package, destination_root, planned) ⇒ Object
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/pray/render.rb', line 230 def collect_legacy_skill_files(project, package, destination_root, planned) package.spec.skills.each do |skill_name, skill| next if legacy_skill_covered_by_export?(package, skill) skill_files = package.skill_files[skill_name] raise Error.render("package #{package.declaration.name} has no indexed files for legacy skill #{skill_name}") unless skill_files collect_tree_files( project, File.join(package.root, skill.path), File.join(destination_root, skill_name), skill_files, [], [], planned ) end end |
.collect_selected_export_files(project, package, destination_root, planned) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/pray/render.rb', line 257 def collect_selected_export_files(project, package, destination_root, planned) package.selected_exports.each do |export_name| export = package.spec.exports[export_name] next unless export case export.kind when "folder", "skill" indexed_files = package.skill_files[export_name] unless indexed_files raise Error.render("package #{package.declaration.name} has no indexed files for folder export #{export_name}") end destination_name = folder_destination_name(export_name, export.path) collect_tree_files( project, File.join(package.root, export.path), File.join(destination_root, destination_name), indexed_files, export.only || [], export.except || [], planned ) when "file" next if package.declaration.file source = File.join(package.root, export.path) raise Error.render("file export source missing: #{source}") unless File.file?(source) destination = File.join(destination_root, export_name, File.basename(source)) planned << PlannedProvisionedFile.new( path: relative_project_path(project, destination), source: source ) end end end |
.collect_tree_files(project, source_root, destination_root, relative_files, only, except, planned) ⇒ Object
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/pray/render.rb', line 298 def collect_tree_files(project, source_root, destination_root, relative_files, only, except, planned) raise Error.render("folder source directory missing: #{source_root}") unless File.directory?(source_root) raise Error.render("no files listed in package manifest for #{source_root}") if relative_files.empty? matched = false relative_files.each do |relative| next if !only.empty? && !only.include?(relative) next if except.include?(relative) source = File.join(source_root, relative) raise Error.render("provisioned file missing: #{source}") unless File.file?(source) destination = File.join(destination_root, relative) planned << PlannedProvisionedFile.new( path: relative_project_path(project, destination), source: source ) matched = true end if !matched && only.empty? && except.empty? raise Error.render("no files listed in package manifest for #{source_root}") end end |
.folder_destination_name(export_name, export_path) ⇒ Object
294 295 296 |
# File 'lib/pray/render.rb', line 294 def folder_destination_name(export_name, export_path) File.basename(export_path.delete_suffix("/")).empty? ? export_name : File.basename(export_path.delete_suffix("/")) end |
.legacy_skill_covered_by_export?(package, skill) ⇒ Boolean
249 250 251 252 253 254 255 |
# File 'lib/pray/render.rb', line 249 def legacy_skill_covered_by_export?(package, skill) package.spec.exports.any? do |export_name, export| package.selected_exports.include?(export_name) && %w[folder skill].include?(export.kind) && export.path.delete_suffix("/") == skill.path.delete_suffix("/") end end |
.materialize_provisioned_exports(project) ⇒ Object
36 37 38 39 40 41 42 43 |
# File 'lib/pray/render.rb', line 36 def materialize_provisioned_exports(project) symbols = project.manifest.symbols || {} planned_provisioned_files(project).each do |file| destination = File.join(project.project_root, file.path) FileUtils.mkdir_p(File.dirname(destination)) write_provisioned_file(file.source, destination, symbols) end end |
.planned_provisioned_files(project) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/pray/render.rb', line 55 def planned_provisioned_files(project) planned = [] collect_exact_file_bindings(project, planned) project.manifest.targets.each do |target| target.skills.each do |folder_root| destination_root = File.join(project.project_root, folder_root) project.packages.each do |package| next unless Environment.package_matches_environment?(package.declaration.groups, project.environment) next unless Destination.package_bound_to_tree?(package.declaration, target) collect_legacy_skill_files(project, package, destination_root, planned) collect_selected_export_files(project, package, destination_root, planned) end end end planned.sort_by(&:path).uniq(&:path) end |
.relative_project_path(project, absolute) ⇒ Object
323 324 325 326 327 |
# File 'lib/pray/render.rb', line 323 def relative_project_path(project, absolute) Pathname(absolute).relative_path_from(Pathname(project.project_root)).to_s rescue ArgumentError absolute end |
.render_legacy_compose(project, target, output) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/pray/render.rb', line 100 def render_legacy_compose(project, target, output) builder = ContentBuilder.new append_header(builder, project, output) unbound_locals = project.local_files.select do |local| declaration = project.manifest.local.find { |entry| entry.path == local.manifest_path } declaration.nil? || !declaration.bound end unless unbound_locals.empty? builder.append_line("## Additional instructions") builder.append_empty_line end symbols = project.manifest.symbols || {} unbound_locals.each do |local| next if local.content.empty? && local.optional builder.append_line("### #{local.manifest_path}") builder.append_body(Substitute.substitute_pray_symbols(local.content, symbols)) builder.append_empty_line end builder.append_line("## Shared instructions") builder.append_empty_line managed_spans = [] project.packages.each do |package| next unless Environment.package_matches_environment?(package.declaration.groups, project.environment) next unless Destination.package_bound_to_compose?(package.declaration, target) package.selected_exports.each do |export| next unless should_inline_export?(package, export) append_managed_export(builder, managed_spans, package, export, target, output, symbols) end end RenderedTarget.new(path: output, content: builder.finish, managed_spans: managed_spans) end |
.render_project(project) ⇒ Object
16 17 18 19 20 21 22 23 24 25 |
# File 'lib/pray/render.rb', line 16 def render_project(project) rendered = [] project.manifest.targets.each do |target| output = target.outputs.first next unless output rendered << render_target(project, target, output) end rendered end |
.render_scoped_compose(project, target, output) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/pray/render.rb', line 83 def render_scoped_compose(project, target, output) builder = ContentBuilder.new append_header(builder, project, output) symbols = project.manifest.symbols || {} managed_spans = [] (target.entries || []).each do |entry| if entry.kind == "local" append_scoped_local(builder, project, entry.path, symbols) else append_scoped_package(builder, managed_spans, project, target, output, entry.name, symbols) end end RenderedTarget.new(path: output, content: builder.finish, managed_spans: managed_spans) end |
.render_target(project, target, output) ⇒ Object
75 76 77 78 79 80 81 |
# File 'lib/pray/render.rb', line 75 def render_target(project, target, output) if target.scoped && target.mode == "compose" return render_scoped_compose(project, target, output) end render_legacy_compose(project, target, output) end |
.should_inline_export?(package, export_name) ⇒ Boolean
199 200 201 202 |
# File 'lib/pray/render.rb', line 199 def should_inline_export?(package, export_name) export = package.spec.exports[export_name] export.nil? || export.kind == "fragment" end |
.write_provisioned_file(source, destination, symbols) ⇒ Object
45 46 47 48 49 50 51 52 53 |
# File 'lib/pray/render.rb', line 45 def write_provisioned_file(source, destination, symbols) bytes = File.binread(source) text = bytes.force_encoding(Encoding::UTF_8) if text.valid_encoding? File.write(destination, Substitute.substitute_pray_symbols(text, symbols)) else File.binwrite(destination, bytes) end end |
.write_rendered_targets(project, rendered) ⇒ Object
27 28 29 30 31 32 33 34 |
# File 'lib/pray/render.rb', line 27 def write_rendered_targets(project, rendered) rendered.each do |target| path = File.join(project.project_root, target.path) FileUtils.mkdir_p(File.dirname(path)) File.write(path, target.content) end materialize_provisioned_exports(project) end |