mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-15 08:50:45 -07:00
This adds a new `compile_to_ruby` method to Liquid::Template that compiles
Liquid templates to pure Ruby code. The compiled code can be eval'd to create
a proc that renders templates without needing the Liquid library at runtime.
## Features
- Compiles all standard Liquid tags: if/unless/case, for, assign, capture,
cycle, increment/decrement, raw, echo, break/continue, comment, tablerow
- Compiles variable expressions with filter chains to direct Ruby method calls
- Supports static partial inlining ({% render %} and {% include %} with string
literals are loaded and compiled at compile time)
- Dynamic partial support via runtime callbacks (__render_dynamic__, __include_dynamic__)
- Debug mode with source comments for error tracing (lightweight source map)
- SourceMapper utility to trace runtime errors back to Liquid source
## Optimization Opportunities
The compiled Ruby code has significant performance advantages:
1. No Context object - variables accessed directly from assigns hash
2. No filter invocation overhead - direct Ruby method calls
3. No resource limits tracking - no per-node render score updates
4. No stack-based scoping - uses Ruby's native block scoping
5. Direct string concatenation - no render_to_output_buffer abstraction
6. Native control flow - break/continue use Ruby's throw/catch
7. No to_liquid calls - values used directly
8. No profiling hooks - no profiler overhead
9. No exception rendering - errors propagate naturally
## Usage
```ruby
template = Liquid::Template.parse("Hello, {{ name }}!")
ruby_code = template.compile_to_ruby
render_proc = eval(ruby_code)
result = render_proc.call({ "name" => "World" })
# => "Hello, World!"
# With debug mode for error tracing:
ruby_code = template.compile_to_ruby(debug: true)
```
## Limitations
- Dynamic {% render %} and {% include %} require runtime callback methods
- Custom tags need explicit compiler implementations
- Custom filters must be available at runtime
38 lines
1.4 KiB
Ruby
38 lines
1.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Liquid
|
|
module Compile
|
|
# VariableCompiler compiles Liquid variable expressions ({{ ... }}) to Ruby code.
|
|
#
|
|
# A Variable consists of:
|
|
# - A name expression (the value to output)
|
|
# - Zero or more filters to apply
|
|
class VariableCompiler
|
|
# Compile a Variable node and append the result to the output buffer
|
|
# @param variable [Liquid::Variable] The variable node
|
|
# @param compiler [RubyCompiler] The main compiler instance
|
|
# @param code [CodeGenerator] The code generator
|
|
def self.compile(variable, compiler, code)
|
|
value_expr = compile_to_expression(variable, compiler)
|
|
code.line "__output__ << __output_value__(#{value_expr})"
|
|
end
|
|
|
|
# Compile a Variable node to a Ruby expression (without output)
|
|
# @param variable [Liquid::Variable] The variable node
|
|
# @param compiler [RubyCompiler] The main compiler instance
|
|
# @return [String] Ruby code expression
|
|
def self.compile_to_expression(variable, compiler)
|
|
# Compile the base name expression
|
|
base_expr = ExpressionCompiler.compile(variable.name, compiler)
|
|
|
|
# Apply filters if any
|
|
if variable.filters && !variable.filters.empty?
|
|
FilterCompiler.compile(base_expr, variable.filters, compiler)
|
|
else
|
|
base_expr
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|