Class: Sxn::Templates::TemplateEngine
- Inherits:
-
Object
- Object
- Sxn::Templates::TemplateEngine
- Defined in:
- lib/sxn/templates/template_engine.rb
Overview
TemplateEngine is the main interface for template processing in sxn. It combines template processing, variable collection, and security validation to provide a safe and convenient API for generating files from templates.
Features:
-
Built-in template discovery
-
Automatic variable collection
-
Security validation
-
Template caching
-
Multiple template formats support
Example:
engine = TemplateEngine.new(session: session, project: project)
engine.process_template("rails/CLAUDE.md", "/path/to/output.md")
Constant Summary collapse
- TEMPLATES_DIR =
Built-in template directory
File.("../templates", __dir__)
Instance Method Summary collapse
-
#apply_template_set(template_set, destination_dir, custom_variables = {}, options = {}) ⇒ Array<String>
Apply a set of templates to a directory.
-
#available_variables(custom_variables = {}) ⇒ Hash
Get available variables for templates.
-
#clear_cache! ⇒ Object
Clear template cache.
-
#initialize(session: nil, project: nil, config: nil) ⇒ TemplateEngine
constructor
A new instance of TemplateEngine.
-
#list_templates(category = nil) ⇒ Array<String>
List available built-in templates.
-
#process_string(template_content, custom_variables = {}, options = {}) ⇒ String
Process a template string directly (not from file).
-
#process_template(template_name, destination_path, custom_variables = {}, options = {}) ⇒ String
Process a template and write it to the specified destination.
-
#refresh_variables! ⇒ Object
Refresh variable cache (useful for long-running processes).
-
#render_template(template_name, variables = {}, options = {}) ⇒ String
Render a template with variables.
-
#template_categories ⇒ Array<String>
Get template categories.
-
#template_exists?(template_name, template_dir = nil) ⇒ Boolean
Check if a template exists.
-
#template_info(template_name, template_dir = nil) ⇒ Hash
Get template information.
-
#validate_template_syntax(template_name, template_dir = nil) ⇒ Boolean
Validate template syntax without processing.
Constructor Details
#initialize(session: nil, project: nil, config: nil) ⇒ TemplateEngine
Returns a new instance of TemplateEngine.
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/sxn/templates/template_engine.rb', line 44 def initialize(session: nil, project: nil, config: nil) @session = session @project = project @config = config @processor = TemplateProcessor.new @variables_collector = TemplateVariables.new(session, project, config) @security = TemplateSecurity.new @template_cache = {} end |
Instance Method Details
#apply_template_set(template_set, destination_dir, custom_variables = {}, options = {}) ⇒ Array<String>
Apply a set of templates to a directory
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/sxn/templates/template_engine.rb', line 271 def apply_template_set(template_set, destination_dir, custom_variables = {}, = {}) templates = list_templates(template_set) created_files = [] templates.each do |template_name| # Determine output filename (remove .liquid extension) output_name = template_name.sub(/\.liquid$/, "") output_path = File.join(destination_dir, File.basename(output_name)) begin process_template(template_name, output_path, custom_variables, ) created_files << output_path rescue StandardError => e # Log error but continue with other templates warn "Failed to process template #{template_name}: #{e.message}" end end created_files end |
#available_variables(custom_variables = {}) ⇒ Hash
Get available variables for templates
204 205 206 |
# File 'lib/sxn/templates/template_engine.rb', line 204 def available_variables(custom_variables = {}) collect_variables(custom_variables) end |
#clear_cache! ⇒ Object
Clear template cache
214 215 216 217 |
# File 'lib/sxn/templates/template_engine.rb', line 214 def clear_cache! @template_cache.clear @security.clear_cache! end |
#list_templates(category = nil) ⇒ Array<String>
List available built-in templates
116 117 118 119 120 121 122 123 |
# File 'lib/sxn/templates/template_engine.rb', line 116 def list_templates(category = nil) templates_dir = category ? File.join(TEMPLATES_DIR, category) : TEMPLATES_DIR return [] unless Dir.exist?(templates_dir) Dir.glob("**/*.liquid", base: templates_dir).map do |path| category ? File.join(category, path) : path end.sort end |
#process_string(template_content, custom_variables = {}, options = {}) ⇒ String
Process a template string directly (not from file)
225 226 227 228 229 230 231 232 233 |
# File 'lib/sxn/templates/template_engine.rb', line 225 def process_string(template_content, custom_variables = {}, = {}) = { validate: true }.merge() variables = collect_variables(custom_variables) @security.validate_template(template_content, variables) if [:validate] @processor.process(template_content, variables, ) end |
#process_template(template_name, destination_path, custom_variables = {}, options = {}) ⇒ String
Process a template and write it to the specified destination
65 66 67 68 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 |
# File 'lib/sxn/templates/template_engine.rb', line 65 def process_template(template_name, destination_path, custom_variables = {}, = {}) = { force: false, validate: true, template_dir: nil }.merge() # Find the template file template_path = find_template(template_name, [:template_dir]) # Check if destination exists and handle accordingly destination = Pathname.new(destination_path) if destination.exist? && ![:force] raise Errors::TemplateError, "Destination file already exists: #{destination_path}. Use force: true to overwrite." end # steep:ignore:start - Template processing uses dynamic variable resolution # Liquid template processing and variable collection use dynamic features # that cannot be statically typed. Runtime validation provides safety. # Collect variables variables = collect_variables(custom_variables) # Runtime validation of template variables variables = RuntimeValidations.validate_template_variables(variables) # Validate template security if requested if [:validate] template_content = File.read(template_path) @security.validate_template(template_content, variables) end # Process the template with runtime validation result = @processor.process_file(template_path, variables, ) # Create destination directory if it doesn't exist destination.dirname.mkpath # Write the result destination.write(result) destination_path rescue Errors::TemplateSecurityError # Re-raise security errors without wrapping raise rescue StandardError => e raise Errors::TemplateProcessingError, "Failed to process template '#{template_name}': #{e.message}" end |
#refresh_variables! ⇒ Object
Refresh variable cache (useful for long-running processes)
209 210 211 |
# File 'lib/sxn/templates/template_engine.rb', line 209 def refresh_variables! @variables_collector.refresh! end |
#render_template(template_name, variables = {}, options = {}) ⇒ String
Render a template with variables
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/sxn/templates/template_engine.rb', line 241 def render_template(template_name, variables = {}, = {}) # Find the template file template_path = find_template(template_name, [:template_dir]) # Read template content template_content = File.read(template_path) # Merge with available variables all_variables = collect_variables(variables) # Validate template security if requested @security.validate_template(template_content, all_variables) if .fetch(:validate, true) # Process and return the result @processor.process(template_content, all_variables, ) rescue Errors::TemplateSecurityError # Re-raise security errors without wrapping raise rescue StandardError => e raise Errors::TemplateProcessingError, "Failed to render template '#{template_name}': #{e.message}" end |
#template_categories ⇒ Array<String>
Get template categories
128 129 130 131 132 133 134 135 |
# File 'lib/sxn/templates/template_engine.rb', line 128 def template_categories return [] unless Dir.exist?(TEMPLATES_DIR) Dir.entries(TEMPLATES_DIR) .select { |entry| File.directory?(File.join(TEMPLATES_DIR, entry)) } .reject { |entry| entry.start_with?(".") } .sort end |
#template_exists?(template_name, template_dir = nil) ⇒ Boolean
Check if a template exists
142 143 144 145 146 147 |
# File 'lib/sxn/templates/template_engine.rb', line 142 def template_exists?(template_name, template_dir = nil) find_template(template_name, template_dir) true rescue Errors::TemplateNotFoundError false end |
#template_info(template_name, template_dir = nil) ⇒ Hash
Get template information
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/sxn/templates/template_engine.rb', line 154 def template_info(template_name, template_dir = nil) template_path = find_template(template_name, template_dir) template_content = File.read(template_path) { name: template_name, path: template_path, size: template_content.bytesize, variables: @processor.extract_variables(template_content), syntax_valid: validate_template_syntax(template_content) } rescue StandardError => e { name: template_name, error: e., syntax_valid: false } end |
#validate_template_syntax(template_name, template_dir = nil) ⇒ Boolean
Validate template syntax without processing
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/sxn/templates/template_engine.rb', line 178 def validate_template_syntax(template_name, template_dir = nil) # Better detection: if it contains Liquid syntax and no path separators, treat as content if template_name.is_a?(String) && (template_name.include?("{{") || template_name.include?("{%")) && !template_name.include?("/") && !template_name.end_with?(".liquid") # It's template content with Liquid syntax template_content = template_name elsif template_name.is_a?(String) && !template_name.include?("\n") && (template_name.include?("/") || template_name.match?(/\.\w+$/) || !template_name.include?("{{")) # It's a template name/path template_path = find_template(template_name, template_dir) template_content = File.read(template_path) else # Default: treat as content for backward compatibility template_content = template_name end @processor.validate_syntax(template_content) rescue Errors::TemplateSyntaxError, Errors::TemplateNotFoundError false end |