Compare commits

..
51 changed files with 245 additions and 902 deletions
-2
View File
@@ -1,2 +0,0 @@
enabled:
- cla
+22
View File
@@ -0,0 +1,22 @@
name: Contributor License Agreement (CLA)
on:
pull_request_target:
types: [opened, synchronize]
issue_comment:
types: [created]
jobs:
cla:
runs-on: ubuntu-latest
if: |
(github.event.issue.pull_request
&& !github.event.issue.pull_request.merged_at
&& contains(github.event.comment.body, 'signed')
)
|| (github.event.pull_request && !github.event.pull_request.merged)
steps:
- uses: Shopify/shopify-cla-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
cla-token: ${{ secrets.CLA_TOKEN }}
+27
View File
@@ -0,0 +1,27 @@
name: Liquid
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
entry:
- { ruby: "2.7" } # minimum supported
- { ruby: "3.0", rubocop: true } # latest working with rubocop
- { ruby: "3.2" } # latest
name: test (${{ matrix.entry.ruby }})
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.entry.ruby }}
- uses: actions/cache@v1
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile') }}
restore-keys: ${{ runner.os }}-gems-
- run: bundle install --jobs=3 --retry=3 --path=vendor/bundle
- run: bundle exec rake test
- name: Run rubocop
if: matrix.entry.rubocop
run: bundle exec rubocop
-1
View File
@@ -7,4 +7,3 @@ pkg
.ruby-version
Gemfile.lock
.bundle
.byebug_history
+12 -16
View File
@@ -1,17 +1,10 @@
inherit_from:
- .rubocop_todo.yml
- ./.rubocop_todo.yml
require: rubocop-performance
Performance:
Enabled: true
inherit_from: ./.rubocop_todo.yml
AllCops:
Exclude:
- 'performance/shopify/*'
- 'vendor/bundle/**/*'
- 'pkg/**'
- 'vendor/bundle/**/*'
Metrics/BlockNesting:
Max: 3
@@ -37,7 +30,7 @@ Lint/ParenthesesAsGroupedExpression:
Lint/UnusedBlockArgument:
Enabled: false
Layout/EndAlignment:
Lint/EndAlignment:
EnforcedStyleAlignWith: variable
Lint/UnusedMethodArgument:
@@ -67,7 +60,7 @@ Style/BracesAroundHashParameters:
Style/NumericLiterals:
Enabled: false
Layout/SpaceInsideArrayLiteralBrackets:
Layout/SpaceInsideBrackets:
Enabled: false
Layout/SpaceBeforeBlockBraces:
@@ -79,11 +72,11 @@ Style/Documentation:
Style/ClassAndModuleChildren:
Enabled: false
Style/TrailingCommaInArrayLiteral:
Style/TrailingCommaInLiteral:
Enabled: false
Style/TrailingCommaInHashLiteral:
Enabled: false
Layout/IndentHash:
EnforcedStyle: consistent
Style/FormatString:
Enabled: false
@@ -109,7 +102,10 @@ Style/RegexpLiteral:
Style/SymbolLiteral:
Enabled: false
Naming/ConstantName:
Performance/Count:
Enabled: false
Style/ConstantName:
Enabled: false
Layout/CaseIndentation:
@@ -127,6 +123,6 @@ Style/TrivialAccessors:
Style/WordArray:
Enabled: false
Naming/MethodName:
Style/MethodName:
Exclude:
- 'example/server/liquid_servlet.rb'
+32 -183
View File
@@ -1,74 +1,15 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-08-29 00:43:36 +1000 using RuboCop version 0.74.0.
# on 2019-02-03 21:12:39 +0530 using RuboCop version 0.49.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: TreatCommentsAsGroupSeparators, Include.
# Include: **/*.gemspec
Gemspec/OrderedDependencies:
Exclude:
- 'liquid.gemspec'
# Offense count: 1
# Configuration parameters: Include.
# Include: **/*.gemspec,
Gemspec/RequiredRubyVersion:
Exclude:
- 'liquid.gemspec'
# Offense count: 124
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: with_first_argument, with_fixed_indentation
Layout/AlignArguments:
Enabled: false
# Offense count: 7
# Cop supports --auto-correct.
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
# SupportedHashRocketStyles: key, separator, table
# SupportedColonStyles: key, separator, table
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
Layout/AlignHash:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/expression.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 6
# Cop supports --auto-correct.
Layout/ClosingHeredocIndentation:
Exclude:
- 'test/integration/tags/for_tag_test.rb'
# Offense count: 27
# Cop supports --auto-correct.
Layout/EmptyLineAfterGuardClause:
Exclude:
- 'lib/liquid/block.rb'
- 'lib/liquid/block_body.rb'
- 'lib/liquid/context.rb'
- 'lib/liquid/drop.rb'
- 'lib/liquid/lexer.rb'
- 'lib/liquid/parser.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/tags/for.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/utils.rb'
- 'lib/liquid/variable.rb'
- 'lib/liquid/variable_lookup.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: squiggly, active_support, powerpack, unindent
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent
Layout/IndentHeredoc:
Exclude:
- 'test/integration/tags/for_tag_test.rb'
@@ -76,87 +17,47 @@ Layout/IndentHeredoc:
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: symmetrical, new_line, same_line
Layout/MultilineMethodCallBraceLayout:
Exclude:
- 'test/integration/error_handling_test.rb'
- 'test/unit/strainer_unit_test.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
Layout/SpaceAroundOperators:
Exclude:
- 'lib/liquid/condition.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: runtime_error, standard_error
Lint/InheritException:
Exclude:
- 'lib/liquid/interrupts.rb'
# Offense count: 1
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'lib/liquid/parse_context.rb'
# Offense count: 53
# Offense count: 51
Metrics/AbcSize:
Max: 56
# Offense count: 12
# Offense count: 11
Metrics/CyclomaticComplexity:
Max: 13
Max: 12
# Offense count: 118
# Configuration parameters: CountComments, ExcludedMethods.
# Offense count: 639
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 294
# Offense count: 108
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 38
Max: 37
# Offense count: 9
# Offense count: 7
Metrics/PerceivedComplexity:
Max: 11
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: PreferredName.
Naming/RescuedExceptionsVariableName:
Exclude:
- 'lib/liquid/context.rb'
# Offense count: 20
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
# AllowedNames: io, id, to, by, on, in, at, ip, db
Naming/UncommunicativeMethodParamName:
Exclude:
- 'example/server/example_servlet.rb'
- 'lib/liquid/condition.rb'
- 'lib/liquid/context.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/variable.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/condition_unit_test.rb'
# Offense count: 5
# Configuration parameters: EnforcedStyle.
# SupportedStyles: inline, group
Style/AccessModifierDeclarations:
Exclude:
- 'lib/liquid/block_body.rb'
- 'lib/liquid/tag.rb'
- 'lib/liquid/tags/include.rb'
- 'test/unit/strainer_unit_test.rb'
# Offense count: 10
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: prefer_alias, prefer_alias_method
Style/Alias:
Exclude:
@@ -168,27 +69,24 @@ Style/Alias:
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/variable.rb'
# Offense count: 22
Style/CommentedKeyword:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Exclude:
- 'lib/liquid/errors.rb'
# Offense count: 1
# Offense count: 2
# Cop supports --auto-correct.
Style/EmptyCaseCondition:
Exclude:
- 'lib/liquid/block_body.rb'
- 'lib/liquid/lexer.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, expanded
Style/EmptyMethod:
Exclude:
@@ -198,35 +96,11 @@ Style/EmptyMethod:
- 'test/integration/tags/include_tag_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 3
# Cop supports --auto-correct.
Style/Encoding:
Exclude:
- 'lib/liquid/version.rb'
- 'liquid.gemspec'
- 'test/integration/standard_filter_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/ExpandPathArguments:
Exclude:
- 'Rakefile'
- 'liquid.gemspec'
# Offense count: 7
# Configuration parameters: EnforcedStyle.
# SupportedStyles: annotated, template, unannotated
# Configuration parameters: SupportedStyles.
# SupportedStyles: annotated, template
Style/FormatStringToken:
Exclude:
- 'test/integration/filter_test.rb'
- 'test/integration/hash_ordering_test.rb'
# Offense count: 106
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, never
Style/FrozenStringLiteralComment:
Enabled: false
EnforcedStyle: template
# Offense count: 14
# Configuration parameters: MinBodyLength.
@@ -245,24 +119,17 @@ Style/GuardClause:
- 'lib/liquid/variable.rb'
- 'test/unit/tokenizer_unit_test.rb'
# Offense count: 53
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
Style/MutableConstant:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength.
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
# SupportedStyles: skip_modifier_ifs, always
Style/Next:
Exclude:
- 'lib/liquid/tags/for.rb'
# Offense count: 13
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
# SupportedStyles: predicate, comparison
Style/NumericPredicate:
Exclude:
@@ -271,8 +138,6 @@ Style/NumericPredicate:
- 'lib/liquid/forloop_drop.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tablerowloop_drop.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/template_test.rb'
# Offense count: 14
# Cop supports --auto-correct.
@@ -290,16 +155,6 @@ Style/RedundantSelf:
Exclude:
- 'lib/liquid/strainer.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, Whitelist.
# Whitelist: present?, blank?, presence, try, try!
Style/SafeNavigation:
Exclude:
- 'lib/liquid/drop.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/tokenizer.rb'
# Offense count: 9
# Cop supports --auto-correct.
# Configuration parameters: AllowAsExpressionSeparator.
@@ -311,14 +166,14 @@ Style/Semicolon:
# Offense count: 7
# Cop supports --auto-correct.
# Configuration parameters: MinSize.
# Configuration parameters: MinSize, SupportedStyles.
# SupportedStyles: percent, brackets
Style/SymbolArray:
EnforcedStyle: brackets
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
Style/TernaryParentheses:
Exclude:
@@ -333,13 +188,7 @@ Style/UnneededPercentQ:
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: MaxLineLength.
Style/WhileUntilModifier:
Exclude:
- 'lib/liquid/tags/case.rb'
# Offense count: 665
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 294
-28
View File
@@ -1,28 +0,0 @@
language: ruby
rvm:
- 2.4
- 2.5
- &latest_ruby 2.6
- 2.7
- ruby-head
- jruby-head
- truffleruby
matrix:
include:
- rvm: *latest_ruby
script: bundle exec rake memory_profile:run
name: Profiling Memory Usage
allow_failures:
- rvm: ruby-head
- rvm: jruby-head
- rvm: truffleruby
cache: bundler
script: bundle exec rake
notifications:
disable: true
+5 -10
View File
@@ -5,21 +5,16 @@ end
gemspec
gem 'stackprof', platforms: :mri
group :benchmark, :test do
gem 'benchmark-ips'
gem 'memory_profiler'
gem 'terminal-table'
install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ && RUBY_ENGINE != 'truffleruby' } do
gem 'stackprof'
end
end
group :test do
gem 'rubocop', '~> 0.74.0', require: false
gem 'rubocop-performance', require: false
gem 'rubocop', '~> 0.49.0'
platform :mri, :truffleruby do
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'liquid-tag'
platform :mri do
gem 'liquid-c', '~> 4.0.1'
end
end
+5
View File
@@ -1,5 +1,10 @@
# Liquid Change Log
## 4.0.4 / (unreleased)
### Fixed
* Fix ruby 3.2 compatibility by avoiding use of the removed taint API
## 4.0.3 / 2019-03-12
### Fixed
+4 -13
View File
@@ -19,10 +19,8 @@ task :warn_test do
end
task :rubocop do
if RUBY_ENGINE == 'ruby'
require 'rubocop/rake_task'
RuboCop::RakeTask.new
end
require 'rubocop/rake_task'
RuboCop::RakeTask.new
end
desc 'runs test suite with both strict and lax parsers'
@@ -34,8 +32,8 @@ task :test do
Rake::Task['base_test'].reenable
Rake::Task['base_test'].invoke
if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'truffleruby'
ENV['LIQUID_C'] = '1'
if RUBY_ENGINE == 'ruby'
ENV['LIQUID-C'] = '1'
ENV['LIQUID_PARSER_MODE'] = 'lax'
Rake::Task['base_test'].reenable
@@ -87,13 +85,6 @@ namespace :profile do
end
end
namespace :memory_profile do
desc "Run memory profiler"
task :run do
ruby "./performance/memory_profile.rb"
end
end
desc "Run example"
task :example do
ruby "-w -d -Ilib example/server/server.rb"
-1
View File
@@ -13,7 +13,6 @@ module Liquid
end
end
# For backwards compatibility
def render(context)
@body.render(context)
end
+21 -67
View File
@@ -1,7 +1,6 @@
module Liquid
class BlockBody
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
FullToken = /\A#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
WhitespaceOrNothing = /\A\s*\z/
TAGSTART = "{%".freeze
@@ -14,42 +13,8 @@ module Liquid
@blank = true
end
def parse(tokenizer, parse_context, &block)
def parse(tokenizer, parse_context)
parse_context.line_number = tokenizer.line_number
if tokenizer.for_liquid_tag
parse_for_liquid_tag(tokenizer, parse_context, &block)
else
parse_for_document(tokenizer, parse_context, &block)
end
end
private def parse_for_liquid_tag(tokenizer, parse_context)
while token = tokenizer.shift
unless token.empty? || token =~ WhitespaceOrNothing
unless token =~ LiquidTagToken
# line isn't empty but didn't match tag syntax, yield and let the
# caller raise a syntax error
return yield token, token
end
tag_name = $1
markup = $2
unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed
return yield tag_name, markup
end
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
@blank &&= new_tag.blank?
@nodelist << new_tag
end
parse_context.line_number = tokenizer.line_number
end
yield nil, nil
end
private def parse_for_document(tokenizer, parse_context, &block)
while token = tokenizer.shift
next if token.empty?
case
@@ -58,20 +23,9 @@ module Liquid
unless token =~ FullToken
raise_missing_tag_terminator(token, parse_context)
end
tag_name = $2
markup = $4
if parse_context.line_number
# newlines inside the tag should increase the line number,
# particularly important for multiline {% liquid %} tags
parse_context.line_number += $1.count("\n".freeze) + $3.count("\n".freeze)
end
if tag_name == 'liquid'.freeze
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true)
next parse_for_liquid_tag(liquid_tag_tokenizer, parse_context, &block)
end
tag_name = $1
markup = $2
# fetch the tag from registered blocks
unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed
@@ -113,23 +67,19 @@ module Liquid
end
def render(context)
render_to_output_buffer(context, '')
end
def render_to_output_buffer(context, output)
output = []
context.resource_limits.render_score += @nodelist.length
idx = 0
while node = @nodelist[idx]
previous_output_size = output.bytesize
case node
when String
check_resources(context, node)
output << node
when Variable
render_node(context, output, node)
render_node_to_output(node, output, context)
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
when Continue, Break
# If we get an Interrupt that means the block must stop processing. An
@@ -138,30 +88,34 @@ module Liquid
context.push_interrupt(node.interrupt)
break
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
end
idx += 1
raise_if_resource_limits_reached(context, output.bytesize - previous_output_size)
end
output
output.join
end
private
def render_node(context, output, node)
node.render_to_output_buffer(context, output)
def render_node_to_output(node, output, context, skip_output = false)
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
context.handle_error(e, node.line_number)
output << nil
rescue ::StandardError => e
line_number = node.is_a?(String) ? nil : node.line_number
output << context.handle_error(e, line_number)
end
def raise_if_resource_limits_reached(context, length)
context.resource_limits.render_length += length
def check_resources(context, node_output)
context.resource_limits.render_length += node_output.length
return unless context.resource_limits.reached?
raise MemoryError.new("Memory limits exceeded".freeze)
end
+1 -4
View File
@@ -44,14 +44,11 @@ module Liquid
tok[0] == type
end
SINGLE_TOKEN_EXPRESSION_TYPES = [:string, :number].freeze
private_constant :SINGLE_TOKEN_EXPRESSION_TYPES
def expression
token = @tokens[@p]
if token[0] == :id
variable_signature
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include? token[0]
elsif [:string, :number].include? token[0]
consume
elsif token.first == :open_round
consume
+8 -8
View File
@@ -1,23 +1,23 @@
module Liquid
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
render_node_without_profiling(context, output, node)
render_node_without_profiling(node, output, context, skip_output)
end
end
alias_method :render_node_without_profiling, :render_node
alias_method :render_node, :render_node_with_profiling
alias_method :render_node_without_profiling, :render_node_to_output
alias_method :render_node_to_output, :render_node_with_profiling
end
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
render_to_output_buffer_without_profiling(context, output)
render_without_profiling(context)
end
end
alias_method :render_to_output_buffer_without_profiling, :render_to_output_buffer
alias_method :render_to_output_buffer, :render_to_output_buffer_with_profiling
alias_method :render_without_profiling, :render
alias_method :render, :render_with_profiling
end
end
+3 -3
View File
@@ -39,7 +39,7 @@ module Liquid
end
def escape(input)
CGI.escapeHTML(input.to_s).untaint unless input.nil?
CGI.escapeHTML(input.to_s) unless input.nil?
end
alias_method :h, :escape
@@ -79,7 +79,7 @@ module Liquid
truncate_string_str = truncate_string.to_s
l = length - truncate_string_str.length
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
def truncatewords(input, words = 15, truncate_string = "...".freeze)
@@ -88,7 +88,7 @@ module Liquid
words = Utils.to_integer(words)
l = words - 1
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
# Split input string into an array of substrings separated by given pattern.
+2 -10
View File
@@ -5,8 +5,8 @@ module Liquid
include ParserSwitching
class << self
def parse(tag_name, markup, tokenizer, parse_context)
tag = new(tag_name, markup, parse_context)
def parse(tag_name, markup, tokenizer, options)
tag = new(tag_name, markup, options)
tag.parse(tokenizer)
tag
end
@@ -36,14 +36,6 @@ module Liquid
''.freeze
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?
false
end
+4 -8
View File
@@ -10,10 +10,6 @@ module Liquid
class Assign < Tag
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
def self.syntax_error_translation_key
"errors.syntax.assign".freeze
end
attr_reader :to, :from
def initialize(tag_name, markup, options)
@@ -22,15 +18,15 @@ module Liquid
@to = $1
@from = Variable.new($2, options)
else
raise SyntaxError.new(options[:locale].t(self.class.syntax_error_translation_key))
raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
end
end
def render_to_output_buffer(context, output)
def render(context)
val = @from.render(context)
context.scopes.last[@to] = val
context.resource_limits.assign_score += assign_score_of(val)
output
''.freeze
end
def blank?
@@ -41,7 +37,7 @@ module Liquid
def assign_score_of(val)
if val.instance_of?(String)
val.bytesize
val.length
elsif val.instance_of?(Array) || val.instance_of?(Hash)
sum = 1
# Uses #each to avoid extra allocations.
+4 -5
View File
@@ -22,12 +22,11 @@ module Liquid
end
end
def render_to_output_buffer(context, output)
previous_output_size = output.bytesize
super
def render(context)
output = super
context.scopes.last[@to] = output
context.resource_limits.assign_score += (output.bytesize - previous_output_size)
output
context.resource_limits.assign_score += output.length
''.freeze
end
def blank?
+5 -5
View File
@@ -38,21 +38,21 @@ module Liquid
end
end
def render_to_output_buffer(context, output)
def render(context)
context.stack do
execute_else_block = true
output = ''
@blocks.each do |block|
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)
execute_else_block = false
block.attachment.render_to_output_buffer(context, output)
output << block.attachment.render(context)
end
end
output
end
output
end
private
+2 -2
View File
@@ -1,7 +1,7 @@
module Liquid
class Comment < Block
def render_to_output_buffer(_context, output)
output
def render(_context)
''.freeze
end
def unknown_tag(_tag, _markup, _tokens)
+3 -14
View File
@@ -31,29 +31,18 @@ module Liquid
end
end
def render_to_output_buffer(context, output)
def render(context)
context.registers[:cycle] ||= {}
context.stack do
key = context.evaluate(@name)
iteration = context.registers[:cycle][key].to_i
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
result = context.evaluate(@variables[iteration])
iteration += 1
iteration = 0 if iteration >= @variables.size
context.registers[:cycle][key] = iteration
result
end
output
end
private
+2 -3
View File
@@ -23,12 +23,11 @@ module Liquid
@variable = markup.strip
end
def render_to_output_buffer(context, output)
def render(context)
value = context.environments.first[@variable] ||= 0
value -= 1
context.environments.first[@variable] = value
output << value.to_s
output
value.to_s
end
end
-24
View File
@@ -1,24 +0,0 @@
module Liquid
# Echo outputs an expression
#
# {% echo monkey %}
# {% echo user.name %}
#
# This is identical to variable output syntax, like {{ foo }}, but works
# inside {% liquid %} tags. The full syntax is supported, including filters:
#
# {% echo user | link %}
#
class Echo < Tag
def initialize(tag_name, markup, parse_context)
super
@variable = Variable.new(markup, parse_context)
end
def render(context)
@variable.render_to_output_buffer(context, '')
end
end
Template.register_tag('echo'.freeze, Echo)
end
+13 -26
View File
@@ -70,16 +70,14 @@ module Liquid
@else_block = BlockBody.new
end
def render_to_output_buffer(context, output)
def render(context)
segment = collection_segment(context)
if segment.empty?
render_else(context, output)
render_else(context)
else
render_segment(context, output, segment)
render_segment(context, segment)
end
output
end
protected
@@ -126,23 +124,14 @@ module Liquid
from = if @from == :continue
offsets[@name].to_i
else
from_value = context.evaluate(@from)
if from_value.nil?
0
else
Utils.to_integer(from_value)
end
context.evaluate(@from).to_i
end
collection = context.evaluate(@collection_name)
collection = collection.to_a if collection.is_a?(Range)
limit_value = context.evaluate(@limit)
to = if limit_value.nil?
nil
else
Utils.to_integer(limit_value) + from
end
limit = context.evaluate(@limit)
to = limit ? limit.to_i + from : nil
segment = Utils.slice_collection(collection, from, to)
segment.reverse! if @reversed
@@ -152,10 +141,12 @@ module Liquid
segment
end
def render_segment(context, output, segment)
def render_segment(context, segment)
for_stack = context.registers[:for_stack] ||= []
length = segment.length
result = ''
context.stack do
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
@@ -166,7 +157,7 @@ module Liquid
segment.each do |item|
context[@variable_name] = item
@for_block.render_to_output_buffer(context, output)
result << @for_block.render(context)
loop_vars.send(:increment!)
# Handle any interrupts if they exist.
@@ -181,7 +172,7 @@ module Liquid
end
end
output
result
end
def set_attribute(key, expr)
@@ -197,12 +188,8 @@ module Liquid
end
end
def render_else(context, output)
if @else_block
@else_block.render_to_output_buffer(context, output)
else
output
end
def render_else(context)
@else_block ? @else_block.render(context) : ''.freeze
end
class ParseTreeVisitor < Liquid::ParseTreeVisitor
+3 -4
View File
@@ -39,16 +39,15 @@ module Liquid
end
end
def render_to_output_buffer(context, output)
def render(context)
context.stack do
@blocks.each do |block|
if block.evaluate(context)
return block.attachment.render_to_output_buffer(context, output)
return block.attachment.render(context)
end
end
''.freeze
end
output
end
private
+7 -8
View File
@@ -1,17 +1,16 @@
module Liquid
class Ifchanged < Block
def render_to_output_buffer(context, output)
def render(context)
context.stack do
block_output = ''
super(context, block_output)
output = super
if block_output != context.registers[:ifchanged]
context.registers[:ifchanged] = block_output
output << block_output
if output != context.registers[:ifchanged]
context.registers[:ifchanged] = output
output
else
''.freeze
end
end
output
end
end
+4 -6
View File
@@ -42,7 +42,7 @@ module Liquid
def parse(_tokens)
end
def render_to_output_buffer(context, output)
def render(context)
template_name = context.evaluate(@template_name_expr)
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
@@ -66,21 +66,19 @@ module Liquid
end
if variable.is_a?(Array)
variable.each do |var|
variable.collect do |var|
context[context_variable_name] = var
partial.render_to_output_buffer(context, output)
partial.render(context)
end
else
context[context_variable_name] = variable
partial.render_to_output_buffer(context, output)
partial.render(context)
end
end
ensure
context.template_name = old_template_name
context.partial = old_partial
end
output
end
private
+2 -3
View File
@@ -20,11 +20,10 @@ module Liquid
@variable = markup.strip
end
def render_to_output_buffer(context, output)
def render(context)
value = context.environments.first[@variable] ||= 0
context.environments.first[@variable] = value + 1
output << value.to_s
output
value.to_s
end
end
+2 -3
View File
@@ -22,9 +22,8 @@ module Liquid
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
end
def render_to_output_buffer(_context, output)
output << @body
output
def render(_context)
@body
end
def nodelist
+6 -9
View File
@@ -18,7 +18,7 @@ module Liquid
end
end
def render_to_output_buffer(context, output)
def render(context)
collection = context.evaluate(@collection_name) or return ''.freeze
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
output << "<tr class=\"row1\">\n"
result = "<tr class=\"row1\">\n"
context.stack do
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
context['tablerowloop'.freeze] = tablerowloop
@@ -38,20 +38,17 @@ module Liquid
collection.each do |item|
context[@variable_name] = item
output << "<td class=\"col#{tablerowloop.col}\">"
super
output << '</td>'
result << "<td class=\"col#{tablerowloop.col}\">" << super << '</td>'
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
tablerowloop.send(:increment!)
end
end
output << "</tr>\n"
output
result << "</tr>\n"
result
end
class ParseTreeVisitor < Liquid::ParseTreeVisitor
+5 -5
View File
@@ -6,23 +6,23 @@ module Liquid
# {% unless x < 0 %} x is greater than zero {% endunless %}
#
class Unless < If
def render_to_output_buffer(context, output)
def render(context)
context.stack do
# First condition is interpreted backwards ( if not )
first_block = @blocks.first
unless first_block.evaluate(context)
return first_block.attachment.render_to_output_buffer(context, output)
return first_block.attachment.render(context)
end
# After the first condition unless works just like if
@blocks[1..-1].each do |block|
if block.evaluate(context)
return block.attachment.render_to_output_buffer(context, output)
return block.attachment.render(context)
end
end
end
output
''.freeze
end
end
end
+6 -14
View File
@@ -50,7 +50,7 @@ module Liquid
private
def lookup_class(name)
Object.const_get(name)
name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) }
end
end
@@ -63,10 +63,7 @@ module Liquid
# :strict will enforce correct syntax.
attr_writer :error_mode
# Sets how strict the taint checker should be.
# :lax is the default, and ignores the taint flag completely
# :warn adds a warning, but does not interrupt the rendering
# :error raises an error when tainted output is used
# Deprecated. No longer used. Removed in version 5
attr_writer :taint_mode
attr_accessor :default_exception_renderer
@@ -94,6 +91,7 @@ module Liquid
@error_mode ||= :lax
end
# Deprecated. Removed in version 5
def taint_mode
@taint_mode ||= :lax
end
@@ -187,12 +185,9 @@ module Liquid
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
end
output = nil
case args.last
when Hash
options = args.pop
output = options[:output] if options[:output]
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
@@ -207,9 +202,10 @@ module Liquid
begin
# render the nodelist.
# for performance reasons we get an array back here. join will make a string out of it.
with_profiling(context) do
@root.render_to_output_buffer(context, output || '')
result = with_profiling(context) do
@root.render(context)
end
result.respond_to?(:join) ? result.join : result
rescue Liquid::MemoryError => e
context.handle_error(e)
ensure
@@ -222,10 +218,6 @@ module Liquid
render(*args)
end
def render_to_output_buffer(context, output)
render(context, output: output)
end
private
def tokenize(source)
+6 -12
View File
@@ -1,31 +1,25 @@
module Liquid
class Tokenizer
attr_reader :line_number, :for_liquid_tag
attr_reader :line_number
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
def initialize(source, line_numbers = false)
@source = source
@line_number = line_number || (line_numbers ? 1 : nil)
@for_liquid_tag = for_liquid_tag
@line_number = line_numbers ? 1 : nil
@tokens = tokenize
end
def shift
token = @tokens.shift or return
if @line_number
@line_number += @for_liquid_tag ? 1 : token.count("\n")
end
token = @tokens.shift
@line_number += token.count("\n") if @line_number && token
token
end
private
def tokenize
@source = @source.source if @source.respond_to?(:source)
return [] if @source.to_s.empty?
return @source.split("\n") if @for_liquid_tag
tokens = @source.split(TemplateParser)
# removes the rogue empty element at the beginning of the array
+3 -38
View File
@@ -84,39 +84,23 @@ module Liquid
context.invoke(filter_name, output, *filter_args)
end
obj = context.apply_global_filter(obj)
taint_check(context, obj)
obj
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
context.apply_global_filter(obj)
end
private
def parse_filter_expressions(filter_name, unparsed_args)
filter_args = []
keyword_args = nil
keyword_args = {}
unparsed_args.each do |a|
if matches = a.match(JustTagAttributes)
keyword_args ||= {}
keyword_args[matches[1]] = Expression.parse(matches[2])
else
filter_args << Expression.parse(a)
end
end
result = [filter_name, filter_args]
result << keyword_args if keyword_args
result << keyword_args unless keyword_args.empty?
result
end
@@ -132,25 +116,6 @@ module Liquid
parsed_args
end
def taint_check(context, obj)
return unless obj.tainted?
return if Template.taint_mode == :lax
@markup =~ QuotedFragment
name = Regexp.last_match(0)
error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
error.line_number = line_number
error.template_name = context.template_name
case Template.taint_mode
when :warn
context.warnings << error
when :error
raise error
end
end
class ParseTreeVisitor < Liquid::ParseTreeVisitor
def children
[@node.name] + @node.filters.flatten
+1 -1
View File
@@ -1,5 +1,5 @@
# encoding: utf-8
module Liquid
VERSION = "4.0.3".freeze
VERSION = "4.0.4".freeze
end
+4 -2
View File
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
s.license = "MIT"
# s.description = "A secure, non-evaling end user template engine with aesthetic markup."
s.required_ruby_version = ">= 2.4.0"
s.required_ruby_version = ">= 2.1.0"
s.required_rubygems_version = ">= 1.3.7"
s.test_files = Dir.glob("{test}/**/*")
@@ -26,6 +26,8 @@ Gem::Specification.new do |s|
s.require_path = "lib"
s.add_development_dependency 'rake', '~> 11.3'
s.metadata["allowed_push_host"] = "https://rubygems.org"
s.add_development_dependency 'rake', '~> 13.0'
s.add_development_dependency 'minitest'
end
-63
View File
@@ -1,63 +0,0 @@
# frozen_string_literal: true
require 'benchmark/ips'
require 'memory_profiler'
require 'terminal-table'
require_relative 'theme_runner'
class Profiler
LOG_LABEL = "Profiling: ".rjust(14).freeze
REPORTS_DIR = File.expand_path('.memprof', __dir__).freeze
def self.run
puts
yield new
end
def initialize
@allocated = []
@retained = []
@headings = []
end
def profile(phase, &block)
print LOG_LABEL
print "#{phase}.. ".ljust(10)
report = MemoryProfiler.report(&block)
puts 'Done.'
@headings << phase.capitalize
@allocated << "#{report.scale_bytes(report.total_allocated_memsize)} (#{report.total_allocated} objects)"
@retained << "#{report.scale_bytes(report.total_retained_memsize)} (#{report.total_retained} objects)"
return if ENV['CI']
require 'fileutils'
report_file = File.join(REPORTS_DIR, "#{sanitize(phase)}.txt")
FileUtils.mkdir_p(REPORTS_DIR)
report.pretty_print(to_file: report_file, scale_bytes: true)
end
def tabulate
table = Terminal::Table.new(headings: @headings.unshift('Phase')) do |t|
t << @allocated.unshift('Total allocated')
t << @retained.unshift('Total retained')
end
puts
puts table
puts "\nDetailed report(s) saved to #{REPORTS_DIR}/" unless ENV['CI']
end
def sanitize(string)
string.downcase.gsub(/[\W]/, '-').squeeze('-')
end
end
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
runner = ThemeRunner.new
Profiler.run do |x|
x.profile('parse') { runner.compile }
x.profile('render') { runner.render }
x.tabulate
end
+2 -4
View File
@@ -12,7 +12,7 @@ class CommentForm < Liquid::Block
end
end
def render_to_output_buffer(context, output)
def render(context)
article = context[@variable_name]
context.stack do
@@ -23,9 +23,7 @@ class CommentForm < Liquid::Block
'email' => context['comment.email'],
'body' => context['comment.body']
}
output << wrap_in_form(article, render_all(@nodelist, context, output))
output
wrap_in_form(article, render_all(@nodelist, context))
end
end
+1 -1
View File
@@ -21,7 +21,7 @@ class Paginate < Liquid::Block
end
end
def render_to_output_buffer(context, output)
def render(context)
@context = context
context.stack do
+5 -6
View File
@@ -1,10 +1,11 @@
require 'test_helper'
class FoobarTag < Liquid::Tag
def render_to_output_buffer(context, output)
output << ' '
output
def render(*args)
" "
end
Liquid::Template.register_tag('foobar', FoobarTag)
end
class BlankTestFileSystem
@@ -30,9 +31,7 @@ class BlankTest < Minitest::Test
end
def test_new_tags_are_not_blank_by_default
with_custom_tag('foobar', FoobarTag) do
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
end
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
end
def test_loops_are_blank
+1 -27
View File
@@ -48,7 +48,7 @@ class ProductDrop < Liquid::Drop
end
def user_input
"foo".taint
"foo"
end
protected
@@ -112,32 +112,6 @@ class DropsTest < Minitest::Test
assert_equal ' ', tpl.render!('product' => ProductDrop.new)
end
def test_rendering_raises_on_tainted_attr
with_taint_mode(:error) do
tpl = Liquid::Template.parse('{{ product.user_input }}')
assert_raises TaintedError do
tpl.render!('product' => ProductDrop.new)
end
end
end
def test_rendering_warns_on_tainted_attr
with_taint_mode(:warn) do
tpl = Liquid::Template.parse('{{ product.user_input }}')
context = Context.new('product' => ProductDrop.new)
tpl.render!(context)
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
assert_equal "variable 'product.user_input' is tainted and was not escaped", context.warnings.first.to_s(false)
end
end
def test_rendering_doesnt_raise_on_escaped_tainted_attr
with_taint_mode(:error) do
tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
tpl.render!('product' => ProductDrop.new)
end
end
def test_drop_does_only_respond_to_whitelisted_methods
assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new)
assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)
+1 -1
View File
@@ -238,7 +238,7 @@ class ParseTreeVisitorTest < Minitest::Test
def traversal(template)
ParseTreeVisitor
.for(Template.parse(template).root)
.add_callback_for(VariableLookup, &:name)
.add_callback_for(VariableLookup) { |node| node.name } # rubocop:disable Style/SymbolProc
end
def visit(template)
-11
View File
@@ -1,11 +0,0 @@
require 'test_helper'
class EchoTest < Minitest::Test
include Liquid
def test_echo_outputs_its_input
assert_template_result('BAR', <<~LIQUID, { 'variable-name' => 'bar' })
{%- echo variable-name | upcase -%}
LIQUID
end
end
-28
View File
@@ -103,34 +103,6 @@ HERE
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
end
def test_limiting_with_invalid_limit
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
template = <<-MKUP
{% for i in array limit: true offset: 1 %}
{{ i }}
{% endfor %}
MKUP
exception = assert_raises(Liquid::ArgumentError) do
Template.parse(template).render!(assigns)
end
assert_equal("Liquid error: invalid integer", exception.message)
end
def test_limiting_with_invalid_offset
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
template = <<-MKUP
{% for i in array limit: 1 offset: true %}
{{ i }}
{% endfor %}
MKUP
exception = assert_raises(Liquid::ArgumentError) do
Template.parse(template).render!(assigns)
end
assert_equal("Liquid error: invalid integer", exception.message)
end
def test_dynamic_variable_limiting
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
assigns['limit'] = 2
+2 -3
View File
@@ -66,9 +66,8 @@ class CustomInclude < Liquid::Tag
def parse(tokens)
end
def render_to_output_buffer(context, output)
output << @template_name[1..-2]
output
def render(context)
@template_name[1..-2]
end
end
-104
View File
@@ -1,104 +0,0 @@
require 'test_helper'
class LiquidTagTest < Minitest::Test
include Liquid
def test_liquid_tag
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
{%- liquid
echo array | join: " "
-%}
LIQUID
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
{%- liquid
for value in array
echo value
unless forloop.last
echo " "
endunless
endfor
-%}
LIQUID
assert_template_result('4 8 12 6', <<~LIQUID, 'array' => [1, 2, 3])
{%- liquid
for value in array
assign double_value = value | times: 2
echo double_value | times: 2
unless forloop.last
echo " "
endunless
endfor
echo " "
echo double_value
-%}
LIQUID
assert_template_result('abc', <<~LIQUID)
{%- liquid echo "a" -%}
b
{%- liquid echo "c" -%}
LIQUID
end
def test_liquid_tag_errors
assert_match_syntax_error("syntax error (line 1): Unknown tag 'error'", <<~LIQUID)
{%- liquid error no such tag -%}
LIQUID
assert_match_syntax_error("syntax error (line 7): Unknown tag 'error'", <<~LIQUID)
{{ test }}
{%-
liquid
for value in array
error no such tag
endfor
-%}
LIQUID
assert_match_syntax_error("syntax error (line 2): Unknown tag '!!! the guards are vigilant'", <<~LIQUID)
{%- liquid
!!! the guards are vigilant
-%}
LIQUID
assert_match_syntax_error("syntax error (line 4): 'for' tag was never closed", <<~LIQUID)
{%- liquid
for value in array
echo 'forgot to close the for tag'
-%}
LIQUID
end
def test_line_number_is_correct_after_a_blank_token
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n\n error %}")
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n \n error %}")
end
def test_cannot_open_blocks_living_past_a_liquid_tag
assert_match_syntax_error("syntax error (line 3): 'if' tag was never closed", <<~LIQUID)
{%- liquid
if true
-%}
{%- endif -%}
LIQUID
end
def test_quirk_can_close_blocks_created_before_a_liquid_tag
assert_template_result("42", <<~LIQUID)
{%- if true -%}
42
{%- liquid endif -%}
LIQUID
end
def test_liquid_tag_in_raw
assert_template_result("{% liquid echo 'test' %}\n", <<~LIQUID)
{% raw %}{% liquid echo 'test' %}{% endraw %}
LIQUID
end
end
-18
View File
@@ -139,16 +139,6 @@ class TemplateTest < Minitest::Test
refute_nil t.resource_limits.assign_score
end
def test_resource_limits_assign_score_counts_bytes_not_characters
t = Template.parse("{% assign foo = 'すごい' %}")
t.render
assert_equal 9, t.resource_limits.assign_score
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
t.render
assert_equal 9, t.resource_limits.assign_score
end
def test_resource_limits_assign_score_nested
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
@@ -197,14 +187,6 @@ class TemplateTest < Minitest::Test
assert_equal "ababab", t.render
end
def test_render_length_uses_number_of_bytes_not_characters
t = Template.parse("{% if true %}すごい{% endif %}")
t.resource_limits.render_length_limit = 10
assert_equal "Liquid error: Memory limits exceeded", t.render
t.resource_limits.render_length_limit = 18
assert_equal "すごい", t.render
end
def test_default_resource_limits_unaffected_by_render_with_context
context = Context.new
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
+4 -19
View File
@@ -14,7 +14,7 @@ if env_mode = ENV['LIQUID_PARSER_MODE']
end
Liquid::Template.error_mode = mode
if ENV['LIQUID_C'] == '1'
if ENV['LIQUID-C'] == '1'
puts "-- LIQUID C"
require 'liquid/c'
end
@@ -37,18 +37,18 @@ module Minitest
include Liquid
def assert_template_result(expected, template, assigns = {}, message = nil)
assert_equal expected, Template.parse(template, line_numbers: true).render!(assigns), message
assert_equal expected, Template.parse(template).render!(assigns), message
end
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
assert_match expected, Template.parse(template, line_numbers: true).render!(assigns), message
assert_match expected, Template.parse(template).render!(assigns), message
end
def assert_match_syntax_error(match, template, assigns = {})
exception = assert_raises(Liquid::SyntaxError) do
Template.parse(template, line_numbers: true).render(assigns)
Template.parse(template).render(assigns)
end
assert_match match, exception.message
end
@@ -69,14 +69,6 @@ module Minitest
Liquid::Strainer.class_variable_set(:@@global_strainer, original_global_strainer)
end
def with_taint_mode(mode)
old_mode = Liquid::Template.taint_mode
Liquid::Template.taint_mode = mode
yield
ensure
Liquid::Template.taint_mode = old_mode
end
def with_error_mode(mode)
old_mode = Liquid::Template.error_mode
Liquid::Template.error_mode = mode
@@ -84,13 +76,6 @@ module Minitest
ensure
Liquid::Template.error_mode = old_mode
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
+4 -41
View File
@@ -44,47 +44,10 @@ class BlockUnitTest < Minitest::Test
end
def test_with_custom_tag
with_custom_tag('testtag', Block) do
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
end
end
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
Liquid::Template.register_tag("testtag", Block)
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
ensure
Liquid::Template.tags.delete('testtag')
end
private
+1
View File
@@ -447,6 +447,7 @@ class ContextUnitTest < Minitest::Test
end
def test_interrupt_avoids_object_allocations
@context.interrupt? # ruby 3.0.0 allocates on the first call
assert_no_object_allocations do
@context.interrupt?
end
-38
View File
@@ -18,42 +18,4 @@ class TagUnitTest < Minitest::Test
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
assert_equal 'some_tag', tag.tag_name
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