mirror of
https://github.com/kemko/liquid.git
synced 2026-01-04 09:15:41 +03:00
Compare commits
2 Commits
superfluid
...
truffle-na
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
350addf364 | ||
|
|
49488e3757 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,4 +7,3 @@ pkg
|
|||||||
.ruby-version
|
.ruby-version
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
.bundle
|
.bundle
|
||||||
.byebug_history
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2019-04-22 19:11:24 -0400 using RuboCop version 0.53.0.
|
# on 2019-03-19 11:04:37 -0400 using RuboCop version 0.53.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
@@ -46,18 +46,18 @@ Lint/Void:
|
|||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/parse_context.rb'
|
- 'lib/liquid/parse_context.rb'
|
||||||
|
|
||||||
# Offense count: 53
|
# Offense count: 54
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 56
|
Max: 56
|
||||||
|
|
||||||
# Offense count: 12
|
# Offense count: 12
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 13
|
Max: 12
|
||||||
|
|
||||||
# Offense count: 112
|
# Offense count: 112
|
||||||
# Configuration parameters: CountComments.
|
# Configuration parameters: CountComments.
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
Max: 38
|
Max: 37
|
||||||
|
|
||||||
# Offense count: 8
|
# Offense count: 8
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
@@ -90,7 +90,7 @@ Naming/UncommunicativeMethodParamName:
|
|||||||
- 'test/integration/template_test.rb'
|
- 'test/integration/template_test.rb'
|
||||||
- 'test/unit/condition_unit_test.rb'
|
- 'test/unit/condition_unit_test.rb'
|
||||||
|
|
||||||
# Offense count: 12
|
# Offense count: 10
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: prefer_alias, prefer_alias_method
|
# SupportedStyles: prefer_alias, prefer_alias_method
|
||||||
@@ -253,7 +253,7 @@ Style/WhileUntilModifier:
|
|||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/tags/case.rb'
|
- 'lib/liquid/tags/case.rb'
|
||||||
|
|
||||||
# Offense count: 648
|
# Offense count: 640
|
||||||
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||||
# URISchemes: http, https
|
# URISchemes: http, https
|
||||||
Metrics/LineLength:
|
Metrics/LineLength:
|
||||||
|
|||||||
14
Gemfile
14
Gemfile
@@ -5,23 +5,23 @@ end
|
|||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
|
|
||||||
group :benchmark, :test do
|
group :benchmark, :test do
|
||||||
gem 'benchmark-ips'
|
gem 'benchmark-ips'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
|
|
||||||
install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ } do
|
# install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ } do
|
||||||
gem 'stackprof'
|
# gem 'stackprof'
|
||||||
end
|
# end
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 0.53.0'
|
gem 'rubocop', '~> 0.53.0'
|
||||||
gem 'awesome_print'
|
|
||||||
gem 'pry'
|
|
||||||
gem 'byebug'
|
|
||||||
|
|
||||||
platform :mri do
|
platform :mri do
|
||||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: '9168659de45d6d576fce30c735f857e597fa26f6'
|
gem 'liquid-c', github: 'Shopify/liquid-c', ref: '9168659de45d6d576fce30c735f857e597fa26f6'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# gem "byebug", "~> 11.0"
|
||||||
|
|
||||||
|
gem "pry", "~> 0.12.2"
|
||||||
|
|||||||
22
Rakefile
22
Rakefile
@@ -45,17 +45,6 @@ task :test do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'runs the test suite using the superfluid compiler'
|
|
||||||
Rake::TestTask.new(:test_superfluid) do |t|
|
|
||||||
t.libs << '.' << 'lib' << 'test'
|
|
||||||
t.test_files = FileList['test/integration/**/*_test.rb']
|
|
||||||
t.verbose = false
|
|
||||||
|
|
||||||
ENV['LIQUID_PARSER_MODE'] = 'strict'
|
|
||||||
ENV['LIQUID-C'] = '1'
|
|
||||||
ENV['SUPERFLUID'] = '1'
|
|
||||||
end
|
|
||||||
|
|
||||||
task gem: :build
|
task gem: :build
|
||||||
task :build do
|
task :build do
|
||||||
system "gem build liquid.gemspec"
|
system "gem build liquid.gemspec"
|
||||||
@@ -82,11 +71,6 @@ namespace :benchmark do
|
|||||||
task :strict do
|
task :strict do
|
||||||
ruby "./performance/benchmark.rb strict"
|
ruby "./performance/benchmark.rb strict"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Run the liquid benchmark with strict parsing"
|
|
||||||
task :superfluid do
|
|
||||||
ruby "./performance/benchmark.rb superfluid"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace :profile do
|
namespace :profile do
|
||||||
@@ -116,3 +100,9 @@ end
|
|||||||
task :console do
|
task :console do
|
||||||
exec 'irb -I lib -r liquid'
|
exec 'irb -I lib -r liquid'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task :dump_portable_ast, [:filename] do |_task, args|
|
||||||
|
require 'liquid/ast_to_portable_json'
|
||||||
|
ast = Liquid::Template.parse(File.read(args[:filename]))
|
||||||
|
puts(Liquid::ASTToPortableJSON.dump(ast))
|
||||||
|
end
|
||||||
|
|||||||
27
lib/liquid/ast_to_portable_json.rb
Normal file
27
lib/liquid/ast_to_portable_json.rb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
require 'yaml'
|
||||||
|
require 'json'
|
||||||
|
require 'liquid'
|
||||||
|
|
||||||
|
module Liquid
|
||||||
|
class ASTToPortableJSON
|
||||||
|
def self.dump(ast)
|
||||||
|
yaml = YAML.dump(ast)
|
||||||
|
yaml.gsub!(/---.*/, '')
|
||||||
|
yaml.gsub!(/!ruby\/object:(.+)\n(\s+)/, "\n\\2class_name: \\1\n\\2")
|
||||||
|
|
||||||
|
bare_hash_ast = YAML.load(yaml)
|
||||||
|
delete_parse_context(bare_hash_ast)
|
||||||
|
JSON.pretty_generate(bare_hash_ast)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete_parse_context(bare_hash_ast)
|
||||||
|
if bare_hash_ast.is_a?(Array)
|
||||||
|
bare_hash_ast.each { |v| delete_parse_context(v) }
|
||||||
|
elsif bare_hash_ast.is_a?(Hash)
|
||||||
|
bare_hash_ast.delete('parse_context')
|
||||||
|
bare_hash_ast.each { |k, v| delete_parse_context(v) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# For backwards compatibility
|
|
||||||
def render(context)
|
def render(context)
|
||||||
@body.render(context)
|
@body.render(context)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -67,23 +67,19 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
render_to_output_buffer(context, '')
|
output = []
|
||||||
end
|
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
context.resource_limits.render_score += @nodelist.length
|
context.resource_limits.render_score += @nodelist.length
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
while node = @nodelist[idx]
|
while node = @nodelist[idx]
|
||||||
previous_output_size = output.bytesize
|
|
||||||
|
|
||||||
case node
|
case node
|
||||||
when String
|
when String
|
||||||
|
check_resources(context, node)
|
||||||
output << node
|
output << node
|
||||||
when Variable
|
when Variable
|
||||||
render_node(context, output, node)
|
render_node_to_output(node, output, context)
|
||||||
when Block
|
when Block
|
||||||
render_node(context, node.blank? ? '' : output, node)
|
render_node_to_output(node, output, context, node.blank?)
|
||||||
break if context.interrupt? # might have happened in a for-block
|
break if context.interrupt? # might have happened in a for-block
|
||||||
when Continue, Break
|
when Continue, Break
|
||||||
# If we get an Interrupt that means the block must stop processing. An
|
# If we get an Interrupt that means the block must stop processing. An
|
||||||
@@ -92,28 +88,38 @@ module Liquid
|
|||||||
context.push_interrupt(node.interrupt)
|
context.push_interrupt(node.interrupt)
|
||||||
break
|
break
|
||||||
else # Other non-Block tags
|
else # Other non-Block tags
|
||||||
render_node(context, output, node)
|
render_node_to_output(node, output, context)
|
||||||
break if context.interrupt? # might have happened through an include
|
break if context.interrupt? # might have happened through an include
|
||||||
end
|
end
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
context.raise_if_resource_limits_reached(output.bytesize - previous_output_size)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
output.join
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_node(context, output, node)
|
def render_node_to_output(node, output, context, skip_output = false)
|
||||||
node.render_to_output_buffer(context, output)
|
node_output = node.render(context)
|
||||||
|
node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
|
||||||
|
check_resources(context, node_output)
|
||||||
|
output << node_output unless skip_output
|
||||||
|
rescue MemoryError => e
|
||||||
|
raise e
|
||||||
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
||||||
context.handle_error(e, node.line_number)
|
context.handle_error(e, node.line_number)
|
||||||
|
output << nil
|
||||||
rescue ::StandardError => e
|
rescue ::StandardError => e
|
||||||
line_number = node.is_a?(String) ? nil : node.line_number
|
line_number = node.is_a?(String) ? nil : node.line_number
|
||||||
output << context.handle_error(e, line_number)
|
output << context.handle_error(e, line_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_resources(context, node_output)
|
||||||
|
context.resource_limits.render_length += node_output.bytesize
|
||||||
|
return unless context.resource_limits.reached?
|
||||||
|
raise MemoryError.new("Memory limits exceeded".freeze)
|
||||||
|
end
|
||||||
|
|
||||||
def create_variable(token, parse_context)
|
def create_variable(token, parse_context)
|
||||||
token.scan(ContentOfVariable) do |content|
|
token.scan(ContentOfVariable) do |content|
|
||||||
markup = content.first
|
markup = content.first
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ module Liquid
|
|||||||
@@operators
|
@@operators
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :attachment, :child_condition, :child_relation
|
attr_reader :attachment, :child_condition
|
||||||
attr_accessor :left, :operator, :right
|
attr_accessor :left, :operator, :right
|
||||||
|
|
||||||
def initialize(left = nil, operator = nil, right = nil)
|
def initialize(left = nil, operator = nil, right = nil)
|
||||||
@@ -81,6 +81,10 @@ module Liquid
|
|||||||
"#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
|
"#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
attr_reader :child_relation
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def equal_variables(left, right)
|
def equal_variables(left, right)
|
||||||
|
|||||||
@@ -37,16 +37,6 @@ module Liquid
|
|||||||
@global_filter = nil
|
@global_filter = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def raise_argument_error(message)
|
|
||||||
raise Liquid::ArgumentError, message
|
|
||||||
end
|
|
||||||
|
|
||||||
def raise_if_resource_limits_reached(length)
|
|
||||||
resource_limits.render_length += length
|
|
||||||
return unless resource_limits.reached?
|
|
||||||
raise MemoryError.new("Memory limits exceeded".freeze)
|
|
||||||
end
|
|
||||||
|
|
||||||
def warnings
|
def warnings
|
||||||
@warnings ||= []
|
@warnings ||= []
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,14 +44,11 @@ module Liquid
|
|||||||
tok[0] == type
|
tok[0] == type
|
||||||
end
|
end
|
||||||
|
|
||||||
SINGLE_TOKEN_EXPRESSION_TYPES = [:string, :number].freeze
|
|
||||||
private_constant :SINGLE_TOKEN_EXPRESSION_TYPES
|
|
||||||
|
|
||||||
def expression
|
def expression
|
||||||
token = @tokens[@p]
|
token = @tokens[@p]
|
||||||
if token[0] == :id
|
if token[0] == :id
|
||||||
variable_signature
|
variable_signature
|
||||||
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include? token[0]
|
elsif [:string, :number].include? token[0]
|
||||||
consume
|
consume
|
||||||
elsif token.first == :open_round
|
elsif token.first == :open_round
|
||||||
consume
|
consume
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class BlockBody
|
class BlockBody
|
||||||
def render_node_with_profiling(context, output, node)
|
def render_node_with_profiling(node, output, context, skip_output = false)
|
||||||
Profiler.profile_node_render(node) do
|
Profiler.profile_node_render(node) do
|
||||||
render_node_without_profiling(context, output, node)
|
render_node_without_profiling(node, output, context, skip_output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :render_node_without_profiling, :render_node
|
alias_method :render_node_without_profiling, :render_node_to_output
|
||||||
alias_method :render_node, :render_node_with_profiling
|
alias_method :render_node_to_output, :render_node_with_profiling
|
||||||
end
|
end
|
||||||
|
|
||||||
class Include < Tag
|
class Include < Tag
|
||||||
def render_to_output_buffer_with_profiling(context, output)
|
def render_with_profiling(context)
|
||||||
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
||||||
render_to_output_buffer_without_profiling(context, output)
|
render_without_profiling(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :render_to_output_buffer_without_profiling, :render_to_output_buffer
|
alias_method :render_without_profiling, :render
|
||||||
alias_method :render_to_output_buffer, :render_to_output_buffer_with_profiling
|
alias_method :render, :render_with_profiling
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ module Liquid
|
|||||||
@end_obj = end_obj
|
@end_obj = end_obj
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :start_obj, :end_obj
|
|
||||||
|
|
||||||
def evaluate(context)
|
def evaluate(context)
|
||||||
start_int = to_integer(context.evaluate(@start_obj))
|
start_int = to_integer(context.evaluate(@start_obj))
|
||||||
end_int = to_integer(context.evaluate(@end_obj))
|
end_int = to_integer(context.evaluate(@end_obj))
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ module Liquid
|
|||||||
truncate_string_str = truncate_string.to_s
|
truncate_string_str = truncate_string.to_s
|
||||||
l = length - truncate_string_str.length
|
l = length - truncate_string_str.length
|
||||||
l = 0 if l < 0
|
l = 0 if l < 0
|
||||||
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
|
||||||
end
|
end
|
||||||
|
|
||||||
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
||||||
@@ -88,7 +88,7 @@ module Liquid
|
|||||||
words = Utils.to_integer(words)
|
words = Utils.to_integer(words)
|
||||||
l = words - 1
|
l = words - 1
|
||||||
l = 0 if l < 0
|
l = 0 if l < 0
|
||||||
wordlist.length > l ? wordlist[0..l].join(" ".freeze).concat(truncate_string.to_s) : input
|
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
|
||||||
end
|
end
|
||||||
|
|
||||||
# Split input string into an array of substrings separated by given pattern.
|
# Split input string into an array of substrings separated by given pattern.
|
||||||
|
|||||||
@@ -1,632 +0,0 @@
|
|||||||
require 'ap'
|
|
||||||
|
|
||||||
require 'pry'
|
|
||||||
|
|
||||||
require 'stackprof'
|
|
||||||
|
|
||||||
AwesomePrint.defaults = {
|
|
||||||
raw: true
|
|
||||||
}
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
Liquid::BlockBody.class_eval do
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
|
|
||||||
ruby = Compiler.compile(@nodelist)
|
|
||||||
|
|
||||||
if false
|
|
||||||
puts
|
|
||||||
puts "--------------------------- GENERATED RUBY -"
|
|
||||||
line_number = 1
|
|
||||||
puts(ruby.lines.map do |line|
|
|
||||||
"#{line_number}\t#{line}".tap { line_number += 1}
|
|
||||||
end)
|
|
||||||
puts "--------------------------- /GENERATED RUBY -"
|
|
||||||
end
|
|
||||||
|
|
||||||
instructions = RubyVM::InstructionSequence.compile(ruby)
|
|
||||||
|
|
||||||
output_io = StringIO.new
|
|
||||||
instructions.eval.call(output_io, context, Condition)
|
|
||||||
|
|
||||||
output << output_io.string
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class SuperfluidError < Exception
|
|
||||||
end
|
|
||||||
|
|
||||||
class Output
|
|
||||||
attr_reader :string, :indent_level
|
|
||||||
|
|
||||||
def initialize(initial_indent)
|
|
||||||
@string = ''.dup
|
|
||||||
@indent_level = initial_indent
|
|
||||||
@indent_str = " " * initial_indent * 2
|
|
||||||
end
|
|
||||||
|
|
||||||
def line(string)
|
|
||||||
@string << @indent_str << string << "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
def echo(string)
|
|
||||||
output << "liquid_out.write(to_output(#{string}))"
|
|
||||||
end
|
|
||||||
|
|
||||||
def indent(&block)
|
|
||||||
output.indent(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def indent
|
|
||||||
@indent_level += 1
|
|
||||||
@indent_str = " " * @indent_level * 2
|
|
||||||
yield
|
|
||||||
@indent_level -= 1
|
|
||||||
@indent_str = " " * @indent_level * 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Compiler
|
|
||||||
class << self
|
|
||||||
def compile(template)
|
|
||||||
compiler = new
|
|
||||||
compiler.compile(template)
|
|
||||||
compiler.ruby
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@variables = Set.new
|
|
||||||
@output = Output.new(2)
|
|
||||||
@blank = false
|
|
||||||
end
|
|
||||||
|
|
||||||
def ruby
|
|
||||||
[
|
|
||||||
header,
|
|
||||||
@output.string,
|
|
||||||
trailer
|
|
||||||
].join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def header
|
|
||||||
<<~RUBY
|
|
||||||
module Warning
|
|
||||||
def warn(*)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ForloopDrop
|
|
||||||
def initialize(name, length, parentloop)
|
|
||||||
@name = name
|
|
||||||
@length = length
|
|
||||||
@parentloop = parentloop
|
|
||||||
@index = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_accessor :parentloop
|
|
||||||
|
|
||||||
def [](value)
|
|
||||||
case value
|
|
||||||
when "length"
|
|
||||||
@length
|
|
||||||
when "name"
|
|
||||||
@name
|
|
||||||
when "index"
|
|
||||||
@index + 1
|
|
||||||
when "index0"
|
|
||||||
@index
|
|
||||||
when "rindex"
|
|
||||||
@length - @index
|
|
||||||
when "rindex0"
|
|
||||||
@length - @index - 1
|
|
||||||
when "first"
|
|
||||||
@index == 0
|
|
||||||
when "last"
|
|
||||||
@index == @length - 1
|
|
||||||
when "parentloop"
|
|
||||||
@parentloop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_liquid
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def key?(*)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def increment!
|
|
||||||
@index += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def slice_collection(collection, from, limit)
|
|
||||||
to = if limit.nil?
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
limit + from
|
|
||||||
end
|
|
||||||
|
|
||||||
if (from != 0 || !to.nil?) && collection.respond_to?(:load_slice)
|
|
||||||
collection.load_slice(from, to)
|
|
||||||
else
|
|
||||||
slice_collection_using_each(collection, from, to)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def slice_collection_using_each(collection, from, to)
|
|
||||||
segments = []
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
if collection.is_a?(String)
|
|
||||||
return collection.empty? ? [] : [collection]
|
|
||||||
end
|
|
||||||
return [] unless collection.respond_to?(:each)
|
|
||||||
|
|
||||||
collection.each do |item|
|
|
||||||
if to && to <= index
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
if from <= index
|
|
||||||
segments << item
|
|
||||||
end
|
|
||||||
|
|
||||||
index += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
segments
|
|
||||||
end
|
|
||||||
|
|
||||||
def apply_operator(left, operator, right)
|
|
||||||
if left.respond_to?(operator) && right.respond_to?(operator) && !left.is_a?(Hash) && !right.is_a?(Hash)
|
|
||||||
begin
|
|
||||||
left.send(operator, right)
|
|
||||||
rescue ::ArgumentError => e
|
|
||||||
raise @context.raise_argument_error(e.message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def contains?(left, right)
|
|
||||||
if left && right && left.respond_to?(:include?)
|
|
||||||
right = right.to_s if left.is_a?(String)
|
|
||||||
left.include?(right)
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class GlobalVariableLookup
|
|
||||||
def method_missing(method_name, *)
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_output(value)
|
|
||||||
output = if value.is_a?(Array)
|
|
||||||
value.join
|
|
||||||
elsif value == nil
|
|
||||||
else
|
|
||||||
value.to_s
|
|
||||||
end
|
|
||||||
@context.apply_global_filter(output)
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(liquid_out, context, condition)
|
|
||||||
@condition = condition
|
|
||||||
@context = context
|
|
||||||
@for_offsets = {}
|
|
||||||
@cycle_values = {}
|
|
||||||
@context.registers[:for_stack] = []
|
|
||||||
@context.registers[:cycle] ||= {}
|
|
||||||
@if_changed_last = nil
|
|
||||||
@prev_output_size = 0
|
|
||||||
#{hoisted_variables}
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
def trailer
|
|
||||||
<<~RUBY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
GlobalVariableLookup.new.method(:run)
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
def hoisted_variables
|
|
||||||
@variables.map do |variable|
|
|
||||||
normal_name = unvar(variable)
|
|
||||||
"#{variable} = @context.find_variable(#{normal_name.inspect}, raise_on_not_found: false)"
|
|
||||||
end.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile(node)
|
|
||||||
old_blank = @blank
|
|
||||||
@blank = if node.respond_to?(:blank?)
|
|
||||||
node.blank?
|
|
||||||
elsif !(node.is_a?(String) && node =~ /\A\s*\z/)
|
|
||||||
false
|
|
||||||
else
|
|
||||||
@blank
|
|
||||||
end
|
|
||||||
|
|
||||||
case node
|
|
||||||
when Liquid::Document, Liquid::BlockBody
|
|
||||||
node.nodelist.collect(&method(:compile))
|
|
||||||
when Array
|
|
||||||
node.collect(&method(:compile))
|
|
||||||
when Liquid::Variable
|
|
||||||
compile_variable(node)
|
|
||||||
when Liquid::For
|
|
||||||
compile_for(node)
|
|
||||||
when Liquid::If
|
|
||||||
compile_if(node)
|
|
||||||
when Liquid::Ifchanged
|
|
||||||
compile_if_changed(node)
|
|
||||||
when Liquid::Template
|
|
||||||
compile(node.root)
|
|
||||||
when Liquid::Assign
|
|
||||||
compile_assign(node)
|
|
||||||
when Liquid::Case
|
|
||||||
compile_case(node)
|
|
||||||
when Liquid::Capture
|
|
||||||
compile_capture(node)
|
|
||||||
when String
|
|
||||||
compile_echo_literal(node)
|
|
||||||
when Liquid::Break
|
|
||||||
line "break" if @in_loop
|
|
||||||
when Liquid::Continue
|
|
||||||
line "next" if @in_loop
|
|
||||||
when Liquid::Cycle
|
|
||||||
compile_cycle(node)
|
|
||||||
when Liquid::Raw
|
|
||||||
compile_raw(node)
|
|
||||||
when Liquid::Increment
|
|
||||||
compile_increment(node)
|
|
||||||
when Liquid::Decrement
|
|
||||||
compile_decrement(node)
|
|
||||||
when Liquid::Comment
|
|
||||||
else
|
|
||||||
raise SuperfluidError, "Unknown node type #{node.inspect}"
|
|
||||||
end
|
|
||||||
|
|
||||||
@blank = old_blank
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_for(node)
|
|
||||||
variable_name = node.variable_name
|
|
||||||
|
|
||||||
collection_name = node.collection_name
|
|
||||||
|
|
||||||
iter_target_expr = case collection_name
|
|
||||||
when Liquid::VariableLookup
|
|
||||||
make_variable_lookup_expr(collection_name)
|
|
||||||
when Range
|
|
||||||
collection_name
|
|
||||||
when Liquid::RangeLookup
|
|
||||||
start_expr = make_variable_expr(collection_name.start_obj)
|
|
||||||
end_expr = make_variable_expr(collection_name.end_obj)
|
|
||||||
line "start = #{start_expr}"
|
|
||||||
line "@context.raise_argument_error('bad value for range') unless start.respond_to?(:to_i)"
|
|
||||||
line "start = #{start_expr}.to_i"
|
|
||||||
|
|
||||||
line "finish = #{end_expr}"
|
|
||||||
line "@context.raise_argument_error('bad value for range') unless finish.respond_to?(:to_i)"
|
|
||||||
line "finish = #{end_expr}.to_i"
|
|
||||||
|
|
||||||
"start..finish"
|
|
||||||
when Liquid::Expression::MethodLiteral
|
|
||||||
'[]'
|
|
||||||
else
|
|
||||||
raise SuperfluidError, "Unknown iteration target: #{collection_name.inspect}"
|
|
||||||
end
|
|
||||||
|
|
||||||
from_expr = if node.from == :continue
|
|
||||||
"@for_offsets['#{node.name}'].to_i"
|
|
||||||
elsif node.from
|
|
||||||
make_variable_expr(node.from)
|
|
||||||
else
|
|
||||||
'0'
|
|
||||||
end
|
|
||||||
|
|
||||||
limit_expr = node.limit ? make_variable_expr(node.limit) : 'nil'
|
|
||||||
line "from = #{from_expr}"
|
|
||||||
line "limit = #{limit_expr}"
|
|
||||||
line "@context.raise_argument_error('invalid integer') unless from.is_a?(Integer)"
|
|
||||||
line "@context.raise_argument_error('invalid integer') unless !limit || limit.is_a?(Integer)"
|
|
||||||
|
|
||||||
line "segment = slice_collection(#{iter_target_expr}, from, limit)"
|
|
||||||
line "segment.reverse!" if node.reversed
|
|
||||||
|
|
||||||
forloop = var('forloop')
|
|
||||||
hoist_var('forloop')
|
|
||||||
line "#{forloop} = ForloopDrop.new('#{node.name}', segment.length, #{forloop})"
|
|
||||||
|
|
||||||
line "if segment.any?"
|
|
||||||
indent do
|
|
||||||
line "segment.each do |#{var(variable_name)}|"
|
|
||||||
indent do
|
|
||||||
line "@context['forloop'] = #{forloop}"
|
|
||||||
|
|
||||||
|
|
||||||
old_in_loop = @in_loop
|
|
||||||
compile(node.for_block)
|
|
||||||
@in_loop = old_in_loop
|
|
||||||
|
|
||||||
line "#{forloop}.send(:increment!)"
|
|
||||||
end
|
|
||||||
line "end"
|
|
||||||
end
|
|
||||||
line "else"
|
|
||||||
indent do
|
|
||||||
compile(node.else_block) if node.else_block
|
|
||||||
end
|
|
||||||
line "end"
|
|
||||||
|
|
||||||
line "@for_offsets['#{node.name}'] = from + segment.length"
|
|
||||||
line "#{forloop} = #{forloop}.parentloop"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_if(node)
|
|
||||||
if_condition = node.blocks.first
|
|
||||||
line "if #{make_condition_expr(if_condition)}"
|
|
||||||
indent { if_condition.attachment.nodelist.each(&method(:compile)) }
|
|
||||||
|
|
||||||
node.blocks.drop(1).each do |condition|
|
|
||||||
if condition.left != nil
|
|
||||||
line "elsif #{make_condition_expr(condition)}"
|
|
||||||
else
|
|
||||||
line "else"
|
|
||||||
end
|
|
||||||
indent { condition.attachment.nodelist.each(&method(:compile)) }
|
|
||||||
end
|
|
||||||
|
|
||||||
line "end"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_if_changed(node)
|
|
||||||
line "if_changed = lambda do |; liquid_out|"
|
|
||||||
indent do
|
|
||||||
line "liquid_out = StringIO.new"
|
|
||||||
node.nodelist.each(&method(:compile))
|
|
||||||
line "liquid_out.string"
|
|
||||||
end
|
|
||||||
line "end.call"
|
|
||||||
|
|
||||||
line "if if_changed != @if_changed_last"
|
|
||||||
indent { echo "if_changed" }
|
|
||||||
line "end"
|
|
||||||
|
|
||||||
line "@if_changed_last = if_changed"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_capture(node)
|
|
||||||
line "#{var(node.to)} = lambda do |; liquid_out|"
|
|
||||||
hoist_var(node.to)
|
|
||||||
indent do
|
|
||||||
line "liquid_out = StringIO.new"
|
|
||||||
node.nodelist.each(&method(:compile))
|
|
||||||
line "liquid_out.string"
|
|
||||||
end
|
|
||||||
line "end.call"
|
|
||||||
end
|
|
||||||
|
|
||||||
def make_condition_expr(node)
|
|
||||||
condition = make_sub_condition_expr(node)
|
|
||||||
if node.child_condition
|
|
||||||
"(#{condition} #{node.child_relation} #{make_condition_expr(node.child_condition)})"
|
|
||||||
else
|
|
||||||
condition
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def make_sub_condition_expr(node)
|
|
||||||
return make_variable_expr(node.left) unless node.operator
|
|
||||||
|
|
||||||
operator = node.operator
|
|
||||||
operator = "!=" if operator == "<>"
|
|
||||||
|
|
||||||
if operator == "=="
|
|
||||||
if node.left.is_a?(Liquid::Expression::MethodLiteral) &&
|
|
||||||
node.right.is_a?(Liquid::Expression::MethodLiteral)
|
|
||||||
return "false"
|
|
||||||
elsif node.right.is_a?(Liquid::Expression::MethodLiteral)
|
|
||||||
target = make_variable_expr(node.left)
|
|
||||||
message = node.right.method_name.inspect
|
|
||||||
return "#{target}.respond_to?(#{message}) ? #{target}.send(#{message}) : nil"
|
|
||||||
elsif node.left.is_a?(Liquid::Expression::MethodLiteral)
|
|
||||||
target = make_variable_expr(node.right)
|
|
||||||
message = node.left.method_name.inspect
|
|
||||||
return "#{target}.respond_to?(#{message}) ? #{target}.send(#{message}) : nil"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
left = make_variable_expr(node.left)
|
|
||||||
right = make_variable_expr(node.right)
|
|
||||||
|
|
||||||
case operator
|
|
||||||
when "contains"
|
|
||||||
"contains?(#{left}, #{right})"
|
|
||||||
else
|
|
||||||
"apply_operator(#{left}, #{operator.inspect}, #{right})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_case(node)
|
|
||||||
line 'if false' # HACK
|
|
||||||
else_nodes, if_nodes = node.blocks.partition { |n| n.is_a?(Liquid::ElseCondition) }
|
|
||||||
raise SuperfluidError, 'Too many else nodes' if else_nodes.count > 1
|
|
||||||
else_node = else_nodes.first
|
|
||||||
|
|
||||||
if_nodes.each do |condition|
|
|
||||||
left = make_variable_expr(condition.left)
|
|
||||||
right = make_variable_expr(condition.right)
|
|
||||||
line "elsif #{left} #{condition.operator} #{right}"
|
|
||||||
indent { condition.attachment.nodelist.each(&method(:compile)) }
|
|
||||||
end
|
|
||||||
|
|
||||||
if else_node
|
|
||||||
line "else"
|
|
||||||
indent { else_node.attachment.nodelist.each(&method(:compile)) }
|
|
||||||
end
|
|
||||||
|
|
||||||
line "end"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_cycle(node)
|
|
||||||
key = node.name
|
|
||||||
key = key.name if key.is_a?(Liquid::VariableLookup)
|
|
||||||
|
|
||||||
line "key = #{key.inspect}"
|
|
||||||
line "iteration = context.registers[:cycle][key].to_i"
|
|
||||||
line "@cycle_values[key] ||= #{node.variables}"
|
|
||||||
|
|
||||||
line "val = @cycle_values[key][iteration]"
|
|
||||||
echo 'val'
|
|
||||||
line "context.registers[:cycle][key] = (iteration + 1) % #{node.variables.size}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_raw(node)
|
|
||||||
echo "#{node.body.inspect}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_increment(node)
|
|
||||||
line "value = context.environments.first[#{node.variable.inspect}] ||= 0"
|
|
||||||
line "@context.environments.first[#{node.variable.inspect}] = value + 1"
|
|
||||||
echo "value"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_decrement(node)
|
|
||||||
line "value = context.environments.first[#{node.variable.inspect}] ||= 0"
|
|
||||||
line "value -= 1"
|
|
||||||
line "@context.environments.first[#{node.variable.inspect}] = value"
|
|
||||||
echo "value"
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_echo_literal(node)
|
|
||||||
echo node.inspect
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_variable(variable)
|
|
||||||
echo make_variable_expr(variable)
|
|
||||||
end
|
|
||||||
|
|
||||||
def compile_assign(node)
|
|
||||||
from_expr = case node.from
|
|
||||||
when Liquid::Variable
|
|
||||||
make_variable_expr(node.from)
|
|
||||||
else
|
|
||||||
raise SuperfluidError, "Unknown assignment `from`: #{node.from.inspect}"
|
|
||||||
end
|
|
||||||
|
|
||||||
line "#{var(node.to)} = #{from_expr}"
|
|
||||||
hoist_var(node.to)
|
|
||||||
end
|
|
||||||
|
|
||||||
def make_variable_expr(variable)
|
|
||||||
case variable
|
|
||||||
when Liquid::Variable
|
|
||||||
base_expression = case variable.name
|
|
||||||
when TrueClass, FalseClass, Numeric, String
|
|
||||||
variable.name.inspect
|
|
||||||
when Liquid::VariableLookup
|
|
||||||
make_variable_lookup_expr(variable.name)
|
|
||||||
when NilClass, Liquid::Expression::MethodLiteral
|
|
||||||
'nil'
|
|
||||||
else
|
|
||||||
raise SuperfluidError, "Invalid variable name: #{variable.name.inspect}"
|
|
||||||
derp "Bad var name", variable.name
|
|
||||||
end
|
|
||||||
variable.filters.inject(base_expression) do |inner, (filter_name, positional_args, keyword_args)|
|
|
||||||
filter_args = positional_args.map(&method(:make_variable_expr))
|
|
||||||
|
|
||||||
if keyword_args
|
|
||||||
filter_args << "{ " + keyword_args
|
|
||||||
.transform_values(&method(:make_variable_expr))
|
|
||||||
.collect { |(key, value)| "#{key.inspect} => #{value}" }
|
|
||||||
.join(", ") + " }"
|
|
||||||
end
|
|
||||||
|
|
||||||
"context.strainer.invoke(#{filter_name.inspect}, #{inner}, *[#{filter_args.join(", ")}])"
|
|
||||||
end
|
|
||||||
when Liquid::VariableLookup
|
|
||||||
make_variable_lookup_expr(variable)
|
|
||||||
when TrueClass, FalseClass, Numeric, String
|
|
||||||
variable.inspect
|
|
||||||
when NilClass
|
|
||||||
'nil'
|
|
||||||
else
|
|
||||||
raise SuperfluidError, "Unknown expression type: #{variable.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def make_variable_lookup_expr(variable_lookup)
|
|
||||||
base_expr = var(variable_lookup.name)
|
|
||||||
hoist_var(variable_lookup.name)
|
|
||||||
return base_expr if variable_lookup.lookups.empty?
|
|
||||||
|
|
||||||
expr = Output.new(output.indent_level)
|
|
||||||
expr.line "(begin"
|
|
||||||
expr.indent do
|
|
||||||
expr.line "inner = #{base_expr}"
|
|
||||||
variable_lookup.lookups.each_with_index do |lookup, i|
|
|
||||||
lookup_expr = lookup.inspect
|
|
||||||
expr.line "inner = if inner.respond_to?(:[]) && ((inner.respond_to?(:key?) && inner.key?(#{lookup_expr})) || (inner.respond_to?(:fetch) && #{lookup_expr}.is_a?(Integer)))"
|
|
||||||
expr.indent do
|
|
||||||
expr.line "inner[#{lookup_expr}].to_liquid"
|
|
||||||
end
|
|
||||||
if variable_lookup.command_flags & (1 << i) != 0
|
|
||||||
expr.line "elsif inner.respond_to?(#{lookup.inspect})"
|
|
||||||
expr.indent do
|
|
||||||
expr.line "inner.#{lookup}.to_liquid"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
expr.line "end"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
expr.line "end)"
|
|
||||||
expr.string.strip
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def line(string)
|
|
||||||
output.line(string)
|
|
||||||
end
|
|
||||||
|
|
||||||
def echo(string)
|
|
||||||
unless @blank
|
|
||||||
line "liquid_out.write(to_output(#{string}))"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def indent(&block)
|
|
||||||
output.indent(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def var(name)
|
|
||||||
name = name
|
|
||||||
.gsub('_', '__')
|
|
||||||
.gsub('-', '_')
|
|
||||||
"__liquid_#{name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def unvar(name)
|
|
||||||
name
|
|
||||||
.delete_prefix('__liquid_')
|
|
||||||
.gsub(/([^_])_([^_])/) { "#$1-#$2" }
|
|
||||||
.gsub('__', '_')
|
|
||||||
end
|
|
||||||
|
|
||||||
def hoist_var(name)
|
|
||||||
@variables << var(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -36,14 +36,6 @@ module Liquid
|
|||||||
''.freeze
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
# For backwards compatibility with custom tags. In a future release, the semantics
|
|
||||||
# of the `render_to_output_buffer` method will become the default and the `render`
|
|
||||||
# method will be removed.
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
output << render(context)
|
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
val = @from.render(context)
|
val = @from.render(context)
|
||||||
context.scopes.last[@to] = val
|
context.scopes.last[@to] = val
|
||||||
context.resource_limits.assign_score += assign_score_of(val)
|
context.resource_limits.assign_score += assign_score_of(val)
|
||||||
output
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ module Liquid
|
|||||||
class Capture < Block
|
class Capture < Block
|
||||||
Syntax = /(#{VariableSignature}+)/o
|
Syntax = /(#{VariableSignature}+)/o
|
||||||
|
|
||||||
attr_reader :to
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
@@ -24,18 +22,16 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
previous_output_size = output.bytesize
|
output = super
|
||||||
super
|
|
||||||
context.scopes.last[@to] = output
|
context.scopes.last[@to] = output
|
||||||
context.resource_limits.assign_score += (output.bytesize - previous_output_size)
|
context.resource_limits.assign_score += output.bytesize
|
||||||
output
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('capture'.freeze, Capture)
|
Template.register_tag('capture'.freeze, Capture)
|
||||||
|
|||||||
@@ -38,21 +38,21 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
execute_else_block = true
|
execute_else_block = true
|
||||||
|
|
||||||
|
output = ''
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
if block.else?
|
if block.else?
|
||||||
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
return block.attachment.render(context) if execute_else_block
|
||||||
elsif block.evaluate(context)
|
elsif block.evaluate(context)
|
||||||
execute_else_block = false
|
execute_else_block = false
|
||||||
block.attachment.render_to_output_buffer(context, output)
|
output << block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
output
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Comment < Block
|
class Comment < Block
|
||||||
def render_to_output_buffer(_context, output)
|
def render(_context)
|
||||||
output
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def unknown_tag(_tag, _markup, _tokens)
|
def unknown_tag(_tag, _markup, _tokens)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ module Liquid
|
|||||||
SimpleSyntax = /\A#{QuotedFragment}+/o
|
SimpleSyntax = /\A#{QuotedFragment}+/o
|
||||||
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
||||||
|
|
||||||
attr_reader :variables, :name
|
attr_reader :variables
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@@ -31,29 +31,18 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.registers[:cycle] ||= {}
|
context.registers[:cycle] ||= {}
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
key = context.evaluate(@name)
|
key = context.evaluate(@name)
|
||||||
iteration = context.registers[:cycle][key].to_i
|
iteration = context.registers[:cycle][key].to_i
|
||||||
|
result = context.evaluate(@variables[iteration])
|
||||||
val = context.evaluate(@variables[iteration])
|
|
||||||
|
|
||||||
if val.is_a?(Array)
|
|
||||||
val = val.join
|
|
||||||
elsif !val.is_a?(String)
|
|
||||||
val = val.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
output << val
|
|
||||||
|
|
||||||
iteration += 1
|
iteration += 1
|
||||||
iteration = 0 if iteration >= @variables.size
|
iteration = 0 if iteration >= @variables.size
|
||||||
context.registers[:cycle][key] = iteration
|
context.registers[:cycle][key] = iteration
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -23,14 +23,11 @@ module Liquid
|
|||||||
@variable = markup.strip
|
@variable = markup.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :variable
|
def render(context)
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
value = context.environments.first[@variable] ||= 0
|
value = context.environments.first[@variable] ||= 0
|
||||||
value -= 1
|
value -= 1
|
||||||
context.environments.first[@variable] = value
|
context.environments.first[@variable] = value
|
||||||
output << value.to_s
|
value.to_s
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ module Liquid
|
|||||||
class For < Block
|
class For < Block
|
||||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||||
|
|
||||||
attr_reader :collection_name, :variable_name, :limit, :from, :for_block, :else_block, :name, :reversed
|
attr_reader :collection_name, :variable_name, :limit, :from
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@@ -70,16 +70,14 @@ module Liquid
|
|||||||
@else_block = BlockBody.new
|
@else_block = BlockBody.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
segment = collection_segment(context)
|
segment = collection_segment(context)
|
||||||
|
|
||||||
if segment.empty?
|
if segment.empty?
|
||||||
render_else(context, output)
|
render_else(context)
|
||||||
else
|
else
|
||||||
render_segment(context, output, segment)
|
render_segment(context, segment)
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@@ -152,10 +150,12 @@ module Liquid
|
|||||||
segment
|
segment
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_segment(context, output, segment)
|
def render_segment(context, segment)
|
||||||
for_stack = context.registers[:for_stack] ||= []
|
for_stack = context.registers[:for_stack] ||= []
|
||||||
length = segment.length
|
length = segment.length
|
||||||
|
|
||||||
|
result = ''
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ module Liquid
|
|||||||
|
|
||||||
segment.each do |item|
|
segment.each do |item|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
@for_block.render_to_output_buffer(context, output)
|
result << @for_block.render(context)
|
||||||
loop_vars.send(:increment!)
|
loop_vars.send(:increment!)
|
||||||
|
|
||||||
# Handle any interrupts if they exist.
|
# Handle any interrupts if they exist.
|
||||||
@@ -181,7 +181,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_attribute(key, expr)
|
def set_attribute(key, expr)
|
||||||
@@ -197,12 +197,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_else(context, output)
|
def render_else(context)
|
||||||
if @else_block
|
@else_block ? @else_block.render(context) : ''.freeze
|
||||||
@else_block.render_to_output_buffer(context, output)
|
|
||||||
else
|
|
||||||
output
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||||
|
|||||||
@@ -39,16 +39,15 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
if block.evaluate(context)
|
if block.evaluate(context)
|
||||||
return block.attachment.render_to_output_buffer(context, output)
|
return block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Ifchanged < Block
|
class Ifchanged < Block
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
block_output = ''
|
output = super
|
||||||
super(context, block_output)
|
|
||||||
|
|
||||||
if block_output != context.registers[:ifchanged]
|
if output != context.registers[:ifchanged]
|
||||||
context.registers[:ifchanged] = block_output
|
context.registers[:ifchanged] = output
|
||||||
output << block_output
|
output
|
||||||
|
else
|
||||||
|
''.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ module Liquid
|
|||||||
def parse(_tokens)
|
def parse(_tokens)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
template_name = context.evaluate(@template_name_expr)
|
template_name = context.evaluate(@template_name_expr)
|
||||||
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
||||||
|
|
||||||
@@ -66,21 +66,19 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
if variable.is_a?(Array)
|
if variable.is_a?(Array)
|
||||||
variable.each do |var|
|
variable.collect do |var|
|
||||||
context[context_variable_name] = var
|
context[context_variable_name] = var
|
||||||
partial.render_to_output_buffer(context, output)
|
partial.render(context)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
context[context_variable_name] = variable
|
context[context_variable_name] = variable
|
||||||
partial.render_to_output_buffer(context, output)
|
partial.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
context.template_name = old_template_name
|
context.template_name = old_template_name
|
||||||
context.partial = old_partial
|
context.partial = old_partial
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -20,13 +20,10 @@ module Liquid
|
|||||||
@variable = markup.strip
|
@variable = markup.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :variable
|
def render(context)
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
value = context.environments.first[@variable] ||= 0
|
value = context.environments.first[@variable] ||= 0
|
||||||
context.environments.first[@variable] = value + 1
|
context.environments.first[@variable] = value + 1
|
||||||
output << value.to_s
|
value.to_s
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ module Liquid
|
|||||||
Syntax = /\A\s*\z/
|
Syntax = /\A\s*\z/
|
||||||
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
||||||
|
|
||||||
attr_reader :body
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, parse_context)
|
def initialize(tag_name, markup, parse_context)
|
||||||
super
|
super
|
||||||
|
|
||||||
@@ -24,9 +22,8 @@ module Liquid
|
|||||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(_context, output)
|
def render(_context)
|
||||||
output << @body
|
@body
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def nodelist
|
def nodelist
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
collection = context.evaluate(@collection_name) or return ''.freeze
|
collection = context.evaluate(@collection_name) or return ''.freeze
|
||||||
|
|
||||||
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
|
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
|
||||||
@@ -30,7 +30,7 @@ module Liquid
|
|||||||
|
|
||||||
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
||||||
|
|
||||||
output << "<tr class=\"row1\">\n"
|
result = "<tr class=\"row1\">\n"
|
||||||
context.stack do
|
context.stack do
|
||||||
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
||||||
context['tablerowloop'.freeze] = tablerowloop
|
context['tablerowloop'.freeze] = tablerowloop
|
||||||
@@ -38,20 +38,17 @@ module Liquid
|
|||||||
collection.each do |item|
|
collection.each do |item|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
|
|
||||||
output << "<td class=\"col#{tablerowloop.col}\">"
|
result << "<td class=\"col#{tablerowloop.col}\">" << super << '</td>'
|
||||||
super
|
|
||||||
output << '</td>'
|
|
||||||
|
|
||||||
if tablerowloop.col_last && !tablerowloop.last
|
if tablerowloop.col_last && !tablerowloop.last
|
||||||
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
result << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
||||||
end
|
end
|
||||||
|
|
||||||
tablerowloop.send(:increment!)
|
tablerowloop.send(:increment!)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
result << "</tr>\n"
|
||||||
output << "</tr>\n"
|
result
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||||
|
|||||||
@@ -6,23 +6,23 @@ module Liquid
|
|||||||
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
||||||
#
|
#
|
||||||
class Unless < If
|
class Unless < If
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
# First condition is interpreted backwards ( if not )
|
# First condition is interpreted backwards ( if not )
|
||||||
first_block = @blocks.first
|
first_block = @blocks.first
|
||||||
unless first_block.evaluate(context)
|
unless first_block.evaluate(context)
|
||||||
return first_block.attachment.render_to_output_buffer(context, output)
|
return first_block.attachment.render(context)
|
||||||
end
|
end
|
||||||
|
|
||||||
# After the first condition unless works just like if
|
# After the first condition unless works just like if
|
||||||
@blocks[1..-1].each do |block|
|
@blocks[1..-1].each do |block|
|
||||||
if block.evaluate(context)
|
if block.evaluate(context)
|
||||||
return block.attachment.render_to_output_buffer(context, output)
|
return block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
output
|
''.freeze
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require 'liquid/ast_to_portable_json'
|
||||||
module Liquid
|
module Liquid
|
||||||
# Templates are central to liquid.
|
# Templates are central to liquid.
|
||||||
# Interpretating templates is a two step process. First you compile the
|
# Interpretating templates is a two step process. First you compile the
|
||||||
@@ -50,7 +51,7 @@ module Liquid
|
|||||||
private
|
private
|
||||||
|
|
||||||
def lookup_class(name)
|
def lookup_class(name)
|
||||||
Object.const_get(name)
|
name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -130,6 +131,8 @@ module Liquid
|
|||||||
@line_numbers = options[:line_numbers] || @profiling
|
@line_numbers = options[:line_numbers] || @profiling
|
||||||
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
||||||
@root = Document.parse(tokenize(source), parse_context)
|
@root = Document.parse(tokenize(source), parse_context)
|
||||||
|
@json = ASTToPortableJSON.dump(self)
|
||||||
|
|
||||||
@warnings = parse_context.warnings
|
@warnings = parse_context.warnings
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
@@ -187,12 +190,9 @@ module Liquid
|
|||||||
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
||||||
end
|
end
|
||||||
|
|
||||||
output = nil
|
|
||||||
|
|
||||||
case args.last
|
case args.last
|
||||||
when Hash
|
when Hash
|
||||||
options = args.pop
|
options = args.pop
|
||||||
output = options[:output] if options[:output]
|
|
||||||
|
|
||||||
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
||||||
|
|
||||||
@@ -201,15 +201,25 @@ module Liquid
|
|||||||
context.add_filters(args.pop)
|
context.add_filters(args.pop)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless @truffle_context
|
||||||
|
environment = context.environments.first
|
||||||
|
@truffle_context = Polyglot.eval('liquid', @json)
|
||||||
|
environment.each do |key, value|
|
||||||
|
@truffle_context[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return @truffle_context[:render].call
|
||||||
|
|
||||||
# Retrying a render resets resource usage
|
# Retrying a render resets resource usage
|
||||||
context.resource_limits.reset
|
context.resource_limits.reset
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# render the nodelist.
|
# render the nodelist.
|
||||||
|
# for performance reasons we get an array back here. join will make a string out of it.
|
||||||
with_profiling(context) do
|
result = with_profiling(context) do
|
||||||
@root.render_to_output_buffer(context, output || '')
|
@root.render(context)
|
||||||
end
|
end
|
||||||
|
result.respond_to?(:join) ? result.join : result
|
||||||
rescue Liquid::MemoryError => e
|
rescue Liquid::MemoryError => e
|
||||||
context.handle_error(e)
|
context.handle_error(e)
|
||||||
ensure
|
ensure
|
||||||
@@ -222,10 +232,6 @@ module Liquid
|
|||||||
render(*args)
|
render(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
render(context, output: output)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def tokenize(source)
|
def tokenize(source)
|
||||||
|
|||||||
@@ -85,23 +85,12 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
obj = context.apply_global_filter(obj)
|
obj = context.apply_global_filter(obj)
|
||||||
|
|
||||||
taint_check(context, obj)
|
taint_check(context, obj)
|
||||||
|
|
||||||
obj
|
obj
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
obj = render(context)
|
|
||||||
|
|
||||||
if obj.is_a?(Array)
|
|
||||||
output << obj.join
|
|
||||||
elsif obj.nil?
|
|
||||||
else
|
|
||||||
output << obj.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_filter_expressions(filter_name, unparsed_args)
|
def parse_filter_expressions(filter_name, unparsed_args)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module Liquid
|
|||||||
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
||||||
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
|
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
|
||||||
|
|
||||||
attr_reader :name, :lookups, :command_flags
|
attr_reader :name, :lookups
|
||||||
|
|
||||||
def self.parse(markup)
|
def self.parse(markup)
|
||||||
new(markup)
|
new(markup)
|
||||||
|
|||||||
@@ -1,27 +1,20 @@
|
|||||||
|
require 'bundler/setup'
|
||||||
require 'benchmark/ips'
|
require 'benchmark/ips'
|
||||||
require_relative 'theme_runner'
|
require_relative 'theme_runner'
|
||||||
|
|
||||||
case ARGV.first.to_sym
|
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
||||||
when :lax
|
|
||||||
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
|
||||||
when :strict
|
|
||||||
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
|
||||||
when :superfluid
|
|
||||||
require 'liquid/superfluid'
|
|
||||||
Liquid::Template.error_mode = :strict
|
|
||||||
end
|
|
||||||
|
|
||||||
profiler = ThemeRunner.new
|
profiler = ThemeRunner.new
|
||||||
|
|
||||||
Benchmark.ips do |x|
|
Benchmark.ips do |x|
|
||||||
x.time = 10
|
x.time = 60
|
||||||
x.warmup = 5
|
x.warmup = 1
|
||||||
|
|
||||||
puts
|
puts
|
||||||
puts "Running benchmark for #{x.time} seconds (with #{x.warmup} seconds warmup)."
|
puts "Running benchmark for #{x.time} seconds (with #{x.warmup} seconds warmup)."
|
||||||
puts
|
puts
|
||||||
|
|
||||||
x.report("parse:") { profiler.compile }
|
profiler.compile
|
||||||
|
# x.report("parse:") { profiler.compile }
|
||||||
x.report("render:") { profiler.render }
|
x.report("render:") { profiler.render }
|
||||||
x.report("parse & render:") { profiler.run }
|
x.report("parse & render:") { profiler.run }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
requirf 'benchmark/ips'
|
require 'benchmark/ips'
|
||||||
require 'memory_profiler'
|
require 'memory_profiler'
|
||||||
require_relative 'theme_runner'
|
require_relative 'theme_runner'
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class CommentForm < Liquid::Block
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
article = context[@variable_name]
|
article = context[@variable_name]
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
@@ -23,9 +23,7 @@ class CommentForm < Liquid::Block
|
|||||||
'email' => context['comment.email'],
|
'email' => context['comment.email'],
|
||||||
'body' => context['comment.body']
|
'body' => context['comment.body']
|
||||||
}
|
}
|
||||||
|
wrap_in_form(article, render_all(@nodelist, context))
|
||||||
output << wrap_in_form(article, render_all(@nodelist, context, output))
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Paginate < Liquid::Block
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
@context = context
|
@context = context
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ThemeRunner
|
|||||||
# Initialize a new liquid ThemeRunner instance
|
# Initialize a new liquid ThemeRunner instance
|
||||||
# Will load all templates into memory, do this now so that we don't profile IO.
|
# Will load all templates into memory, do this now so that we don't profile IO.
|
||||||
def initialize
|
def initialize
|
||||||
@tests = Dir[__dir__ + '/tests/**/*.liquid'].collect do |test|
|
@tests = Dir[__dir__ + '/tests/ripen/product.liquid'].collect do |test|
|
||||||
next if File.basename(test) == 'theme.liquid'
|
next if File.basename(test) == 'theme.liquid'
|
||||||
|
|
||||||
theme_path = File.dirname(test) + '/theme.liquid'
|
theme_path = File.dirname(test) + '/theme.liquid'
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class FoobarTag < Liquid::Tag
|
class FoobarTag < Liquid::Tag
|
||||||
def render_to_output_buffer(context, output)
|
def render(*args)
|
||||||
output << ' '
|
" "
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Liquid::Template.register_tag('foobar', FoobarTag)
|
||||||
end
|
end
|
||||||
|
|
||||||
class BlankTestFileSystem
|
class BlankTestFileSystem
|
||||||
@@ -30,9 +31,7 @@ class BlankTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_new_tags_are_not_blank_by_default
|
def test_new_tags_are_not_blank_by_default
|
||||||
with_custom_tag('foobar', FoobarTag) do
|
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
||||||
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_loops_are_blank
|
def test_loops_are_blank
|
||||||
|
|||||||
@@ -66,9 +66,8 @@ class CustomInclude < Liquid::Tag
|
|||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
output << @template_name[1..-2]
|
@template_name[1..-2]
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ if ENV['LIQUID-C'] == '1'
|
|||||||
require 'liquid/c'
|
require 'liquid/c'
|
||||||
end
|
end
|
||||||
|
|
||||||
if ENV['SUPERFLUID'] == '1'
|
|
||||||
puts "-- SUPERFLUID"
|
|
||||||
require 'liquid/superfluid'
|
|
||||||
end
|
|
||||||
|
|
||||||
if Minitest.const_defined?('Test')
|
if Minitest.const_defined?('Test')
|
||||||
# We're on Minitest 5+. Nothing to do here.
|
# We're on Minitest 5+. Nothing to do here.
|
||||||
else
|
else
|
||||||
@@ -89,13 +84,6 @@ module Minitest
|
|||||||
ensure
|
ensure
|
||||||
Liquid::Template.error_mode = old_mode
|
Liquid::Template.error_mode = old_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_custom_tag(tag_name, tag_class)
|
|
||||||
Liquid::Template.register_tag(tag_name, tag_class)
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
Liquid::Template.tags.delete(tag_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -44,47 +44,10 @@ class BlockUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_with_custom_tag
|
def test_with_custom_tag
|
||||||
with_custom_tag('testtag', Block) do
|
Liquid::Template.register_tag("testtag", Block)
|
||||||
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
||||||
end
|
ensure
|
||||||
end
|
Liquid::Template.tags.delete('testtag')
|
||||||
|
|
||||||
def test_custom_block_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
|
|
||||||
klass1 = Class.new(Block) do
|
|
||||||
def render(*)
|
|
||||||
'hello'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass1) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %} bla {% endblabla %}")
|
|
||||||
|
|
||||||
assert_equal 'hello', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'hello', output
|
|
||||||
assert_equal 'hello', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
|
|
||||||
klass2 = Class.new(klass1) do
|
|
||||||
def render(*)
|
|
||||||
'foo' + super + 'bar'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass2) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %} foo {% endblabla %}")
|
|
||||||
|
|
||||||
assert_equal 'foohellobar', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'foohellobar', output
|
|
||||||
assert_equal 'foohellobar', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -18,42 +18,4 @@ class TagUnitTest < Minitest::Test
|
|||||||
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
||||||
assert_equal 'some_tag', tag.tag_name
|
assert_equal 'some_tag', tag.tag_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_custom_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
|
|
||||||
klass1 = Class.new(Tag) do
|
|
||||||
def render(*)
|
|
||||||
'hello'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass1) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %}")
|
|
||||||
|
|
||||||
assert_equal 'hello', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'hello', output
|
|
||||||
assert_equal 'hello', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
|
|
||||||
klass2 = Class.new(klass1) do
|
|
||||||
def render(*)
|
|
||||||
'foo' + super + 'bar'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass2) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %}")
|
|
||||||
|
|
||||||
assert_equal 'foohellobar', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'foohellobar', output
|
|
||||||
assert_equal 'foohellobar', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user