Compare commits

..

4 Commits

Author SHA1 Message Date
Mike Angell
10f7ed4b9c Tidy instance variables 2019-09-17 02:33:02 +10:00
Mike Angell
3bf8470a49 Avoid deep flatten 2019-09-17 02:30:20 +10:00
Mike Angell
740241a997 Enable Instance Assigns to survive many renders 2019-09-17 01:59:55 +10:00
Mike Angell
29dfe2aea4 Prevent needing to squash instance assigns
Instead of needing to squash instance assigns, we instead just put them at the bottom of the enviornment stack.

This simplifies the code base and gains extra performance by not needing to loop over scopes and environments.
2019-09-16 21:42:51 +10:00
131 changed files with 1321 additions and 2146 deletions

View File

@@ -1,5 +1,5 @@
inherit_from: inherit_from:
- 'https://shopify.github.io/ruby-style-guide/rubocop.yml' - https://shopify.github.io/ruby-style-guide/rubocop.yml
- .rubocop_todo.yml - .rubocop_todo.yml
require: rubocop-performance require: rubocop-performance
@@ -8,10 +8,9 @@ Performance:
Enabled: true Enabled: true
AllCops: AllCops:
TargetRubyVersion: 2.4
Exclude: Exclude:
- 'vendor/bundle/**/*' - 'vendor/bundle/**/*'
Naming/MethodName: Naming/MethodName:
Exclude: Exclude:
- 'example/server/liquid_servlet.rb' - 'example/server/liquid_servlet.rb'

View File

@@ -6,6 +6,26 @@
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again. # versions of RuboCop, may require this file to be generated again.
# Offense count: 2
Lint/AmbiguousOperator:
Exclude:
- 'test/unit/condition_unit_test.rb'
# Offense count: 21
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Exclude:
- 'lib/liquid/block_body.rb'
- 'lib/liquid/lexer.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tags/for.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/tags/raw.rb'
- 'lib/liquid/variable.rb'
- 'performance/profile.rb'
- 'test/test_helper.rb'
- 'test/unit/tokenizer_unit_test.rb'
# Offense count: 2 # Offense count: 2
# Cop supports --auto-correct. # Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle. # Configuration parameters: EnforcedStyle.
@@ -14,6 +34,17 @@ Lint/InheritException:
Exclude: Exclude:
- 'lib/liquid/interrupts.rb' - 'lib/liquid/interrupts.rb'
# Offense count: 2
Lint/UselessAssignment:
Exclude:
- 'performance/shopify/database.rb'
# Offense count: 1
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'lib/liquid/parse_context.rb'
# Offense count: 98 # Offense count: 98
# Cop supports --auto-correct. # Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
@@ -45,4 +76,26 @@ Style/ClassVars:
Exclude: Exclude:
- 'lib/liquid/condition.rb' - 'lib/liquid/condition.rb'
- 'lib/liquid/strainer.rb' - 'lib/liquid/strainer.rb'
- 'lib/liquid/template.rb' - 'lib/liquid/template.rb'
# Offense count: 1
# Configuration parameters: AllowCoercion.
Style/DateTime:
Exclude:
- 'test/unit/context_unit_test.rb'
# Offense count: 119
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, never
Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 9
# Cop supports --auto-correct.
# Configuration parameters: AllowAsExpressionSeparator.
Style/Semicolon:
Exclude:
- 'test/integration/error_handling_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/context_unit_test.rb'

View File

@@ -7,6 +7,8 @@ rvm:
- &latest_ruby 2.6 - &latest_ruby 2.6
- 2.7 - 2.7
- ruby-head - ruby-head
- jruby-head
- truffleruby
matrix: matrix:
include: include:
@@ -15,6 +17,8 @@ matrix:
name: Profiling Memory Usage name: Profiling Memory Usage
allow_failures: allow_failures:
- rvm: ruby-head - rvm: ruby-head
- rvm: jruby-head
- rvm: truffleruby
branches: branches:
only: only:

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
source 'https://rubygems.org' source 'https://rubygems.org'
git_source(:github) do |repo_name| git_source(:github) do |repo_name|
"https://github.com/#{repo_name}.git" "https://github.com/#{repo_name}.git"

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'rake' require 'rake'
require 'rake/testtask' require 'rake/testtask'
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__)) $LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module ProductsFilter module ProductsFilter
def price(integer) def price(integer)
format("$%.2d USD", integer / 100.0) format("$%.2d USD", integer / 100.0)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
def do_GET(req, res) def do_GET(req, res)
handle(:get, req, res) handle(:get, req, res)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'webrick' require 'webrick'
require 'rexml/document' require 'rexml/document'

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (c) 2005 Tobias Luetke # Copyright (c) 2005 Tobias Luetke
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
@@ -23,10 +21,10 @@
module Liquid module Liquid
FilterSeparator = /\|/ FilterSeparator = /\|/
ArgumentSeparator = ',' ArgumentSeparator = ','.freeze
FilterArgumentSeparator = ':' FilterArgumentSeparator = ':'.freeze
VariableAttributeSeparator = '.' VariableAttributeSeparator = '.'.freeze
WhitespaceControl = '-' WhitespaceControl = '-'.freeze
TagStart = /\{\%/ TagStart = /\{\%/
TagEnd = /\%\}/ TagEnd = /\%\}/
VariableSignature = /\(?[\w\-\.\[\]]\)?/ VariableSignature = /\(?[\w\-\.\[\]]\)?/
@@ -78,10 +76,7 @@ require 'liquid/tokenizer'
require 'liquid/parse_context' require 'liquid/parse_context'
require 'liquid/partial_cache' require 'liquid/partial_cache'
require 'liquid/usage' require 'liquid/usage'
require 'liquid/register'
require 'liquid/static_registers'
# Load all the tags of the standard library # Load all the tags of the standard library
# #
Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f } Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
Dir["#{__dir__}/liquid/registers/*.rb"].each { |f| require f }

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Block < Tag class Block < Tag
MAX_DEPTH = 100 MAX_DEPTH = 100
@@ -29,16 +27,16 @@ module Liquid
end end
def unknown_tag(tag, _params, _tokens) def unknown_tag(tag, _params, _tokens)
if tag == 'else' if tag == 'else'.freeze
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else", raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else".freeze,
block_name: block_name) block_name: block_name)
elsif tag.start_with?('end') elsif tag.start_with?('end'.freeze)
raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter", raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter".freeze,
tag: tag, tag: tag,
block_name: block_name, block_name: block_name,
block_delimiter: block_delimiter) block_delimiter: block_delimiter)
else else
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag) raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag)
end end
end end
@@ -54,7 +52,7 @@ module Liquid
def parse_body(body, tokens) def parse_body(body, tokens)
if parse_context.depth >= MAX_DEPTH if parse_context.depth >= MAX_DEPTH
raise StackLevelError, "Nesting too deep" raise StackLevelError, "Nesting too deep".freeze
end end
parse_context.depth += 1 parse_context.depth += 1
begin begin
@@ -63,7 +61,7 @@ module Liquid
return false if end_tag_name == block_delimiter return false if end_tag_name == block_delimiter
unless end_tag_name unless end_tag_name
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name) raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name)
end end
# this tag is not registered with the system # this tag is not registered with the system

View File

@@ -1,13 +1,11 @@
# frozen_string_literal: true
module Liquid module Liquid
class BlockBody class BlockBody
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o 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 ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
WhitespaceOrNothing = /\A\s*\z/ WhitespaceOrNothing = /\A\s*\z/
TAGSTART = "{%" TAGSTART = "{%".freeze
VARSTART = "{{" VARSTART = "{{".freeze
attr_reader :nodelist attr_reader :nodelist
@@ -27,7 +25,7 @@ module Liquid
end end
private def parse_for_liquid_tag(tokenizer, parse_context) private def parse_for_liquid_tag(tokenizer, parse_context)
while (token = tokenizer.shift) while token = tokenizer.shift
unless token.empty? || token =~ WhitespaceOrNothing unless token.empty? || token =~ WhitespaceOrNothing
unless token =~ LiquidTagToken unless token =~ LiquidTagToken
# line isn't empty but didn't match tag syntax, yield and let the # line isn't empty but didn't match tag syntax, yield and let the
@@ -36,7 +34,7 @@ module Liquid
end end
tag_name = Regexp.last_match(1) tag_name = Regexp.last_match(1)
markup = Regexp.last_match(2) markup = Regexp.last_match(2)
unless (tag = registered_tags[tag_name]) unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide # end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed # determine how to proceed
return yield tag_name, markup return yield tag_name, markup
@@ -52,7 +50,7 @@ module Liquid
end end
private def parse_for_document(tokenizer, parse_context, &block) private def parse_for_document(tokenizer, parse_context, &block)
while (token = tokenizer.shift) while token = tokenizer.shift
next if token.empty? next if token.empty?
case case
when token.start_with?(TAGSTART) when token.start_with?(TAGSTART)
@@ -66,15 +64,15 @@ module Liquid
if parse_context.line_number if parse_context.line_number
# newlines inside the tag should increase the line number, # newlines inside the tag should increase the line number,
# particularly important for multiline {% liquid %} tags # particularly important for multiline {% liquid %} tags
parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n") parse_context.line_number += Regexp.last_match(1).count("\n".freeze) + Regexp.last_match(3).count("\n".freeze)
end end
if tag_name == 'liquid' if tag_name == 'liquid'.freeze
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true) 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) next parse_for_liquid_tag(liquid_tag_tokenizer, parse_context, &block)
end end
unless (tag = registered_tags[tag_name]) unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide # end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed # determine how to proceed
return yield tag_name, markup return yield tag_name, markup
@@ -115,14 +113,14 @@ module Liquid
end end
def render(context) def render(context)
render_to_output_buffer(context, +'') render_to_output_buffer(context, '')
end end
def render_to_output_buffer(context, output) 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 previous_output_size = output.bytesize
case node case node
@@ -131,7 +129,7 @@ module Liquid
when Variable when Variable
render_node(context, output, node) render_node(context, output, node)
when Block when Block
render_node(context, node.blank? ? +'' : output, node) render_node(context, node.blank? ? '' : output, node)
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
@@ -154,13 +152,7 @@ module Liquid
private private
def render_node(context, output, node) def render_node(context, output, node)
if node.disabled?(context) node.render_to_output_buffer(context, output)
output << node.disabled_error_message
return
end
disable_tags(context, node.disabled_tags) do
node.render_to_output_buffer(context, output)
end
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
context.handle_error(e, node.line_number) context.handle_error(e, node.line_number)
rescue ::StandardError => e rescue ::StandardError => e
@@ -168,15 +160,10 @@ module Liquid
output << context.handle_error(e, line_number) output << context.handle_error(e, line_number)
end end
def disable_tags(context, tags, &block)
return yield if tags.empty?
context.registers[:disabled_tags].disable(tags, &block)
end
def raise_if_resource_limits_reached(context, length) def raise_if_resource_limits_reached(context, length)
context.resource_limits.render_length += length context.resource_limits.render_length += length
return unless context.resource_limits.reached? return unless context.resource_limits.reached?
raise MemoryError, "Memory limits exceeded" raise MemoryError, "Memory limits exceeded".freeze
end end
def create_variable(token, parse_context) def create_variable(token, parse_context)
@@ -188,11 +175,11 @@ module Liquid
end end
def raise_missing_tag_terminator(token, parse_context) def raise_missing_tag_terminator(token, parse_context)
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect) raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination".freeze, token: token, tag_end: TagEnd.inspect)
end end
def raise_missing_variable_terminator(token, parse_context) def raise_missing_variable_terminator(token, parse_context)
raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect) raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect)
end end
def registered_tags def registered_tags

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Container for liquid nodes which conveniently wraps decision making logic # Container for liquid nodes which conveniently wraps decision making logic
# #
@@ -10,14 +8,14 @@ module Liquid
# #
class Condition #:nodoc: class Condition #:nodoc:
@@operators = { @@operators = {
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) }, '=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
'!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) }, '!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
'<>' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) }, '<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
'<' => :<, '<'.freeze => :<,
'>' => :>, '>'.freeze => :>,
'>=' => :>=, '>='.freeze => :>=,
'<=' => :<=, '<='.freeze => :<=,
'contains' => lambda do |_cond, left, right| 'contains'.freeze => lambda do |_cond, left, right|
if left && right && left.respond_to?(:include?) if left && right && left.respond_to?(:include?)
right = right.to_s if left.is_a?(String) right = right.to_s if left.is_a?(String)
left.include?(right) left.include?(right)
@@ -80,7 +78,7 @@ module Liquid
end end
def inspect def inspect
"#<Condition #{[@left, @operator, @right].compact.join(' ')}>" "#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
end end
protected protected

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Context keeps the variable stack and resolves variables, as well as keywords # Context keeps the variable stack and resolves variables, as well as keywords
# #
@@ -18,32 +16,29 @@ module Liquid
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
# rubocop:disable Metrics/ParameterLists # rubocop:disable Metrics/ParameterLists
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}) def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_registers: {}, static_environments: {})
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments) new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_registers, static_environments)
end end
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {}) def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_registers = {}, static_environments = {})
@environments = [environments] @environments = environments.is_a?(Array) ? environments : [environments]
@environments.flatten!
@static_environments = [static_environments].flat_map(&:freeze).freeze @static_environments = [static_environments].flat_map(&:freeze).freeze
@scopes = [(outer_scope || {})] @scopes = [(outer_scope || {})]
@registers = registers @registers = registers
@static_registers = static_registers.freeze
@errors = [] @errors = []
@partial = false @partial = false
@strict_variables = false @strict_variables = false
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits) @resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
@base_scope_depth = 0 @base_scope_depth = 0
squash_instance_assigns_with_environments @interrupts = []
@filters = []
@global_filter = nil
self.exception_renderer = Template.default_exception_renderer self.exception_renderer = Template.default_exception_renderer
if rethrow_errors if rethrow_errors
self.exception_renderer = ->(_e) { raise } self.exception_renderer = ->(_e) { raise }
end end
@interrupts = []
@filters = []
@global_filter = nil
end end
# rubocop:enable Metrics/ParameterLists # rubocop:enable Metrics/ParameterLists
@@ -136,7 +131,7 @@ module Liquid
Context.build( Context.build(
resource_limits: resource_limits, resource_limits: resource_limits,
static_environments: static_environments, static_environments: static_environments,
registers: StaticRegisters.new(registers) static_registers: static_registers
).tap do |subcontext| ).tap do |subcontext|
subcontext.base_scope_depth = base_scope_depth + 1 subcontext.base_scope_depth = base_scope_depth + 1
subcontext.exception_renderer = exception_renderer subcontext.exception_renderer = exception_renderer
@@ -233,7 +228,7 @@ module Liquid
end end
def check_overflow def check_overflow
raise StackLevelError, "Nesting too deep" if overflow? raise StackLevelError, "Nesting too deep".freeze if overflow?
end end
def overflow? def overflow?
@@ -246,16 +241,5 @@ module Liquid
rescue Liquid::InternalError => exc rescue Liquid::InternalError => exc
exc exc
end end
def squash_instance_assigns_with_environments
@scopes.last.each_key do |k|
@environments.each do |env|
if env.key?(k)
scopes.last[k] = lookup_and_evaluate(env, k)
break
end
end
end
end # squash_instance_assigns_with_environments
end # Context end # Context
end # Liquid end # Liquid

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Document < BlockBody class Document < BlockBody
def self.parse(tokens, parse_context) def self.parse(tokens, parse_context)
@@ -19,10 +17,10 @@ module Liquid
def unknown_tag(tag, parse_context) def unknown_tag(tag, parse_context)
case tag case tag
when 'else', 'end' when 'else'.freeze, 'end'.freeze
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_outer_tag", tag: tag) raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_outer_tag".freeze, tag: tag)
else else
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag) raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag)
end end
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'set' require 'set'
module Liquid module Liquid
@@ -69,7 +67,7 @@ module Liquid
if include?(Enumerable) if include?(Enumerable)
blacklist += Enumerable.public_instance_methods blacklist += Enumerable.public_instance_methods
blacklist -= [:sort, :count, :first, :min, :max] blacklist -= [:sort, :count, :first, :min, :max, :include?]
end end
whitelist = [:to_liquid] + (public_instance_methods - blacklist) whitelist = [:to_liquid] + (public_instance_methods - blacklist)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Error < ::StandardError class Error < ::StandardError
attr_accessor :line_number attr_accessor :line_number
@@ -7,7 +5,7 @@ module Liquid
attr_accessor :markup_context attr_accessor :markup_context
def to_s(with_prefix = true) def to_s(with_prefix = true)
str = +"" str = ""
str << message_prefix if with_prefix str << message_prefix if with_prefix
str << super() str << super()
@@ -22,7 +20,7 @@ module Liquid
private private
def message_prefix def message_prefix
str = +"" str = ""
str << if is_a?(SyntaxError) str << if is_a?(SyntaxError)
"Liquid syntax error" "Liquid syntax error"
else else

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Expression class Expression
class MethodLiteral class MethodLiteral
@@ -16,11 +14,11 @@ module Liquid
end end
LITERALS = { LITERALS = {
nil => nil, 'nil' => nil, 'null' => nil, '' => nil, nil => nil, 'nil'.freeze => nil, 'null'.freeze => nil, ''.freeze => nil,
'true' => true, 'true'.freeze => true,
'false' => false, 'false'.freeze => false,
'blank' => MethodLiteral.new(:blank?, '').freeze, 'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
'empty' => MethodLiteral.new(:empty?, '').freeze 'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
}.freeze }.freeze
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m SINGLE_QUOTED_STRING = /\A'(.*)'\z/m

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'time' require 'time'
require 'date' require 'date'

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# A Liquid file system is a way to let your templates retrieve other templates for use with the include tag. # A Liquid file system is a way to let your templates retrieve other templates for use with the include tag.
# #
@@ -46,7 +44,7 @@ module Liquid
class LocalFileSystem class LocalFileSystem
attr_accessor :root attr_accessor :root
def initialize(root, pattern = "_%s.liquid") def initialize(root, pattern = "_%s.liquid".freeze)
@root = root @root = root
@pattern = pattern @pattern = pattern
end end
@@ -59,9 +57,9 @@ module Liquid
end end
def full_path(template_path) def full_path(template_path)
raise FileSystemError, "Illegal template name '#{template_path}'" unless %r{\A[^./][a-zA-Z0-9_/]+\z}.match?(template_path) raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ %r{\A[^./][a-zA-Z0-9_/]+\z}
full_path = if template_path.include?('/') full_path = if template_path.include?('/'.freeze)
File.join(root, File.dirname(template_path), @pattern % File.basename(template_path)) File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
else else
File.join(root, @pattern % template_path) File.join(root, @pattern % template_path)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class ForloopDrop < Drop class ForloopDrop < Drop
def initialize(name, length, parentloop) def initialize(name, length, parentloop)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'yaml' require 'yaml'
module Liquid module Liquid
@@ -33,7 +31,7 @@ module Liquid
end end
def deep_fetch_translation(name) def deep_fetch_translation(name)
name.split('.').reduce(locale) do |level, cur| name.split('.'.freeze).reduce(locale) do |level, cur|
level[cur] || raise(TranslationError, "Translation for #{name} does not exist in locale #{path}") level[cur] || raise(TranslationError, "Translation for #{name} does not exist in locale #{path}")
end end
end end

View File

@@ -1,12 +1,10 @@
# frozen_string_literal: true
module Liquid module Liquid
# An interrupt is any command that breaks processing of a block (ex: a for loop). # An interrupt is any command that breaks processing of a block (ex: a for loop).
class Interrupt class Interrupt
attr_reader :message attr_reader :message
def initialize(message = nil) def initialize(message = nil)
@message = message || "interrupt" @message = message || "interrupt".freeze
end end
end end

View File

@@ -1,19 +1,17 @@
# frozen_string_literal: true
require "strscan" require "strscan"
module Liquid module Liquid
class Lexer class Lexer
SPECIALS = { SPECIALS = {
'|' => :pipe, '|'.freeze => :pipe,
'.' => :dot, '.'.freeze => :dot,
':' => :colon, ':'.freeze => :colon,
',' => :comma, ','.freeze => :comma,
'[' => :open_square, '['.freeze => :open_square,
']' => :close_square, ']'.freeze => :close_square,
'(' => :open_round, '('.freeze => :open_round,
')' => :close_round, ')'.freeze => :close_round,
'?' => :question, '?'.freeze => :question,
'-' => :dash, '-'.freeze => :dash,
}.freeze }.freeze
IDENTIFIER = /[a-zA-Z_][\w-]*\??/ IDENTIFIER = /[a-zA-Z_][\w-]*\??/
SINGLE_STRING_LITERAL = /'[^\']*'/ SINGLE_STRING_LITERAL = /'[^\']*'/
@@ -33,21 +31,15 @@ module Liquid
until @ss.eos? until @ss.eos?
@ss.skip(WHITESPACE_OR_NOTHING) @ss.skip(WHITESPACE_OR_NOTHING)
break if @ss.eos? break if @ss.eos?
tok = if (t = @ss.scan(COMPARISON_OPERATOR)) tok = if t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
[:comparison, t] elsif t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]
elsif (t = @ss.scan(SINGLE_STRING_LITERAL)) elsif t = @ss.scan(DOUBLE_STRING_LITERAL) then [:string, t]
[:string, t] elsif t = @ss.scan(NUMBER_LITERAL) then [:number, t]
elsif (t = @ss.scan(DOUBLE_STRING_LITERAL)) elsif t = @ss.scan(IDENTIFIER) then [:id, t]
[:string, t] elsif t = @ss.scan(DOTDOT) then [:dotdot, t]
elsif (t = @ss.scan(NUMBER_LITERAL))
[:number, t]
elsif (t = @ss.scan(IDENTIFIER))
[:id, t]
elsif (t = @ss.scan(DOTDOT))
[:dotdot, t]
else else
c = @ss.getch c = @ss.getch
if (s = SPECIALS[c]) if s = SPECIALS[c]
[s, c] [s, c]
else else
raise SyntaxError, "Unexpected character #{c}" raise SyntaxError, "Unexpected character #{c}"

View File

@@ -25,5 +25,3 @@
render: "Syntax error in tag 'render' - Template name must be a quoted string" render: "Syntax error in tag 'render' - Template name must be a quoted string"
argument: argument:
include: "Argument error in tag 'include' - Illegal template name" include: "Argument error in tag 'include' - Illegal template name"
disabled:
tag: "usage is not allowed in this context"

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class ParseContext class ParseContext
attr_accessor :locale, :line_number, :trim_whitespace, :depth attr_accessor :locale, :line_number, :trim_whitespace, :depth
@@ -21,6 +19,7 @@ module Liquid
@partial = value @partial = value
@options = value ? partial_options : @template_options @options = value ? partial_options : @template_options
@error_mode = @options[:error_mode] || Template.error_mode @error_mode = @options[:error_mode] || Template.error_mode
value
end end
def partial_options def partial_options

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Parser class Parser
def initialize(input) def initialize(input)
@@ -68,10 +66,10 @@ module Liquid
end end
def argument def argument
str = +"" str = ""
# might be a keyword argument (identifier: expression) # might be a keyword argument (identifier: expression)
if look(:id) && look(:colon, 1) if look(:id) && look(:colon, 1)
str << consume << consume << ' ' str << consume << consume << ' '.freeze
end end
str << expression str << expression

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
module ParserSwitching module ParserSwitching
def parse_with_selected_parser(markup) def parse_with_selected_parser(markup)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class PartialCache class PartialCache
def self.load(template_name, context:, parse_context:) def self.load(template_name, context:, parse_context:)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'liquid/profiler/hooks' require 'liquid/profiler/hooks'
module Liquid module Liquid
@@ -46,7 +44,7 @@ module Liquid
include Enumerable include Enumerable
class Timing class Timing
attr_reader :code, :partial, :line_number, :children, :total_time, :self_time attr_reader :code, :partial, :line_number, :children
def initialize(node, partial) def initialize(node, partial)
@code = node.respond_to?(:raw) ? node.raw : node @code = node.respond_to?(:raw) ? node.raw : node
@@ -65,17 +63,6 @@ module Liquid
def finish def finish
@end_time = Time.now @end_time = Time.now
@total_time = @end_time - @start_time
if @children.empty?
@self_time = @total_time
else
total_children_time = 0
@children.each do |child|
total_children_time += child.total_time
end
@self_time = @total_time - total_children_time
end
end end
def render_time def render_time
@@ -109,8 +96,8 @@ module Liquid
Thread.current[:liquid_profiler] Thread.current[:liquid_profiler]
end end
def initialize(partial_name = "<root>") def initialize
@partial_stack = [partial_name] @partial_stack = ["<root>"]
@root_timing = Timing.new("", current_partial) @root_timing = Timing.new("", current_partial)
@timing_stack = [@root_timing] @timing_stack = [@root_timing]

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class BlockBody class BlockBody
def render_node_with_profiling(context, output, node) def render_node_with_profiling(context, output, node)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class RangeLookup class RangeLookup
def self.parse(start_markup, end_markup) def self.parse(start_markup, end_markup)

View File

@@ -1,6 +0,0 @@
# frozen_string_literal: true
module Liquid
class Register
end
end

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
module Liquid
class DisabledTags < Register
def initialize
@disabled_tags = {}
end
def disabled?(tag)
@disabled_tags.key?(tag) && @disabled_tags[tag] > 0
end
def disable(tags)
tags.each(&method(:increment))
yield
ensure
tags.each(&method(:decrement))
end
private
def increment(tag)
@disabled_tags[tag] ||= 0
@disabled_tags[tag] += 1
end
def decrement(tag)
@disabled_tags[tag] -= 1
end
end
Template.add_register(:disabled_tags, DisabledTags.new)
end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class ResourceLimits class ResourceLimits
attr_accessor :render_length, :render_score, :assign_score, attr_accessor :render_length, :render_score, :assign_score,

View File

@@ -1,16 +1,14 @@
# frozen_string_literal: true
require 'cgi' require 'cgi'
require 'bigdecimal' require 'bigdecimal'
module Liquid module Liquid
module StandardFilters module StandardFilters
HTML_ESCAPE = { HTML_ESCAPE = {
'&' => '&amp;', '&'.freeze => '&amp;'.freeze,
'>' => '&gt;', '>'.freeze => '&gt;'.freeze,
'<' => '&lt;', '<'.freeze => '&lt;'.freeze,
'"' => '&quot;', '"'.freeze => '&quot;'.freeze,
"'" => '&#39;', "'".freeze => '&#39;'.freeze,
}.freeze }.freeze
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
STRIP_HTML_BLOCKS = Regexp.union( STRIP_HTML_BLOCKS = Regexp.union(
@@ -74,7 +72,7 @@ module Liquid
end end
# Truncate a string down to x characters # Truncate a string down to x characters
def truncate(input, length = 50, truncate_string = "...") def truncate(input, length = 50, truncate_string = "...".freeze)
return if input.nil? return if input.nil?
input_str = input.to_s input_str = input.to_s
length = Utils.to_integer(length) length = Utils.to_integer(length)
@@ -84,13 +82,13 @@ module Liquid
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
end end
def truncatewords(input, words = 15, truncate_string = "...") def truncatewords(input, words = 15, truncate_string = "...".freeze)
return if input.nil? return if input.nil?
wordlist = input.to_s.split wordlist = input.to_s.split
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(" ").concat(truncate_string.to_s) : input wordlist.length > l ? wordlist[0..l].join(" ".freeze).concat(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.
@@ -115,7 +113,7 @@ module Liquid
end end
def strip_html(input) def strip_html(input)
empty = '' empty = ''.freeze
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty) result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
result.gsub!(STRIP_HTML_TAGS, empty) result.gsub!(STRIP_HTML_TAGS, empty)
result result
@@ -123,18 +121,18 @@ module Liquid
# Remove all newlines from the string # Remove all newlines from the string
def strip_newlines(input) def strip_newlines(input)
input.to_s.gsub(/\r?\n/, '') input.to_s.gsub(/\r?\n/, ''.freeze)
end end
# Join elements of the array with certain character between them # Join elements of the array with certain character between them
def join(input, glue = ' ') def join(input, glue = ' '.freeze)
InputIterator.new(input, context).join(glue) InputIterator.new(input).join(glue)
end end
# Sort elements of the array # Sort elements of the array
# provide optional property with which to sort an array of hashes or drops # provide optional property with which to sort an array of hashes or drops
def sort(input, property = nil) def sort(input, property = nil)
ary = InputIterator.new(input, context) ary = InputIterator.new(input)
return [] if ary.empty? return [] if ary.empty?
@@ -154,7 +152,7 @@ module Liquid
# Sort elements of an array ignoring case if strings # Sort elements of an array ignoring case if strings
# provide optional property with which to sort an array of hashes or drops # provide optional property with which to sort an array of hashes or drops
def sort_natural(input, property = nil) def sort_natural(input, property = nil)
ary = InputIterator.new(input, context) ary = InputIterator.new(input)
return [] if ary.empty? return [] if ary.empty?
@@ -174,7 +172,7 @@ module Liquid
# Filter the elements of an array to those with a certain property value. # Filter the elements of an array to those with a certain property value.
# By default the target is any truthy value. # By default the target is any truthy value.
def where(input, property, target_value = nil) def where(input, property, target_value = nil)
ary = InputIterator.new(input, context) ary = InputIterator.new(input)
if ary.empty? if ary.empty?
[] []
@@ -196,7 +194,7 @@ module Liquid
# Remove duplicate elements from an array # Remove duplicate elements from an array
# provide optional property with which to determine uniqueness # provide optional property with which to determine uniqueness
def uniq(input, property = nil) def uniq(input, property = nil)
ary = InputIterator.new(input, context) ary = InputIterator.new(input)
if property.nil? if property.nil?
ary.uniq ary.uniq
@@ -213,16 +211,16 @@ module Liquid
# Reverse the elements of an array # Reverse the elements of an array
def reverse(input) def reverse(input)
ary = InputIterator.new(input, context) ary = InputIterator.new(input)
ary.reverse ary.reverse
end end
# map/collect on a given property # map/collect on a given property
def map(input, property) def map(input, property)
InputIterator.new(input, context).map do |e| InputIterator.new(input).map do |e|
e = e.call if e.is_a?(Proc) e = e.call if e.is_a?(Proc)
if property == "to_liquid" if property == "to_liquid".freeze
e e
elsif e.respond_to?(:[]) elsif e.respond_to?(:[])
r = e[property] r = e[property]
@@ -236,7 +234,7 @@ module Liquid
# Remove nils within an array # Remove nils within an array
# provide optional property with which to check for nil # provide optional property with which to check for nil
def compact(input, property = nil) def compact(input, property = nil)
ary = InputIterator.new(input, context) ary = InputIterator.new(input)
if property.nil? if property.nil?
ary.compact ary.compact
@@ -252,23 +250,23 @@ module Liquid
end end
# Replace occurrences of a string with another # Replace occurrences of a string with another
def replace(input, string, replacement = '') def replace(input, string, replacement = ''.freeze)
input.to_s.gsub(string.to_s, replacement.to_s) input.to_s.gsub(string.to_s, replacement.to_s)
end end
# Replace the first occurrences of a string with another # Replace the first occurrences of a string with another
def replace_first(input, string, replacement = '') def replace_first(input, string, replacement = ''.freeze)
input.to_s.sub(string.to_s, replacement.to_s) input.to_s.sub(string.to_s, replacement.to_s)
end end
# remove a substring # remove a substring
def remove(input, string) def remove(input, string)
input.to_s.gsub(string.to_s, '') input.to_s.gsub(string.to_s, ''.freeze)
end end
# remove the first occurrences of a substring # remove the first occurrences of a substring
def remove_first(input, string) def remove_first(input, string)
input.to_s.sub(string.to_s, '') input.to_s.sub(string.to_s, ''.freeze)
end end
# add one string to another # add one string to another
@@ -280,7 +278,7 @@ module Liquid
unless array.respond_to?(:to_ary) unless array.respond_to?(:to_ary)
raise ArgumentError, "concat filter requires an array argument" raise ArgumentError, "concat filter requires an array argument"
end end
InputIterator.new(input, context).concat(array) InputIterator.new(input).concat(array)
end end
# prepend a string to another # prepend a string to another
@@ -290,7 +288,7 @@ module Liquid
# Add <br /> tags in front of all newlines in input string # Add <br /> tags in front of all newlines in input string
def newline_to_br(input) def newline_to_br(input)
input.to_s.gsub(/\n/, "<br />\n") input.to_s.gsub(/\n/, "<br />\n".freeze)
end end
# Reformat a date using Ruby's core Time#strftime( string ) -> string # Reformat a date using Ruby's core Time#strftime( string ) -> string
@@ -327,7 +325,7 @@ module Liquid
def date(input, format) def date(input, format)
return input if format.to_s.empty? return input if format.to_s.empty?
return input unless (date = Utils.to_date(input)) return input unless date = Utils.to_date(input)
date.strftime(format.to_s) date.strftime(format.to_s)
end end
@@ -421,26 +419,17 @@ module Liquid
result.is_a?(BigDecimal) ? result.to_f : result result.is_a?(BigDecimal) ? result.to_f : result
end end
# Set a default value when the input is nil, false or empty def default(input, default_value = ''.freeze)
# if !input || input.respond_to?(:empty?) && input.empty?
# Example: Usage.increment("default_filter_received_false_value") if input == false # See https://github.com/Shopify/liquid/issues/1127
# {{ product.title | default: "No Title" }} default_value
# else
# Use `allow_false` when an input should only be tested against nil or empty and not false. input
# end
# Example:
# {{ product.title | default: "No Title", allow_false: true }}
#
def default(input, default_value = '', options = {})
options = {} unless options.is_a?(Hash)
false_check = options['allow_false'] ? input.nil? : !input
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
end end
private private
attr_reader :context
def raise_property_error(property) def raise_property_error(property)
raise Liquid::ArgumentError, "cannot select the property '#{property}'" raise Liquid::ArgumentError, "cannot select the property '#{property}'"
end end
@@ -469,8 +458,7 @@ module Liquid
class InputIterator class InputIterator
include Enumerable include Enumerable
def initialize(input, context) def initialize(input)
@context = context
@input = if input.is_a?(Array) @input = if input.is_a?(Array)
input.flatten input.flatten
elsif input.is_a?(Hash) elsif input.is_a?(Hash)
@@ -509,7 +497,6 @@ module Liquid
def each def each
@input.each do |e| @input.each do |e|
e.context = @context if e.respond_to?(:context=)
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e) yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
end end
end end

View File

@@ -1,36 +0,0 @@
# frozen_string_literal: true
module Liquid
class StaticRegisters
attr_reader :static, :registers
def initialize(registers = {})
@static = registers.is_a?(StaticRegisters) ? registers.static : registers
@registers = {}
end
def []=(key, value)
@registers[key] = value
end
def [](key)
if @registers.key?(key)
@registers[key]
else
@static[key]
end
end
def delete(key)
@registers.delete(key)
end
def fetch(key, default = nil)
key?(key) ? self[key] : default
end
def key?(key)
@registers.key?(key) || @static.key?(key)
end
end
end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'set' require 'set'
module Liquid module Liquid

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class TablerowloopDrop < Drop class TablerowloopDrop < Drop
def initialize(length, cols) def initialize(length, cols)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Tag class Tag
attr_reader :nodelist, :tag_name, :line_number, :parse_context attr_reader :nodelist, :tag_name, :line_number, :parse_context
@@ -13,15 +11,7 @@ module Liquid
tag tag
end end
def disable_tags(*tags)
disabled_tags.push(*tags)
end
private :new private :new
def disabled_tags
@disabled_tags ||= []
end
end end
def initialize(tag_name, markup, parse_context) def initialize(tag_name, markup, parse_context)
@@ -43,15 +33,7 @@ module Liquid
end end
def render(_context) def render(_context)
'' ''.freeze
end
def disabled?(context)
context.registers[:disabled_tags].disabled?(tag_name)
end
def disabled_error_message
"#{tag_name} #{options[:locale].t('errors.disabled.tag')}"
end end
# For backwards compatibility with custom tags. In a future release, the semantics # For backwards compatibility with custom tags. In a future release, the semantics
@@ -65,9 +47,5 @@ module Liquid
def blank? def blank?
false false
end end
def disabled_tags
self.class.disabled_tags
end
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Assign sets a variable in your template. # Assign sets a variable in your template.
# #
@@ -13,7 +11,7 @@ module Liquid
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
def self.syntax_error_translation_key def self.syntax_error_translation_key
"errors.syntax.assign" "errors.syntax.assign".freeze
end end
attr_reader :to, :from attr_reader :to, :from
@@ -61,5 +59,5 @@ module Liquid
end end
end end
Template.register_tag('assign', Assign) Template.register_tag('assign'.freeze, Assign)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Break tag to be used to break out of a for loop. # Break tag to be used to break out of a for loop.
# #
@@ -16,5 +14,5 @@ module Liquid
end end
end end
Template.register_tag('break', Break) Template.register_tag('break'.freeze, Break)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Capture stores the result of a block into a variable without rendering it inplace. # Capture stores the result of a block into a variable without rendering it inplace.
# #
@@ -37,5 +35,5 @@ module Liquid
end end
end end
Template.register_tag('capture', Capture) Template.register_tag('capture'.freeze, Capture)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Case < Block class Case < Block
Syntax = /(#{QuotedFragment})/o Syntax = /(#{QuotedFragment})/o
@@ -14,7 +12,7 @@ module Liquid
if markup =~ Syntax if markup =~ Syntax
@left = Expression.parse(Regexp.last_match(1)) @left = Expression.parse(Regexp.last_match(1))
else else
raise SyntaxError, options[:locale].t("errors.syntax.case") raise SyntaxError, options[:locale].t("errors.syntax.case".freeze)
end end
end end
@@ -29,9 +27,9 @@ module Liquid
def unknown_tag(tag, markup, tokens) def unknown_tag(tag, markup, tokens)
case tag case tag
when 'when' when 'when'.freeze
record_when_condition(markup) record_when_condition(markup)
when 'else' when 'else'.freeze
record_else_condition(markup) record_else_condition(markup)
else else
super super
@@ -60,12 +58,12 @@ module Liquid
while markup while markup
unless markup =~ WhenSyntax unless markup =~ WhenSyntax
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_when") raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_when".freeze)
end end
markup = Regexp.last_match(2) markup = Regexp.last_match(2)
block = Condition.new(@left, '==', Expression.parse(Regexp.last_match(1))) block = Condition.new(@left, '=='.freeze, Expression.parse(Regexp.last_match(1)))
block.attach(body) block.attach(body)
@blocks << block @blocks << block
end end
@@ -73,7 +71,7 @@ module Liquid
def record_else_condition(markup) def record_else_condition(markup)
unless markup.strip.empty? unless markup.strip.empty?
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_else") raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_else".freeze)
end end
block = ElseCondition.new block = ElseCondition.new
@@ -88,5 +86,5 @@ module Liquid
end end
end end
Template.register_tag('case', Case) Template.register_tag('case'.freeze, Case)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Comment < Block class Comment < Block
def render_to_output_buffer(_context, output) def render_to_output_buffer(_context, output)
@@ -14,5 +12,5 @@ module Liquid
end end
end end
Template.register_tag('comment', Comment) Template.register_tag('comment'.freeze, Comment)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Continue tag to be used to break out of a for loop. # Continue tag to be used to break out of a for loop.
# #
@@ -16,5 +14,5 @@ module Liquid
end end
end end
Template.register_tag('continue', Continue) Template.register_tag('continue'.freeze, Continue)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Cycle is usually used within a loop to alternate between values, like colors or DOM classes. # Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
# #
@@ -29,7 +27,7 @@ module Liquid
@variables = variables_from_string(markup) @variables = variables_from_string(markup)
@name = @variables.to_s @name = @variables.to_s
else else
raise SyntaxError, options[:locale].t("errors.syntax.cycle") raise SyntaxError, options[:locale].t("errors.syntax.cycle".freeze)
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# decrement is used in a place where one needs to insert a counter # decrement is used in a place where one needs to insert a counter
# into a template, and needs the counter to survive across # into a template, and needs the counter to survive across
@@ -34,5 +32,5 @@ module Liquid
end end
end end
Template.register_tag('decrement', Decrement) Template.register_tag('decrement'.freeze, Decrement)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Echo outputs an expression # Echo outputs an expression
# #
@@ -18,9 +16,9 @@ module Liquid
end end
def render(context) def render(context)
@variable.render_to_output_buffer(context, +'') @variable.render_to_output_buffer(context, '')
end end
end end
Template.register_tag('echo', Echo) Template.register_tag('echo'.freeze, Echo)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# "For" iterates over an array or collection. # "For" iterates over an array or collection.
# Several useful variables are available to you within the loop. # Several useful variables are available to you within the loop.
@@ -68,7 +66,7 @@ module Liquid
end end
def unknown_tag(tag, markup, tokens) def unknown_tag(tag, markup, tokens)
return super unless tag == 'else' return super unless tag == 'else'.freeze
@else_block = BlockBody.new @else_block = BlockBody.new
end end
@@ -97,22 +95,22 @@ module Liquid
set_attribute(key, value) set_attribute(key, value)
end end
else else
raise SyntaxError, options[:locale].t("errors.syntax.for") raise SyntaxError, options[:locale].t("errors.syntax.for".freeze)
end end
end end
def strict_parse(markup) def strict_parse(markup)
p = Parser.new(markup) p = Parser.new(markup)
@variable_name = p.consume(:id) @variable_name = p.consume(:id)
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in') raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in".freeze) unless p.id?('in'.freeze)
collection_name = p.expression collection_name = p.expression
@name = "#{@variable_name}-#{collection_name}" @name = "#{@variable_name}-#{collection_name}"
@collection_name = Expression.parse(collection_name) @collection_name = Expression.parse(collection_name)
@reversed = p.id?('reversed') @reversed = p.id?('reversed'.freeze)
while p.look(:id) && p.look(:colon, 1) while p.look(:id) && p.look(:colon, 1)
unless (attribute = p.id?('limit') || p.id?('offset')) unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute") raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute".freeze)
end end
p.consume p.consume
set_attribute(attribute, p.expression) set_attribute(attribute, p.expression)
@@ -164,7 +162,7 @@ module Liquid
for_stack.push(loop_vars) for_stack.push(loop_vars)
begin begin
context['forloop'] = loop_vars context['forloop'.freeze] = loop_vars
segment.each do |item| segment.each do |item|
context[@variable_name] = item context[@variable_name] = item
@@ -187,13 +185,13 @@ module Liquid
def set_attribute(key, expr) def set_attribute(key, expr)
case key case key
when 'offset' when 'offset'.freeze
@from = if expr == 'continue' @from = if expr == 'continue'.freeze
:continue :continue
else else
Expression.parse(expr) Expression.parse(expr)
end end
when 'limit' when 'limit'.freeze
@limit = Expression.parse(expr) @limit = Expression.parse(expr)
end end
end end
@@ -213,5 +211,5 @@ module Liquid
end end
end end
Template.register_tag('for', For) Template.register_tag('for'.freeze, For)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# If is the conditional block # If is the conditional block
# #
@@ -21,7 +19,7 @@ module Liquid
def initialize(tag_name, markup, options) def initialize(tag_name, markup, options)
super super
@blocks = [] @blocks = []
push_block('if', markup) push_block('if'.freeze, markup)
end end
def nodelist def nodelist
@@ -34,7 +32,7 @@ module Liquid
end end
def unknown_tag(tag, markup, tokens) def unknown_tag(tag, markup, tokens)
if ['elsif', 'else'].include?(tag) if ['elsif'.freeze, 'else'.freeze].include?(tag)
push_block(tag, markup) push_block(tag, markup)
else else
super super
@@ -54,7 +52,7 @@ module Liquid
private private
def push_block(tag, markup) def push_block(tag, markup)
block = if tag == 'else' block = if tag == 'else'.freeze
ElseCondition.new ElseCondition.new
else else
parse_with_selected_parser(markup) parse_with_selected_parser(markup)
@@ -66,17 +64,17 @@ module Liquid
def lax_parse(markup) def lax_parse(markup)
expressions = markup.scan(ExpressionsAndOperators) expressions = markup.scan(ExpressionsAndOperators)
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax raise SyntaxError, options[:locale].t("errors.syntax.if".freeze) unless expressions.pop =~ Syntax
condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3))) condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3)))
until expressions.empty? until expressions.empty?
operator = expressions.pop.to_s.strip operator = expressions.pop.to_s.strip
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop.to_s =~ Syntax raise SyntaxError, options[:locale].t("errors.syntax.if".freeze) unless expressions.pop.to_s =~ Syntax
new_condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3))) new_condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3)))
raise SyntaxError, options[:locale].t("errors.syntax.if") unless BOOLEAN_OPERATORS.include?(operator) raise SyntaxError, options[:locale].t("errors.syntax.if".freeze) unless BOOLEAN_OPERATORS.include?(operator)
new_condition.send(operator, condition) new_condition.send(operator, condition)
condition = new_condition condition = new_condition
end end
@@ -94,7 +92,7 @@ module Liquid
def parse_binary_comparisons(p) def parse_binary_comparisons(p)
condition = parse_comparison(p) condition = parse_comparison(p)
first_condition = condition first_condition = condition
while (op = (p.id?('and') || p.id?('or'))) while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
child_condition = parse_comparison(p) child_condition = parse_comparison(p)
condition.send(op, child_condition) condition.send(op, child_condition)
condition = child_condition condition = child_condition
@@ -104,7 +102,7 @@ module Liquid
def parse_comparison(p) def parse_comparison(p)
a = Expression.parse(p.expression) a = Expression.parse(p.expression)
if (op = p.consume?(:comparison)) if op = p.consume?(:comparison)
b = Expression.parse(p.expression) b = Expression.parse(p.expression)
Condition.new(a, op, b) Condition.new(a, op, b)
else else
@@ -119,5 +117,5 @@ module Liquid
end end
end end
Template.register_tag('if', If) Template.register_tag('if'.freeze, If)
end end

View File

@@ -1,9 +1,7 @@
# frozen_string_literal: true
module Liquid module Liquid
class Ifchanged < Block class Ifchanged < Block
def render_to_output_buffer(context, output) def render_to_output_buffer(context, output)
block_output = +'' block_output = ''
super(context, block_output) super(context, block_output)
if block_output != context.registers[:ifchanged] if block_output != context.registers[:ifchanged]
@@ -15,5 +13,5 @@ module Liquid
end end
end end
Template.register_tag('ifchanged', Ifchanged) Template.register_tag('ifchanged'.freeze, Ifchanged)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Include allows templates to relate with other templates # Include allows templates to relate with other templates
# #
@@ -16,20 +14,18 @@ module Liquid
# {% include 'product' for products %} # {% include 'product' for products %}
# #
class Include < Tag class Include < Tag
SYNTAX = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
Syntax = SYNTAX
attr_reader :template_name_expr, :variable_name_expr, :attributes attr_reader :template_name_expr, :variable_name_expr, :attributes
def initialize(tag_name, markup, options) def initialize(tag_name, markup, options)
super super
if markup =~ SYNTAX if markup =~ Syntax
template_name = Regexp.last_match(1) template_name = Regexp.last_match(1)
variable_name = Regexp.last_match(3) variable_name = Regexp.last_match(3)
@alias_name = Regexp.last_match(5)
@variable_name_expr = variable_name ? Expression.parse(variable_name) : nil @variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
@template_name_expr = Expression.parse(template_name) @template_name_expr = Expression.parse(template_name)
@attributes = {} @attributes = {}
@@ -39,7 +35,7 @@ module Liquid
end end
else else
raise SyntaxError, options[:locale].t("errors.syntax.include") raise SyntaxError, options[:locale].t("errors.syntax.include".freeze)
end end
end end
@@ -56,7 +52,7 @@ module Liquid
parse_context: parse_context parse_context: parse_context
) )
context_variable_name = @alias_name || template_name.split('/').last context_variable_name = template_name.split('/'.freeze).last
variable = if @variable_name_expr variable = if @variable_name_expr
context.evaluate(@variable_name_expr) context.evaluate(@variable_name_expr)
@@ -105,5 +101,5 @@ module Liquid
end end
end end
Template.register_tag('include', Include) Template.register_tag('include'.freeze, Include)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# increment is used in a place where one needs to insert a counter # increment is used in a place where one needs to insert a counter
# into a template, and needs the counter to survive across # into a template, and needs the counter to survive across
@@ -31,5 +29,5 @@ module Liquid
end end
end end
Template.register_tag('increment', Increment) Template.register_tag('increment'.freeze, Increment)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Raw < Block class Raw < Block
Syntax = /\A\s*\z/ Syntax = /\A\s*\z/
@@ -12,16 +10,16 @@ module Liquid
end end
def parse(tokens) def parse(tokens)
@body = +'' @body = ''
while (token = tokens.shift) while token = tokens.shift
if token =~ FullTokenPossiblyInvalid if token =~ FullTokenPossiblyInvalid
@body << Regexp.last_match(1) if Regexp.last_match(1) != "" @body << Regexp.last_match(1) if Regexp.last_match(1) != "".freeze
return if block_delimiter == Regexp.last_match(2) return if block_delimiter == Regexp.last_match(2)
end end
@body << token unless token.empty? @body << token unless token.empty?
end end
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name) raise SyntaxError, 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_to_output_buffer(_context, output)
@@ -40,11 +38,11 @@ module Liquid
protected protected
def ensure_valid_markup(tag_name, markup, parse_context) def ensure_valid_markup(tag_name, markup, parse_context)
unless Syntax.match?(markup) unless markup =~ Syntax
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_unexpected_args", tag: tag_name) raise SyntaxError, parse_context.locale.t("errors.syntax.tag_unexpected_args".freeze, tag: tag_name)
end end
end end
end end
Template.register_tag('raw', Raw) Template.register_tag('raw'.freeze, Raw)
end end

View File

@@ -1,23 +1,16 @@
# frozen_string_literal: true
module Liquid module Liquid
class Render < Tag class Render < Tag
SYNTAX = /(#{QuotedString}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o SYNTAX = /(#{QuotedString})#{QuotedFragment}*/o
disable_tags "include"
attr_reader :template_name_expr, :attributes attr_reader :template_name_expr, :attributes
def initialize(tag_name, markup, options) def initialize(tag_name, markup, options)
super super
raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX raise SyntaxError, options[:locale].t("errors.syntax.render".freeze) unless markup =~ SYNTAX
template_name = Regexp.last_match(1) template_name = Regexp.last_match(1)
variable_name = Regexp.last_match(3)
@alias_name = Regexp.last_match(5)
@variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
@template_name_expr = Expression.parse(template_name) @template_name_expr = Expression.parse(template_name)
@attributes = {} @attributes = {}
@@ -27,10 +20,6 @@ module Liquid
end end
def render_to_output_buffer(context, output) def render_to_output_buffer(context, output)
render_tag(context, output)
end
def render_tag(context, output)
# Though we evaluate this here we will only ever parse it as a string literal. # Though we evaluate this here we will only ever parse it as a string literal.
template_name = context.evaluate(@template_name_expr) template_name = context.evaluate(@template_name_expr)
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
@@ -41,28 +30,13 @@ module Liquid
parse_context: parse_context parse_context: parse_context
) )
context_variable_name = @alias_name || template_name.split('/').last inner_context = context.new_isolated_subcontext
inner_context.template_name = template_name
render_partial_func = ->(var, forloop) { inner_context.partial = true
inner_context = context.new_isolated_subcontext @attributes.each do |key, value|
inner_context.template_name = template_name inner_context[key] = context.evaluate(value)
inner_context.partial = true
inner_context['forloop'] = forloop if forloop
@attributes.each do |key, value|
inner_context[key] = context.evaluate(value)
end
inner_context[context_variable_name] = var unless var.nil?
partial.render_to_output_buffer(inner_context, output)
forloop&.send(:increment!)
}
variable = @variable_name_expr ? context.evaluate(@variable_name_expr) : nil
if variable.is_a?(Array)
forloop = Liquid::ForloopDrop.new(template_name, variable.count, nil)
variable.each { |var| render_partial_func.call(var, forloop) }
else
render_partial_func.call(variable, nil)
end end
partial.render_to_output_buffer(inner_context, output)
output output
end end
@@ -76,5 +50,5 @@ module Liquid
end end
end end
Template.register_tag('render', Render) Template.register_tag('render'.freeze, Render)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class TableRow < Block class TableRow < Block
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
@@ -16,26 +14,26 @@ module Liquid
@attributes[key] = Expression.parse(value) @attributes[key] = Expression.parse(value)
end end
else else
raise SyntaxError, options[:locale].t("errors.syntax.table_row") raise SyntaxError, options[:locale].t("errors.syntax.table_row".freeze)
end end
end end
def render_to_output_buffer(context, output) def render_to_output_buffer(context, output)
(collection = context.evaluate(@collection_name)) || (return '') (collection = context.evaluate(@collection_name)) || (return ''.freeze)
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset']).to_i : 0 from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
to = @attributes.key?('limit') ? from + context.evaluate(@attributes['limit']).to_i : nil to = @attributes.key?('limit'.freeze) ? from + context.evaluate(@attributes['limit'.freeze]).to_i : nil
collection = Utils.slice_collection(collection, from, to) collection = Utils.slice_collection(collection, from, to)
length = collection.length length = collection.length
cols = context.evaluate(@attributes['cols']).to_i cols = context.evaluate(@attributes['cols'.freeze]).to_i
output << "<tr class=\"row1\">\n" output << "<tr class=\"row1\">\n"
context.stack do context.stack do
tablerowloop = Liquid::TablerowloopDrop.new(length, cols) tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
context['tablerowloop'] = tablerowloop context['tablerowloop'.freeze] = tablerowloop
collection.each do |item| collection.each do |item|
context[@variable_name] = item context[@variable_name] = item
@@ -63,5 +61,5 @@ module Liquid
end end
end end
Template.register_tag('tablerow', TableRow) Template.register_tag('tablerow'.freeze, TableRow)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require_relative 'if' require_relative 'if'
module Liquid module Liquid
@@ -26,5 +24,5 @@ module Liquid
end end
end end
Template.register_tag('unless', Unless) Template.register_tag('unless'.freeze, Unless)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
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
@@ -92,14 +90,6 @@ module Liquid
@tags ||= TagRegistry.new @tags ||= TagRegistry.new
end end
def add_register(name, klass)
registers[name.to_sym] = klass
end
def registers
@registers ||= {}
end
def error_mode def error_mode
@error_mode ||= :lax @error_mode ||= :lax
end end
@@ -153,7 +143,12 @@ module Liquid
end end
def instance_assigns def instance_assigns
@instance_assigns ||= {} @instance_assigns ||= []
end
def new_outer_scope
@instance_assigns.unshift(last = {})
last
end end
def errors def errors
@@ -175,7 +170,7 @@ module Liquid
# filters and tags and might be useful to integrate liquid more with its host application # filters and tags and might be useful to integrate liquid more with its host application
# #
def render(*args) def render(*args)
return '' if @root.nil? return ''.freeze if @root.nil?
context = case args.first context = case args.first
when Liquid::Context when Liquid::Context
@@ -188,37 +183,29 @@ module Liquid
c c
when Liquid::Drop when Liquid::Drop
drop = args.shift drop = args.shift
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits) drop.context = Context.new([drop, assigns].concat(instance_assigns), new_outer_scope, registers, @rethrow_errors, @resource_limits)
when Hash when Hash
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits) Context.new([args.shift, assigns].concat(instance_assigns), new_outer_scope, registers, @rethrow_errors, @resource_limits)
when nil when nil
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits) Context.new([assigns].concat(instance_assigns), new_outer_scope, registers, @rethrow_errors, @resource_limits)
else else
raise ArgumentError, "Expected Hash or Liquid::Context as parameter" raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
end end
output = nil output = nil
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
case args.last case args.last
when Hash when Hash
options = args.pop options = args.pop
output = options[:output] if options[:output] output = options[:output] if options[:output]
options[:registers]&.each do |key, register| registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
context_register[key] = register
end
apply_options_to_context(context, options) apply_options_to_context(context, options)
when Module, Array when Module, Array
context.add_filters(args.pop) context.add_filters(args.pop)
end end
Template.registers.each do |key, register|
context_register[key] = register
end
# Retrying a render resets resource usage # Retrying a render resets resource usage
context.resource_limits.reset context.resource_limits.reset
@@ -226,7 +213,7 @@ module Liquid
# render the nodelist. # render the nodelist.
# for performance reasons we get an array back here. join will make a string out of it. # for performance reasons we get an array back here. join will make a string out of it.
with_profiling(context) do with_profiling(context) do
@root.render_to_output_buffer(context, output || +'') @root.render_to_output_buffer(context, output || '')
end end
rescue Liquid::MemoryError => e rescue Liquid::MemoryError => e
context.handle_error(e) context.handle_error(e)
@@ -254,7 +241,7 @@ module Liquid
if @profiling && !context.partial if @profiling && !context.partial
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler) raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
@profiler = Profiler.new(context.template_name) @profiler = Profiler.new
@profiler.start @profiler.start
begin begin

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
class Tokenizer class Tokenizer
attr_reader :line_number, :for_liquid_tag attr_reader :line_number, :for_liquid_tag

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
module Usage module Usage
def self.increment(name) def self.increment(name)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
module Utils module Utils
def self.slice_collection(collection, from, to) def self.slice_collection(collection, from, to)
@@ -52,7 +50,7 @@ module Liquid
when Numeric when Numeric
obj obj
when String when String
/\A-?\d+\.\d+\z/.match?(obj.strip) ? BigDecimal(obj) : obj.to_i obj.strip =~ /\A-?\d+\.\d+\z/ ? BigDecimal(obj) : obj.to_i
else else
if obj.respond_to?(:to_number) if obj.respond_to?(:to_number)
obj.to_number obj.to_number
@@ -71,7 +69,7 @@ module Liquid
end end
case obj case obj
when 'now', 'today' when 'now'.freeze, 'today'.freeze
Time.now Time.now
when /\A\d+\z/, Integer when /\A\d+\z/, Integer
Time.at(obj.to_i) Time.at(obj.to_i)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module Liquid module Liquid
# Holds variables. Variables are only loaded "just in time" # Holds variables. Variables are only loaded "just in time"
# and are not evaluated as part of the render stage # and are not evaluated as part of the render stage
@@ -104,21 +102,13 @@ module Liquid
output output
end end
def disabled?(_context)
false
end
def disabled_tags
[]
end
private private
def parse_filter_expressions(filter_name, unparsed_args) def parse_filter_expressions(filter_name, unparsed_args)
filter_args = [] filter_args = []
keyword_args = nil keyword_args = nil
unparsed_args.each do |a| unparsed_args.each do |a|
if (matches = a.match(JustTagAttributes)) if matches = a.match(JustTagAttributes)
keyword_args ||= {} keyword_args ||= {}
keyword_args[matches[1]] = Expression.parse(matches[2]) keyword_args[matches[1]] = Expression.parse(matches[2])
else else

View File

@@ -1,9 +1,7 @@
# frozen_string_literal: true
module Liquid module Liquid
class VariableLookup class VariableLookup
SQUARE_BRACKETED = /\A\[(.*)\]\z/m SQUARE_BRACKETED = /\A\[(.*)\]\z/m
COMMAND_METHODS = ['size', 'first', 'last'].freeze COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
attr_reader :name, :lookups attr_reader :name, :lookups

View File

@@ -1,6 +1,5 @@
# encoding: utf-8 # encoding: utf-8
# frozen_string_literal: true
module Liquid module Liquid
VERSION = "4.0.3" VERSION = "4.0.3".freeze
end end

View File

@@ -1,5 +1,4 @@
# encoding: utf-8 # encoding: utf-8
# frozen_string_literal: true
lib = File.expand_path('../lib/', __FILE__) lib = File.expand_path('../lib/', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'benchmark/ips' require 'benchmark/ips'
require_relative 'theme_runner' require_relative 'theme_runner'

View File

@@ -21,8 +21,8 @@ class Profiler
end end
def profile(phase, &block) def profile(phase, &block)
print(LOG_LABEL) print LOG_LABEL
print("#{phase}.. ".ljust(10)) print "#{phase}.. ".ljust(10)
report = MemoryProfiler.report(&block) report = MemoryProfiler.report(&block)
puts 'Done.' puts 'Done.'
@headings << phase.capitalize @headings << phase.capitalize

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'stackprof' require 'stackprof'
require_relative 'theme_runner' require_relative 'theme_runner'
@@ -15,7 +13,7 @@ profiler.run
end end
end end
if profile_type == :cpu && (graph_filename = ENV['GRAPH_FILENAME']) if profile_type == :cpu && graph_filename = ENV['GRAPH_FILENAME']
File.open(graph_filename, 'w') do |f| File.open(graph_filename, 'w') do |f|
StackProf::Report.new(results).print_graphviz(nil, f) StackProf::Report.new(results).print_graphviz(nil, f)
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
class CommentForm < Liquid::Block class CommentForm < Liquid::Block
Syntax = /(#{Liquid::VariableSignature}+)/ Syntax = /(#{Liquid::VariableSignature}+)/

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'yaml' require 'yaml'
module Database module Database
@@ -32,8 +30,8 @@ module Database
db['article'] = db['blog']['articles'].first db['article'] = db['blog']['articles'].first
db['cart'] = { db['cart'] = {
'total_price' => db['line_items'].values.inject(0) { |sum, item| sum + item['line_price'] * item['quantity'] }, 'total_price' => db['line_items'].values.inject(0) { |sum, item| sum += item['line_price'] * item['quantity'] },
'item_count' => db['line_items'].values.inject(0) { |sum, item| sum + item['quantity'] }, 'item_count' => db['line_items'].values.inject(0) { |sum, item| sum += item['quantity'] },
'items' => db['line_items'].values, 'items' => db['line_items'].values,
} }

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'json' require 'json'
module JsonFilter module JsonFilter

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
$LOAD_PATH.unshift(__dir__ + '/../../lib') $LOAD_PATH.unshift(__dir__ + '/../../lib')
require_relative '../../lib/liquid' require_relative '../../lib/liquid'

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module MoneyFilter module MoneyFilter
def money_with_currency(money) def money_with_currency(money)
return '' if money.nil? return '' if money.nil?

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
class Paginate < Liquid::Block class Paginate < Liquid::Block
Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/ Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module ShopFilter module ShopFilter
def asset_url(input) def asset_url(input)
"/files/1/[shop_id]/[shop_id]/assets/#{input}" "/files/1/[shop_id]/[shop_id]/assets/#{input}"
@@ -31,7 +29,7 @@ module ShopFilter
def link_to_vendor(vendor) def link_to_vendor(vendor)
if vendor if vendor
link_to(vendor, url_for_vendor(vendor), vendor) link_to vendor, url_for_vendor(vendor), vendor
else else
'Unknown Vendor' 'Unknown Vendor'
end end
@@ -39,7 +37,7 @@ module ShopFilter
def link_to_type(type) def link_to_type(type)
if type if type
link_to(type, url_for_type(type), type) link_to type, url_for_type(type), type
else else
'Unknown Vendor' 'Unknown Vendor'
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module TagFilter module TagFilter
def link_to_tag(label, tag) def link_to_tag(label, tag)
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tag}\">#{label}</a>" "<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tag}\">#{label}</a>"

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
module WeightFilter module WeightFilter
def weight(grams) def weight(grams)
format("%.2f", grams / 1000) format("%.2f", grams / 1000)

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
# This profiler run simulates Shopify. # This profiler run simulates Shopify.
# We are looking in the tests directory for liquid files and render them within the designated layout file. # We are looking in the tests directory for liquid files and render them within the designated layout file.
# We will also export a substantial database to liquid which the templates can render values of. # We will also export a substantial database to liquid which the templates can render values of.

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class AssignTest < Minitest::Test class AssignTest < Minitest::Test
@@ -12,7 +10,7 @@ class AssignTest < Minitest::Test
END_TEMPLATE END_TEMPLATE
template = Template.parse(template_source) template = Template.parse(template_source)
rendered = template.render! rendered = template.render!
assert_equal("Print this-thing", rendered.strip) assert_equal "Print this-thing", rendered.strip
end end
def test_assigned_variable def test_assigned_variable

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class FoobarTag < Liquid::Tag class FoobarTag < Liquid::Tag
@@ -96,9 +94,9 @@ class BlankTest < Minitest::Test
def test_include_is_blank def test_include_is_blank
Liquid::Template.file_system = BlankTestFileSystem.new Liquid::Template.file_system = BlankTestFileSystem.new
assert_template_result("foobar" * (N + 1), wrap("{% include 'foobar' %}")) assert_template_result "foobar" * (N + 1), wrap("{% include 'foobar' %}")
assert_template_result(" foobar " * (N + 1), wrap("{% include ' foobar ' %}")) assert_template_result " foobar " * (N + 1), wrap("{% include ' foobar ' %}")
assert_template_result(" " * (N + 1), wrap(" {% include ' ' %} ")) assert_template_result " " * (N + 1), wrap(" {% include ' ' %} ")
end end
def test_case_is_blank def test_case_is_blank

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class BlockTest < Minitest::Test class BlockTest < Minitest::Test
@@ -9,6 +7,6 @@ class BlockTest < Minitest::Test
exc = assert_raises(SyntaxError) do exc = assert_raises(SyntaxError) do
Template.parse("{% if true %}{% endunless %}") Template.parse("{% if true %}{% endunless %}")
end end
assert_equal(exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif") assert_equal exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif"
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class CaptureTest < Minitest::Test class CaptureTest < Minitest::Test
@@ -16,7 +14,7 @@ class CaptureTest < Minitest::Test
END_TEMPLATE END_TEMPLATE
template = Template.parse(template_source) template = Template.parse(template_source)
rendered = template.render! rendered = template.render!
assert_equal("Print this-thing", rendered.strip) assert_equal "Print this-thing", rendered.strip
end end
def test_capture_to_variable_from_outer_scope_if_existing def test_capture_to_variable_from_outer_scope_if_existing
@@ -32,7 +30,7 @@ class CaptureTest < Minitest::Test
END_TEMPLATE END_TEMPLATE
template = Template.parse(template_source) template = Template.parse(template_source)
rendered = template.render! rendered = template.render!
assert_equal("test-string", rendered.gsub(/\s/, '')) assert_equal "test-string", rendered.gsub(/\s/, '')
end end
def test_assigning_from_capture def test_assigning_from_capture
@@ -47,6 +45,6 @@ class CaptureTest < Minitest::Test
END_TEMPLATE END_TEMPLATE
template = Template.parse(template_source) template = Template.parse(template_source)
rendered = template.render! rendered = template.render!
assert_equal("3-3", rendered.gsub(/\s/, '')) assert_equal "3-3", rendered.gsub(/\s/, '')
end end
end # CaptureTest end # CaptureTest

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class ContextTest < Minitest::Test class ContextTest < Minitest::Test

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class DocumentTest < Minitest::Test class DocumentTest < Minitest::Test
@@ -9,13 +7,13 @@ class DocumentTest < Minitest::Test
exc = assert_raises(SyntaxError) do exc = assert_raises(SyntaxError) do
Template.parse("{% else %}") Template.parse("{% else %}")
end end
assert_equal(exc.message, "Liquid syntax error: Unexpected outer 'else' tag") assert_equal exc.message, "Liquid syntax error: Unexpected outer 'else' tag"
end end
def test_unknown_tag def test_unknown_tag
exc = assert_raises(SyntaxError) do exc = assert_raises(SyntaxError) do
Template.parse("{% foo %}") Template.parse("{% foo %}")
end end
assert_equal(exc.message, "Liquid syntax error: Unknown tag 'foo'") assert_equal exc.message, "Liquid syntax error: Unknown tag 'foo'"
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class ContextDrop < Liquid::Drop class ContextDrop < Liquid::Drop
@@ -33,7 +31,7 @@ class ProductDrop < Liquid::Drop
class CatchallDrop < Liquid::Drop class CatchallDrop < Liquid::Drop
def liquid_method_missing(method) def liquid_method_missing(method)
"catchall_method: #{method}" 'catchall_method: ' << method.to_s
end end
end end
@@ -50,7 +48,7 @@ class ProductDrop < Liquid::Drop
end end
def user_input def user_input
(+"foo").taint "foo".taint
end end
protected protected
@@ -111,7 +109,7 @@ class DropsTest < Minitest::Test
def test_product_drop def test_product_drop
tpl = Liquid::Template.parse(' ') tpl = Liquid::Template.parse(' ')
assert_equal(' ', tpl.render!('product' => ProductDrop.new)) assert_equal ' ', tpl.render!('product' => ProductDrop.new)
end end
def test_rendering_raises_on_tainted_attr def test_rendering_raises_on_tainted_attr
@@ -141,57 +139,52 @@ class DropsTest < Minitest::Test
end end
def test_drop_does_only_respond_to_whitelisted_methods 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.inspect }}").render!('product' => ProductDrop.new)
assert_equal("", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)) assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)
assert_equal("", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new)) assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new)
assert_equal("", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new)) assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new)
assert_equal("", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new)) assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new)
assert_equal("", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new)) assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new)
end end
def test_drops_respond_to_to_liquid def test_drops_respond_to_to_liquid
assert_equal("text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new)) assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new)
assert_equal("text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new)) assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new)
end end
def test_text_drop def test_text_drop
output = Liquid::Template.parse(' {{ product.texts.text }} ').render!('product' => ProductDrop.new) output = Liquid::Template.parse(' {{ product.texts.text }} ').render!('product' => ProductDrop.new)
assert_equal(' text1 ', output) assert_equal ' text1 ', output
end end
def test_catchall_unknown_method def test_catchall_unknown_method
output = Liquid::Template.parse(' {{ product.catchall.unknown }} ').render!('product' => ProductDrop.new) output = Liquid::Template.parse(' {{ product.catchall.unknown }} ').render!('product' => ProductDrop.new)
assert_equal(' catchall_method: unknown ', output) assert_equal ' catchall_method: unknown ', output
end end
def test_catchall_integer_argument_drop def test_catchall_integer_argument_drop
output = Liquid::Template.parse(' {{ product.catchall[8] }} ').render!('product' => ProductDrop.new) output = Liquid::Template.parse(' {{ product.catchall[8] }} ').render!('product' => ProductDrop.new)
assert_equal(' catchall_method: 8 ', output) assert_equal ' catchall_method: 8 ', output
end end
def test_text_array_drop def test_text_array_drop
output = Liquid::Template.parse('{% for text in product.texts.array %} {{text}} {% endfor %}').render!('product' => ProductDrop.new) output = Liquid::Template.parse('{% for text in product.texts.array %} {{text}} {% endfor %}').render!('product' => ProductDrop.new)
assert_equal(' text1 text2 ', output) assert_equal ' text1 text2 ', output
end end
def test_context_drop def test_context_drop
output = Liquid::Template.parse(' {{ context.bar }} ').render!('context' => ContextDrop.new, 'bar' => "carrot") output = Liquid::Template.parse(' {{ context.bar }} ').render!('context' => ContextDrop.new, 'bar' => "carrot")
assert_equal(' carrot ', output) assert_equal ' carrot ', output
end
def test_context_drop_array_with_map
output = Liquid::Template.parse(' {{ contexts | map: "bar" }} ').render!('contexts' => [ContextDrop.new, ContextDrop.new], 'bar' => "carrot")
assert_equal(' carrotcarrot ', output)
end end
def test_nested_context_drop def test_nested_context_drop
output = Liquid::Template.parse(' {{ product.context.foo }} ').render!('product' => ProductDrop.new, 'foo' => "monkey") output = Liquid::Template.parse(' {{ product.context.foo }} ').render!('product' => ProductDrop.new, 'foo' => "monkey")
assert_equal(' monkey ', output) assert_equal ' monkey ', output
end end
def test_protected def test_protected
output = Liquid::Template.parse(' {{ product.callmenot }} ').render!('product' => ProductDrop.new) output = Liquid::Template.parse(' {{ product.callmenot }} ').render!('product' => ProductDrop.new)
assert_equal(' ', output) assert_equal ' ', output
end end
def test_object_methods_not_allowed def test_object_methods_not_allowed
@@ -202,40 +195,40 @@ class DropsTest < Minitest::Test
end end
def test_scope def test_scope
assert_equal('1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new)) assert_equal '1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new)
assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])) assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])) assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
end end
def test_scope_though_proc def test_scope_though_proc
assert_equal('1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] })) assert_equal '1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] })
assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1])) assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1])
assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1])) assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1])
end end
def test_scope_with_assigns def test_scope_with_assigns
assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new)) assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new)
assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])) assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
assert_equal('test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new)) assert_equal 'test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new)
assert_equal('test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new)) assert_equal 'test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new)
end end
def test_scope_from_tags def test_scope_from_tags
assert_equal('1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])) assert_equal '1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
assert_equal('12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])) assert_equal '12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])) assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
end end
def test_access_context_from_drop def test_access_context_from_drop
assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3])) assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3])
end end
def test_enumerable_drop def test_enumerable_drop
assert_equal('123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new)) assert_equal '123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new)
end end
def test_enumerable_drop_size def test_enumerable_drop_size
assert_equal('3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new)) assert_equal '3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new)
end end
def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
@@ -255,7 +248,7 @@ class DropsTest < Minitest::Test
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new) assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
end end
assert_equal("yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new)) assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new)
[:min, :first].each do |method| [:min, :first].each do |method|
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new) assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
@@ -266,22 +259,15 @@ class DropsTest < Minitest::Test
end end
def test_empty_string_value_access def test_empty_string_value_access
assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => '')) assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => '')
end end
def test_nil_value_access def test_nil_value_access
assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil)) assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil)
end end
def test_default_to_s_on_drops def test_default_to_s_on_drops
assert_equal('ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)) assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)
assert_equal('EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new)) assert_equal 'EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new)
end
def test_invokable_methods
assert_equal(%w(to_liquid catchall user_input context texts).to_set, ProductDrop.invokable_methods)
assert_equal(%w(to_liquid scopes_as_array loop_pos scopes).to_set, ContextDrop.invokable_methods)
assert_equal(%w(to_liquid size max min first count).to_set, EnumerableDrop.invokable_methods)
assert_equal(%w(to_liquid max min sort count first).to_set, RealEnumerableDrop.invokable_methods)
end end
end # DropsTest end # DropsTest

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class ErrorHandlingTest < Minitest::Test class ErrorHandlingTest < Minitest::Test
@@ -35,31 +33,31 @@ class ErrorHandlingTest < Minitest::Test
TEXT TEXT
output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new) output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
assert_equal(expected, output) assert_equal expected, output
end end
def test_standard_error def test_standard_error
template = Liquid::Template.parse(' {{ errors.standard_error }} ') template = Liquid::Template.parse(' {{ errors.standard_error }} ')
assert_equal(' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)) assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
assert_equal(1, template.errors.size) assert_equal 1, template.errors.size
assert_equal(StandardError, template.errors.first.class) assert_equal StandardError, template.errors.first.class
end end
def test_syntax def test_syntax
template = Liquid::Template.parse(' {{ errors.syntax_error }} ') template = Liquid::Template.parse(' {{ errors.syntax_error }} ')
assert_equal(' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)) assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
assert_equal(1, template.errors.size) assert_equal 1, template.errors.size
assert_equal(SyntaxError, template.errors.first.class) assert_equal SyntaxError, template.errors.first.class
end end
def test_argument def test_argument
template = Liquid::Template.parse(' {{ errors.argument_error }} ') template = Liquid::Template.parse(' {{ errors.argument_error }} ')
assert_equal(' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)) assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
assert_equal(1, template.errors.size) assert_equal 1, template.errors.size
assert_equal(ArgumentError, template.errors.first.class) assert_equal ArgumentError, template.errors.first.class
end end
def test_missing_endtag_parse_time_error def test_missing_endtag_parse_time_error
@@ -78,9 +76,9 @@ class ErrorHandlingTest < Minitest::Test
def test_lax_unrecognized_operator def test_lax_unrecognized_operator
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax) template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
assert_equal(' Liquid error: Unknown operator =! ', template.render) assert_equal ' Liquid error: Unknown operator =! ', template.render
assert_equal(1, template.errors.size) assert_equal 1, template.errors.size
assert_equal(Liquid::ArgumentError, template.errors.first.class) assert_equal Liquid::ArgumentError, template.errors.first.class
end end
def test_with_line_numbers_adds_numbers_to_parser_errors def test_with_line_numbers_adds_numbers_to_parser_errors
@@ -124,8 +122,8 @@ class ErrorHandlingTest < Minitest::Test
error_mode: :warn, error_mode: :warn,
line_numbers: true) line_numbers: true)
assert_equal(['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'], assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
template.warnings.map(&:message)) template.warnings.map(&:message)
end end
def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
@@ -141,7 +139,7 @@ class ErrorHandlingTest < Minitest::Test
line_numbers: true) line_numbers: true)
end end
assert_equal('Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message) assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
end end
def test_syntax_errors_in_nested_blocks_have_correct_line_number def test_syntax_errors_in_nested_blocks_have_correct_line_number
@@ -158,37 +156,37 @@ class ErrorHandlingTest < Minitest::Test
line_numbers: true) line_numbers: true)
end end
assert_equal("Liquid syntax error (line 5): Unknown tag 'foo'", err.message) assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
end end
def test_strict_error_messages def test_strict_error_messages
err = assert_raises(SyntaxError) do err = assert_raises(SyntaxError) do
Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :strict) Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :strict)
end end
assert_equal('Liquid syntax error: Unexpected character = in "1 =! 2"', err.message) assert_equal 'Liquid syntax error: Unexpected character = in "1 =! 2"', err.message
err = assert_raises(SyntaxError) do err = assert_raises(SyntaxError) do
Liquid::Template.parse('{{%%%}}', error_mode: :strict) Liquid::Template.parse('{{%%%}}', error_mode: :strict)
end end
assert_equal('Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message) assert_equal 'Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message
end end
def test_warnings def test_warnings
template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', error_mode: :warn) template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', error_mode: :warn)
assert_equal(3, template.warnings.size) assert_equal 3, template.warnings.size
assert_equal('Unexpected character ~ in "~~~"', template.warnings[0].to_s(false)) assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].to_s(false)
assert_equal('Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false)) assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false)
assert_equal('Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false)) assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false)
assert_equal('', template.render) assert_equal '', template.render
end end
def test_warning_line_numbers def test_warning_line_numbers
template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", error_mode: :warn, line_numbers: true) template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", error_mode: :warn, line_numbers: true)
assert_equal('Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message) assert_equal 'Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message
assert_equal('Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message) assert_equal 'Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message
assert_equal('Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message) assert_equal 'Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message
assert_equal(3, template.warnings.size) assert_equal 3, template.warnings.size
assert_equal([1, 2, 3], template.warnings.map(&:line_number)) assert_equal [1, 2, 3], template.warnings.map(&:line_number)
end end
# Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
@@ -204,23 +202,20 @@ class ErrorHandlingTest < Minitest::Test
output = template.render('errors' => ErrorDrop.new) output = template.render('errors' => ErrorDrop.new)
assert_equal('This is a runtime error: Liquid error (line 1): internal', output) assert_equal 'This is a runtime error: Liquid error (line 1): internal', output
assert_equal([Liquid::InternalError], template.errors.map(&:class)) assert_equal [Liquid::InternalError], template.errors.map(&:class)
end end
def test_setting_default_exception_renderer def test_setting_default_exception_renderer
old_exception_renderer = Liquid::Template.default_exception_renderer old_exception_renderer = Liquid::Template.default_exception_renderer
exceptions = [] exceptions = []
Liquid::Template.default_exception_renderer = ->(e) { Liquid::Template.default_exception_renderer = ->(e) { exceptions << e; '' }
exceptions << e
''
}
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}') template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
output = template.render('errors' => ErrorDrop.new) output = template.render('errors' => ErrorDrop.new)
assert_equal('This is a runtime error: ', output) assert_equal 'This is a runtime error: ', output
assert_equal([Liquid::ArgumentError], template.errors.map(&:class)) assert_equal [Liquid::ArgumentError], template.errors.map(&:class)
ensure ensure
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
end end
@@ -228,17 +223,14 @@ class ErrorHandlingTest < Minitest::Test
def test_exception_renderer_exposing_non_liquid_error def test_exception_renderer_exposing_non_liquid_error
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true) template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
exceptions = [] exceptions = []
handler = ->(e) { handler = ->(e) { exceptions << e; e.cause }
exceptions << e
e.cause
}
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler) output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
assert_equal('This is a runtime error: runtime error', output) assert_equal 'This is a runtime error: runtime error', output
assert_equal([Liquid::InternalError], exceptions.map(&:class)) assert_equal [Liquid::InternalError], exceptions.map(&:class)
assert_equal(exceptions, template.errors) assert_equal exceptions, template.errors
assert_equal('#<RuntimeError: runtime error>', exceptions.first.cause.inspect) assert_equal '#<RuntimeError: runtime error>', exceptions.first.cause.inspect
end end
class TestFileSystem class TestFileSystem
@@ -257,7 +249,7 @@ class ErrorHandlingTest < Minitest::Test
ensure ensure
Liquid::Template.file_system = old_file_system Liquid::Template.file_system = old_file_system
end end
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page) assert_equal "Argument error:\nLiquid error (product line 1): argument error", page
assert_equal("product", template.errors.first.template_name) assert_equal "product", template.errors.first.template_name
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
module MoneyFilter module MoneyFilter
@@ -41,13 +39,13 @@ class FiltersTest < Minitest::Test
@context['var'] = 1000 @context['var'] = 1000
@context.add_filters(MoneyFilter) @context.add_filters(MoneyFilter)
assert_equal(' 1000$ ', Template.parse("{{var | money}}").render(@context)) assert_equal ' 1000$ ', Template.parse("{{var | money}}").render(@context)
end end
def test_underscore_in_filter_name def test_underscore_in_filter_name
@context['var'] = 1000 @context['var'] = 1000
@context.add_filters(MoneyFilter) @context.add_filters(MoneyFilter)
assert_equal(' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context)) assert_equal ' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context)
end end
def test_second_filter_overwrites_first def test_second_filter_overwrites_first
@@ -55,20 +53,20 @@ class FiltersTest < Minitest::Test
@context.add_filters(MoneyFilter) @context.add_filters(MoneyFilter)
@context.add_filters(CanadianMoneyFilter) @context.add_filters(CanadianMoneyFilter)
assert_equal(' 1000$ CAD ', Template.parse("{{var | money}}").render(@context)) assert_equal ' 1000$ CAD ', Template.parse("{{var | money}}").render(@context)
end end
def test_size def test_size
@context['var'] = 'abcd' @context['var'] = 'abcd'
@context.add_filters(MoneyFilter) @context.add_filters(MoneyFilter)
assert_equal('4', Template.parse("{{var | size}}").render(@context)) assert_equal '4', Template.parse("{{var | size}}").render(@context)
end end
def test_join def test_join
@context['var'] = [1, 2, 3, 4] @context['var'] = [1, 2, 3, 4]
assert_equal("1 2 3 4", Template.parse("{{var | join}}").render(@context)) assert_equal "1 2 3 4", Template.parse("{{var | join}}").render(@context)
end end
def test_sort def test_sort
@@ -78,11 +76,11 @@ class FiltersTest < Minitest::Test
@context['arrays'] = ['flower', 'are'] @context['arrays'] = ['flower', 'are']
@context['case_sensitive'] = ['sensitive', 'Expected', 'case'] @context['case_sensitive'] = ['sensitive', 'Expected', 'case']
assert_equal('1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context)) assert_equal '1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context)
assert_equal('alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context)) assert_equal 'alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context)
assert_equal('3', Template.parse("{{value | sort}}").render(@context)) assert_equal '3', Template.parse("{{value | sort}}").render(@context)
assert_equal('are flower', Template.parse("{{arrays | sort | join}}").render(@context)) assert_equal 'are flower', Template.parse("{{arrays | sort | join}}").render(@context)
assert_equal('Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context)) assert_equal 'Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context)
end end
def test_sort_natural def test_sort_natural
@@ -91,13 +89,13 @@ class FiltersTest < Minitest::Test
@context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')] @context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
# Test strings # Test strings
assert_equal('Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context)) assert_equal 'Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context)
# Test hashes # Test hashes
assert_equal('A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context)) assert_equal 'A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context)
# Test objects # Test objects
assert_equal('A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context)) assert_equal 'A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context)
end end
def test_compact def test_compact
@@ -106,37 +104,37 @@ class FiltersTest < Minitest::Test
@context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')] @context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
# Test strings # Test strings
assert_equal('a b c', Template.parse("{{words | compact | join}}").render(@context)) assert_equal 'a b c', Template.parse("{{words | compact | join}}").render(@context)
# Test hashes # Test hashes
assert_equal('A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context)) assert_equal 'A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context)
# Test objects # Test objects
assert_equal('A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context)) assert_equal 'A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context)
end end
def test_strip_html def test_strip_html
@context['var'] = "<b>bla blub</a>" @context['var'] = "<b>bla blub</a>"
assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context)) assert_equal "bla blub", Template.parse("{{ var | strip_html }}").render(@context)
end end
def test_strip_html_ignore_comments_with_html def test_strip_html_ignore_comments_with_html
@context['var'] = "<!-- split and some <ul> tag --><b>bla blub</a>" @context['var'] = "<!-- split and some <ul> tag --><b>bla blub</a>"
assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context)) assert_equal "bla blub", Template.parse("{{ var | strip_html }}").render(@context)
end end
def test_capitalize def test_capitalize
@context['var'] = "blub" @context['var'] = "blub"
assert_equal("Blub", Template.parse("{{ var | capitalize }}").render(@context)) assert_equal "Blub", Template.parse("{{ var | capitalize }}").render(@context)
end end
def test_nonexistent_filter_is_ignored def test_nonexistent_filter_is_ignored
@context['var'] = 1000 @context['var'] = 1000
assert_equal('1000', Template.parse("{{ var | xyzzy }}").render(@context)) assert_equal '1000', Template.parse("{{ var | xyzzy }}").render(@context)
end end
def test_filter_with_keyword_arguments def test_filter_with_keyword_arguments
@@ -144,14 +142,14 @@ class FiltersTest < Minitest::Test
@context['input'] = 'hello %{first_name}, %{last_name}' @context['input'] = 'hello %{first_name}, %{last_name}'
@context.add_filters(SubstituteFilter) @context.add_filters(SubstituteFilter)
output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context) output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context)
assert_equal('hello john, doe', output) assert_equal 'hello john, doe', output
end end
def test_override_object_method_in_filter def test_override_object_method_in_filter
assert_equal("tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter])) assert_equal "tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter])
# tap still treated as a non-existent filter # tap still treated as a non-existent filter
assert_equal("1000", Template.parse("{{var | tap}}").render!('var' => 1000)) assert_equal "1000", Template.parse("{{var | tap}}").render!('var' => 1000)
end end
end end
@@ -167,8 +165,8 @@ class FiltersInTemplate < Minitest::Test
end end
def test_local_filter_with_deprecated_syntax def test_local_filter_with_deprecated_syntax
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter)) assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter)
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter])) assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter])
end end
end # FiltersTest end # FiltersTest

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class HashOrderingTest < Minitest::Test class HashOrderingTest < Minitest::Test

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
module FunnyFilter module FunnyFilter
@@ -42,84 +40,84 @@ class OutputTest < Minitest::Test
text = %( {{best_cars}} ) text = %( {{best_cars}} )
expected = %( bmw ) expected = %( bmw )
assert_equal(expected, Template.parse(text).render!(@assigns)) assert_equal expected, Template.parse(text).render!(@assigns)
end end
def test_variable_traversing_with_two_brackets def test_variable_traversing_with_two_brackets
text = %({{ site.data.menu[include.menu][include.locale] }}) text = %({{ site.data.menu[include.menu][include.locale] }})
assert_equal("it works!", Template.parse(text).render!( assert_equal "it works!", Template.parse(text).render!(
"site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } }, "site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } },
"include" => { "menu" => "foo", "locale" => "bar" } "include" => { "menu" => "foo", "locale" => "bar" }
)) )
end end
def test_variable_traversing def test_variable_traversing
text = %( {{car.bmw}} {{car.gm}} {{car.bmw}} ) text = %( {{car.bmw}} {{car.gm}} {{car.bmw}} )
expected = %( good bad good ) expected = %( good bad good )
assert_equal(expected, Template.parse(text).render!(@assigns)) assert_equal expected, Template.parse(text).render!(@assigns)
end end
def test_variable_piping def test_variable_piping
text = %( {{ car.gm | make_funny }} ) text = %( {{ car.gm | make_funny }} )
expected = %( LOL ) expected = %( LOL )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_variable_piping_with_input def test_variable_piping_with_input
text = %( {{ car.gm | cite_funny }} ) text = %( {{ car.gm | cite_funny }} )
expected = %( LOL: bad ) expected = %( LOL: bad )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_variable_piping_with_args def test_variable_piping_with_args
text = %! {{ car.gm | add_smiley : ':-(' }} ! text = %! {{ car.gm | add_smiley : ':-(' }} !
expected = %| bad :-( | expected = %| bad :-( |
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_variable_piping_with_no_args def test_variable_piping_with_no_args
text = %( {{ car.gm | add_smiley }} ) text = %( {{ car.gm | add_smiley }} )
expected = %| bad :-) | expected = %| bad :-) |
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_multiple_variable_piping_with_args def test_multiple_variable_piping_with_args
text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} ! text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
expected = %| bad :-( :-( | expected = %| bad :-( :-( |
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_variable_piping_with_multiple_args def test_variable_piping_with_multiple_args
text = %( {{ car.gm | add_tag : 'span', 'bar'}} ) text = %( {{ car.gm | add_tag : 'span', 'bar'}} )
expected = %( <span id="bar">bad</span> ) expected = %( <span id="bar">bad</span> )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_variable_piping_with_variable_args def test_variable_piping_with_variable_args
text = %( {{ car.gm | add_tag : 'span', car.bmw}} ) text = %( {{ car.gm | add_tag : 'span', car.bmw}} )
expected = %( <span id="good">bad</span> ) expected = %( <span id="good">bad</span> )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_multiple_pipings def test_multiple_pipings
text = %( {{ best_cars | cite_funny | paragraph }} ) text = %( {{ best_cars | cite_funny | paragraph }} )
expected = %( <p>LOL: bmw</p> ) expected = %( <p>LOL: bmw</p> )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
def test_link_to def test_link_to
text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} ) text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
expected = %( <a href="http://typo.leetsoft.com">Typo</a> ) expected = %( <a href="http://typo.leetsoft.com">Typo</a> )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])) assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
end end
end # OutputTest end # OutputTest

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class ParsingQuirksTest < Minitest::Test class ParsingQuirksTest < Minitest::Test
@@ -7,7 +5,7 @@ class ParsingQuirksTest < Minitest::Test
def test_parsing_css def test_parsing_css
text = " div { font-weight: bold; } " text = " div { font-weight: bold; } "
assert_equal(text, Template.parse(text).render!) assert_equal text, Template.parse(text).render!
end end
def test_raise_on_single_close_bracet def test_raise_on_single_close_bracet
@@ -29,7 +27,7 @@ class ParsingQuirksTest < Minitest::Test
end end
def test_error_on_empty_filter def test_error_on_empty_filter
assert(Template.parse("{{test}}")) assert Template.parse("{{test}}")
with_error_mode(:lax) do with_error_mode(:lax) do
assert Template.parse("{{|test}}") assert Template.parse("{{|test}}")
@@ -64,9 +62,9 @@ class ParsingQuirksTest < Minitest::Test
end end
def test_no_error_on_lax_empty_filter def test_no_error_on_lax_empty_filter
assert(Template.parse("{{test |a|b|}}", error_mode: :lax)) assert Template.parse("{{test |a|b|}}", error_mode: :lax)
assert(Template.parse("{{test}}", error_mode: :lax)) assert Template.parse("{{test}}", error_mode: :lax)
assert(Template.parse("{{|test|}}", error_mode: :lax)) assert Template.parse("{{|test|}}", error_mode: :lax)
end end
def test_meaningless_parens_lax def test_meaningless_parens_lax

View File

@@ -1,27 +0,0 @@
# frozen_string_literal: true
require 'test_helper'
class DisabledTagsTest < Minitest::Test
include Liquid
class DisableRaw < Block
disable_tags "raw"
end
class DisableRawEcho < Block
disable_tags "raw", "echo"
end
def test_disables_raw
with_custom_tag('disable', DisableRaw) do
assert_template_result 'raw usage is not allowed in this contextfoo', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
end
end
def test_disables_echo_and_raw
with_custom_tag('disable', DisableRawEcho) do
assert_template_result 'raw usage is not allowed in this contextecho usage is not allowed in this context', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
end
end
end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class RenderProfilingTest < Minitest::Test class RenderProfilingTest < Minitest::Test
@@ -19,35 +17,35 @@ class RenderProfilingTest < Minitest::Test
t = Template.parse("{{ 'a string' | upcase }}") t = Template.parse("{{ 'a string' | upcase }}")
t.render! t.render!
assert_nil(t.profiler) assert_nil t.profiler
end end
def test_parse_makes_available_simple_profiling def test_parse_makes_available_simple_profiling
t = Template.parse("{{ 'a string' | upcase }}", profile: true) t = Template.parse("{{ 'a string' | upcase }}", profile: true)
t.render! t.render!
assert_equal(1, t.profiler.length) assert_equal 1, t.profiler.length
node = t.profiler[0] node = t.profiler[0]
assert_equal(" 'a string' | upcase ", node.code) assert_equal " 'a string' | upcase ", node.code
end end
def test_render_ignores_raw_strings_when_profiling def test_render_ignores_raw_strings_when_profiling
t = Template.parse("This is raw string\nstuff\nNewline", profile: true) t = Template.parse("This is raw string\nstuff\nNewline", profile: true)
t.render! t.render!
assert_equal(0, t.profiler.length) assert_equal 0, t.profiler.length
end end
def test_profiling_includes_line_numbers_of_liquid_nodes def test_profiling_includes_line_numbers_of_liquid_nodes
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true) t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
t.render! t.render!
assert_equal(2, t.profiler.length) assert_equal 2, t.profiler.length
# {{ 'a string' | upcase }} # {{ 'a string' | upcase }}
assert_equal(1, t.profiler[0].line_number) assert_equal 1, t.profiler[0].line_number
# {{ increment test }} # {{ increment test }}
assert_equal(2, t.profiler[1].line_number) assert_equal 2, t.profiler[1].line_number
end end
def test_profiling_includes_line_numbers_of_included_partials def test_profiling_includes_line_numbers_of_included_partials
@@ -57,9 +55,9 @@ class RenderProfilingTest < Minitest::Test
included_children = t.profiler[0].children included_children = t.profiler[0].children
# {% assign template_name = 'a_template' %} # {% assign template_name = 'a_template' %}
assert_equal(1, included_children[0].line_number) assert_equal 1, included_children[0].line_number
# {{ template_name }} # {{ template_name }}
assert_equal(2, included_children[1].line_number) assert_equal 2, included_children[1].line_number
end end
def test_profiling_times_the_rendering_of_tokens def test_profiling_times_the_rendering_of_tokens
@@ -67,14 +65,14 @@ class RenderProfilingTest < Minitest::Test
t.render! t.render!
node = t.profiler[0] node = t.profiler[0]
refute_nil(node.render_time) refute_nil node.render_time
end end
def test_profiling_times_the_entire_render def test_profiling_times_the_entire_render
t = Template.parse("{% include 'a_template' %}", profile: true) t = Template.parse("{% include 'a_template' %}", profile: true)
t.render! t.render!
assert(t.profiler.total_render_time >= 0, "Total render time was not calculated") assert t.profiler.total_render_time >= 0, "Total render time was not calculated"
end end
def test_profiling_uses_include_to_mark_children def test_profiling_uses_include_to_mark_children
@@ -82,7 +80,7 @@ class RenderProfilingTest < Minitest::Test
t.render! t.render!
include_node = t.profiler[1] include_node = t.profiler[1]
assert_equal(2, include_node.children.length) assert_equal 2, include_node.children.length
end end
def test_profiling_marks_children_with_the_name_of_included_partial def test_profiling_marks_children_with_the_name_of_included_partial
@@ -134,38 +132,23 @@ class RenderProfilingTest < Minitest::Test
timing_count += 1 timing_count += 1
end end
assert_equal(2, timing_count) assert_equal 2, timing_count
end end
def test_profiling_marks_children_of_if_blocks def test_profiling_marks_children_of_if_blocks
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true) t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
t.render! t.render!
assert_equal(1, t.profiler.length) assert_equal 1, t.profiler.length
assert_equal(2, t.profiler[0].children.length) assert_equal 2, t.profiler[0].children.length
end end
def test_profiling_marks_children_of_for_blocks def test_profiling_marks_children_of_for_blocks
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true) t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
t.render!("collection" => ["one", "two"]) t.render!("collection" => ["one", "two"])
assert_equal(1, t.profiler.length) assert_equal 1, t.profiler.length
# Will profile each invocation of the for block # Will profile each invocation of the for block
assert_equal(2, t.profiler[0].children.length) assert_equal 2, t.profiler[0].children.length
end
def test_profiling_supports_self_time
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
t.render!("collection" => ["one", "two"])
leaf = t.profiler[0].children[0]
assert_operator leaf.self_time, :>, 0
end
def test_profiling_supports_total_time
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
t.render!
assert_operator t.profiler[0].total_time, :>, 0
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
module SecurityFilter module SecurityFilter
@@ -19,28 +17,28 @@ class SecurityTest < Minitest::Test
text = %( {{ '1+1' | instance_eval }} ) text = %( {{ '1+1' | instance_eval }} )
expected = %( 1+1 ) expected = %( 1+1 )
assert_equal(expected, Template.parse(text).render!(@assigns)) assert_equal expected, Template.parse(text).render!(@assigns)
end end
def test_no_existing_instance_eval def test_no_existing_instance_eval
text = %( {{ '1+1' | __instance_eval__ }} ) text = %( {{ '1+1' | __instance_eval__ }} )
expected = %( 1+1 ) expected = %( 1+1 )
assert_equal(expected, Template.parse(text).render!(@assigns)) assert_equal expected, Template.parse(text).render!(@assigns)
end end
def test_no_instance_eval_after_mixing_in_new_filter def test_no_instance_eval_after_mixing_in_new_filter
text = %( {{ '1+1' | instance_eval }} ) text = %( {{ '1+1' | instance_eval }} )
expected = %( 1+1 ) expected = %( 1+1 )
assert_equal(expected, Template.parse(text).render!(@assigns)) assert_equal expected, Template.parse(text).render!(@assigns)
end end
def test_no_instance_eval_later_in_chain def test_no_instance_eval_later_in_chain
text = %( {{ '1+1' | add_one | instance_eval }} ) text = %( {{ '1+1' | add_one | instance_eval }} )
expected = %( 1+1 + 1 ) expected = %( 1+1 + 1 )
assert_equal(expected, Template.parse(text).render!(@assigns, filters: SecurityFilter)) assert_equal expected, Template.parse(text).render!(@assigns, filters: SecurityFilter)
end end
def test_does_not_add_filters_to_symbol_table def test_does_not_add_filters_to_symbol_table
@@ -49,27 +47,27 @@ class SecurityTest < Minitest::Test
test = %( {{ "some_string" | a_bad_filter }} ) test = %( {{ "some_string" | a_bad_filter }} )
template = Template.parse(test) template = Template.parse(test)
assert_equal([], (Symbol.all_symbols - current_symbols)) assert_equal [], (Symbol.all_symbols - current_symbols)
template.render! template.render!
assert_equal([], (Symbol.all_symbols - current_symbols)) assert_equal [], (Symbol.all_symbols - current_symbols)
end end
def test_does_not_add_drop_methods_to_symbol_table def test_does_not_add_drop_methods_to_symbol_table
current_symbols = Symbol.all_symbols current_symbols = Symbol.all_symbols
assigns = { 'drop' => Drop.new } assigns = { 'drop' => Drop.new }
assert_equal("", Template.parse("{{ drop.custom_method_1 }}", assigns).render!) assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render!
assert_equal("", Template.parse("{{ drop.custom_method_2 }}", assigns).render!) assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render!
assert_equal("", Template.parse("{{ drop.custom_method_3 }}", assigns).render!) assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render!
assert_equal([], (Symbol.all_symbols - current_symbols)) assert_equal [], (Symbol.all_symbols - current_symbols)
end end
def test_max_depth_nested_blocks_does_not_raise_exception def test_max_depth_nested_blocks_does_not_raise_exception
depth = Liquid::Block::MAX_DEPTH depth = Liquid::Block::MAX_DEPTH
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
assert_equal("rendered", Template.parse(code).render!) assert_equal "rendered", Template.parse(code).render!
end end
def test_more_than_max_depth_nested_blocks_raises_exception def test_more_than_max_depth_nested_blocks_raises_exception

View File

@@ -1,5 +1,4 @@
# encoding: utf-8 # encoding: utf-8
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
@@ -60,34 +59,34 @@ class StandardFiltersTest < Minitest::Test
end end
def test_size def test_size
assert_equal(3, @filters.size([1, 2, 3])) assert_equal 3, @filters.size([1, 2, 3])
assert_equal(0, @filters.size([])) assert_equal 0, @filters.size([])
assert_equal(0, @filters.size(nil)) assert_equal 0, @filters.size(nil)
end end
def test_downcase def test_downcase
assert_equal('testing', @filters.downcase("Testing")) assert_equal 'testing', @filters.downcase("Testing")
assert_equal('', @filters.downcase(nil)) assert_equal '', @filters.downcase(nil)
end end
def test_upcase def test_upcase
assert_equal('TESTING', @filters.upcase("Testing")) assert_equal 'TESTING', @filters.upcase("Testing")
assert_equal('', @filters.upcase(nil)) assert_equal '', @filters.upcase(nil)
end end
def test_slice def test_slice
assert_equal('oob', @filters.slice('foobar', 1, 3)) assert_equal 'oob', @filters.slice('foobar', 1, 3)
assert_equal('oobar', @filters.slice('foobar', 1, 1000)) assert_equal 'oobar', @filters.slice('foobar', 1, 1000)
assert_equal('', @filters.slice('foobar', 1, 0)) assert_equal '', @filters.slice('foobar', 1, 0)
assert_equal('o', @filters.slice('foobar', 1, 1)) assert_equal 'o', @filters.slice('foobar', 1, 1)
assert_equal('bar', @filters.slice('foobar', 3, 3)) assert_equal 'bar', @filters.slice('foobar', 3, 3)
assert_equal('ar', @filters.slice('foobar', -2, 2)) assert_equal 'ar', @filters.slice('foobar', -2, 2)
assert_equal('ar', @filters.slice('foobar', -2, 1000)) assert_equal 'ar', @filters.slice('foobar', -2, 1000)
assert_equal('r', @filters.slice('foobar', -1)) assert_equal 'r', @filters.slice('foobar', -1)
assert_equal('', @filters.slice(nil, 0)) assert_equal '', @filters.slice(nil, 0)
assert_equal('', @filters.slice('foobar', 100, 10)) assert_equal '', @filters.slice('foobar', 100, 10)
assert_equal('', @filters.slice('foobar', -100, 10)) assert_equal '', @filters.slice('foobar', -100, 10)
assert_equal('oob', @filters.slice('foobar', '1', '3')) assert_equal 'oob', @filters.slice('foobar', '1', '3')
assert_raises(Liquid::ArgumentError) do assert_raises(Liquid::ArgumentError) do
@filters.slice('foobar', nil) @filters.slice('foobar', nil)
end end
@@ -98,112 +97,109 @@ class StandardFiltersTest < Minitest::Test
def test_slice_on_arrays def test_slice_on_arrays
input = 'foobar'.split(//) input = 'foobar'.split(//)
assert_equal(%w(o o b), @filters.slice(input, 1, 3)) assert_equal %w(o o b), @filters.slice(input, 1, 3)
assert_equal(%w(o o b a r), @filters.slice(input, 1, 1000)) assert_equal %w(o o b a r), @filters.slice(input, 1, 1000)
assert_equal(%w(), @filters.slice(input, 1, 0)) assert_equal %w(), @filters.slice(input, 1, 0)
assert_equal(%w(o), @filters.slice(input, 1, 1)) assert_equal %w(o), @filters.slice(input, 1, 1)
assert_equal(%w(b a r), @filters.slice(input, 3, 3)) assert_equal %w(b a r), @filters.slice(input, 3, 3)
assert_equal(%w(a r), @filters.slice(input, -2, 2)) assert_equal %w(a r), @filters.slice(input, -2, 2)
assert_equal(%w(a r), @filters.slice(input, -2, 1000)) assert_equal %w(a r), @filters.slice(input, -2, 1000)
assert_equal(%w(r), @filters.slice(input, -1)) assert_equal %w(r), @filters.slice(input, -1)
assert_equal(%w(), @filters.slice(input, 100, 10)) assert_equal %w(), @filters.slice(input, 100, 10)
assert_equal(%w(), @filters.slice(input, -100, 10)) assert_equal %w(), @filters.slice(input, -100, 10)
end end
def test_truncate def test_truncate
assert_equal('1234...', @filters.truncate('1234567890', 7)) assert_equal '1234...', @filters.truncate('1234567890', 7)
assert_equal('1234567890', @filters.truncate('1234567890', 20)) assert_equal '1234567890', @filters.truncate('1234567890', 20)
assert_equal('...', @filters.truncate('1234567890', 0)) assert_equal '...', @filters.truncate('1234567890', 0)
assert_equal('1234567890', @filters.truncate('1234567890')) assert_equal '1234567890', @filters.truncate('1234567890')
assert_equal("测试...", @filters.truncate("测试测试测试测试", 5)) assert_equal "测试...", @filters.truncate("测试测试测试测试", 5)
assert_equal('12341', @filters.truncate("1234567890", 5, 1)) assert_equal '12341', @filters.truncate("1234567890", 5, 1)
end end
def test_split def test_split
assert_equal(['12', '34'], @filters.split('12~34', '~')) assert_equal ['12', '34'], @filters.split('12~34', '~')
assert_equal(['A? ', ' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')) assert_equal ['A? ', ' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~')
assert_equal(['A?Z'], @filters.split('A?Z', '~')) assert_equal ['A?Z'], @filters.split('A?Z', '~')
assert_equal([], @filters.split(nil, ' ')) assert_equal [], @filters.split(nil, ' ')
assert_equal(['A', 'Z'], @filters.split('A1Z', 1)) assert_equal ['A', 'Z'], @filters.split('A1Z', 1)
end end
def test_escape def test_escape
assert_equal('&lt;strong&gt;', @filters.escape('<strong>')) assert_equal '&lt;strong&gt;', @filters.escape('<strong>')
assert_equal('1', @filters.escape(1)) assert_equal '1', @filters.escape(1)
assert_equal('2001-02-03', @filters.escape(Date.new(2001, 2, 3))) assert_equal '2001-02-03', @filters.escape(Date.new(2001, 2, 3))
assert_nil(@filters.escape(nil)) assert_nil @filters.escape(nil)
end end
def test_h def test_h
assert_equal('&lt;strong&gt;', @filters.h('<strong>')) assert_equal '&lt;strong&gt;', @filters.h('<strong>')
assert_equal('1', @filters.h(1)) assert_equal '1', @filters.h(1)
assert_equal('2001-02-03', @filters.h(Date.new(2001, 2, 3))) assert_equal '2001-02-03', @filters.h(Date.new(2001, 2, 3))
assert_nil(@filters.h(nil)) assert_nil @filters.h(nil)
end end
def test_escape_once def test_escape_once
assert_equal('&lt;strong&gt;Hulk&lt;/strong&gt;', @filters.escape_once('&lt;strong&gt;Hulk</strong>')) assert_equal '&lt;strong&gt;Hulk&lt;/strong&gt;', @filters.escape_once('&lt;strong&gt;Hulk</strong>')
end end
def test_url_encode def test_url_encode
assert_equal('foo%2B1%40example.com', @filters.url_encode('foo+1@example.com')) assert_equal 'foo%2B1%40example.com', @filters.url_encode('foo+1@example.com')
assert_equal('1', @filters.url_encode(1)) assert_equal '1', @filters.url_encode(1)
assert_equal('2001-02-03', @filters.url_encode(Date.new(2001, 2, 3))) assert_equal '2001-02-03', @filters.url_encode(Date.new(2001, 2, 3))
assert_nil(@filters.url_encode(nil)) assert_nil @filters.url_encode(nil)
end end
def test_url_decode def test_url_decode
assert_equal('foo bar', @filters.url_decode('foo+bar')) assert_equal 'foo bar', @filters.url_decode('foo+bar')
assert_equal('foo bar', @filters.url_decode('foo%20bar')) assert_equal 'foo bar', @filters.url_decode('foo%20bar')
assert_equal('foo+1@example.com', @filters.url_decode('foo%2B1%40example.com')) assert_equal 'foo+1@example.com', @filters.url_decode('foo%2B1%40example.com')
assert_equal('1', @filters.url_decode(1)) assert_equal '1', @filters.url_decode(1)
assert_equal('2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))) assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
assert_nil(@filters.url_decode(nil)) assert_nil @filters.url_decode(nil)
exception = assert_raises Liquid::ArgumentError do exception = assert_raises Liquid::ArgumentError do
@filters.url_decode('%ff') @filters.url_decode('%ff')
end end
assert_equal('Liquid error: invalid byte sequence in UTF-8', exception.message) assert_equal 'Liquid error: invalid byte sequence in UTF-8', exception.message
end end
def test_truncatewords def test_truncatewords
assert_equal('one two three', @filters.truncatewords('one two three', 4)) assert_equal 'one two three', @filters.truncatewords('one two three', 4)
assert_equal('one two...', @filters.truncatewords('one two three', 2)) assert_equal 'one two...', @filters.truncatewords('one two three', 2)
assert_equal('one two three', @filters.truncatewords('one two three')) assert_equal 'one two three', @filters.truncatewords('one two three')
assert_equal( assert_equal 'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...', @filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover.', 15)
'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...', assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5)
@filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover.', 15) assert_equal 'one two1', @filters.truncatewords("one two three", 2, 1)
)
assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
end end
def test_strip_html def test_strip_html
assert_equal('test', @filters.strip_html("<div>test</div>")) assert_equal 'test', @filters.strip_html("<div>test</div>")
assert_equal('test', @filters.strip_html("<div id='test'>test</div>")) assert_equal 'test', @filters.strip_html("<div id='test'>test</div>")
assert_equal('', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>")) assert_equal '', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>")
assert_equal('', @filters.strip_html("<style type='text/css'>foo bar</style>")) assert_equal '', @filters.strip_html("<style type='text/css'>foo bar</style>")
assert_equal('test', @filters.strip_html("<div\nclass='multiline'>test</div>")) assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
assert_equal('test', @filters.strip_html("<!-- foo bar \n test -->test")) assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
assert_equal('', @filters.strip_html(nil)) assert_equal '', @filters.strip_html(nil)
# Quirk of the existing implementation # Quirk of the existing implementation
assert_equal('foo;', @filters.strip_html("<<<script </script>script>foo;</script>")) assert_equal 'foo;', @filters.strip_html("<<<script </script>script>foo;</script>")
end end
def test_join def test_join
assert_equal('1 2 3 4', @filters.join([1, 2, 3, 4])) assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
assert_equal('1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')) assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
assert_equal('1121314', @filters.join([1, 2, 3, 4], 1)) assert_equal '1121314', @filters.join([1, 2, 3, 4], 1)
end end
def test_sort def test_sort
assert_equal([1, 2, 3, 4], @filters.sort([4, 3, 2, 1])) assert_equal [1, 2, 3, 4], @filters.sort([4, 3, 2, 1])
assert_equal([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")) assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
end end
def test_sort_with_nils def test_sort_with_nils
assert_equal([1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1])) assert_equal [1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1])
assert_equal([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a")) assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a")
end end
def test_sort_when_property_is_sometimes_missing_puts_nils_last def test_sort_when_property_is_sometimes_missing_puts_nils_last
@@ -221,17 +217,17 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "delta" }, { "handle" => "delta" },
{ "handle" => "beta" }, { "handle" => "beta" },
] ]
assert_equal(expectation, @filters.sort(input, "price")) assert_equal expectation, @filters.sort(input, "price")
end end
def test_sort_natural def test_sort_natural
assert_equal(["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"])) assert_equal ["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"])
assert_equal([{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a")) assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a")
end end
def test_sort_natural_with_nils def test_sort_natural_with_nils
assert_equal(["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"])) assert_equal ["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"])
assert_equal([{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a")) assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a")
end end
def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
@@ -249,7 +245,7 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "delta" }, { "handle" => "delta" },
{ "handle" => "beta" }, { "handle" => "beta" },
] ]
assert_equal(expectation, @filters.sort_natural(input, "price")) assert_equal expectation, @filters.sort_natural(input, "price")
end end
def test_sort_natural_case_check def test_sort_natural_case_check
@@ -271,12 +267,12 @@ class StandardFiltersTest < Minitest::Test
{ "key" => "Z" }, { "key" => "Z" },
{ "fake" => "t" }, { "fake" => "t" },
] ]
assert_equal(expectation, @filters.sort_natural(input, "key")) assert_equal expectation, @filters.sort_natural(input, "key")
assert_equal(["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"])) assert_equal ["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"])
end end
def test_sort_empty_array def test_sort_empty_array
assert_equal([], @filters.sort([], "a")) assert_equal [], @filters.sort([], "a")
end end
def test_sort_invalid_property def test_sort_invalid_property
@@ -292,7 +288,7 @@ class StandardFiltersTest < Minitest::Test
end end
def test_sort_natural_empty_array def test_sort_natural_empty_array
assert_equal([], @filters.sort_natural([], "a")) assert_equal [], @filters.sort_natural([], "a")
end end
def test_sort_natural_invalid_property def test_sort_natural_invalid_property
@@ -308,26 +304,26 @@ class StandardFiltersTest < Minitest::Test
end end
def test_legacy_sort_hash def test_legacy_sort_hash
assert_equal([{ a: 1, b: 2 }], @filters.sort(a: 1, b: 2)) assert_equal [{ a: 1, b: 2 }], @filters.sort(a: 1, b: 2)
end end
def test_numerical_vs_lexicographical_sort def test_numerical_vs_lexicographical_sort
assert_equal([2, 10], @filters.sort([10, 2])) assert_equal [2, 10], @filters.sort([10, 2])
assert_equal([{ "a" => 2 }, { "a" => 10 }], @filters.sort([{ "a" => 10 }, { "a" => 2 }], "a")) assert_equal [{ "a" => 2 }, { "a" => 10 }], @filters.sort([{ "a" => 10 }, { "a" => 2 }], "a")
assert_equal(["10", "2"], @filters.sort(["10", "2"])) assert_equal ["10", "2"], @filters.sort(["10", "2"])
assert_equal([{ "a" => "10" }, { "a" => "2" }], @filters.sort([{ "a" => "10" }, { "a" => "2" }], "a")) assert_equal [{ "a" => "10" }, { "a" => "2" }], @filters.sort([{ "a" => "10" }, { "a" => "2" }], "a")
end end
def test_uniq def test_uniq
assert_equal(["foo"], @filters.uniq("foo")) assert_equal ["foo"], @filters.uniq("foo")
assert_equal([1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1])) assert_equal [1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1])
assert_equal([{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")) assert_equal [{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
testdrop = TestDrop.new testdrop = TestDrop.new
assert_equal([testdrop], @filters.uniq([testdrop, TestDrop.new], 'test')) assert_equal [testdrop], @filters.uniq([testdrop, TestDrop.new], 'test')
end end
def test_uniq_empty_array def test_uniq_empty_array
assert_equal([], @filters.uniq([], "a")) assert_equal [], @filters.uniq([], "a")
end end
def test_uniq_invalid_property def test_uniq_invalid_property
@@ -343,7 +339,7 @@ class StandardFiltersTest < Minitest::Test
end end
def test_compact_empty_array def test_compact_empty_array
assert_equal([], @filters.compact([], "a")) assert_equal [], @filters.compact([], "a")
end end
def test_compact_invalid_property def test_compact_invalid_property
@@ -359,51 +355,51 @@ class StandardFiltersTest < Minitest::Test
end end
def test_reverse def test_reverse
assert_equal([4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])) assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
end end
def test_legacy_reverse_hash def test_legacy_reverse_hash
assert_equal([{ a: 1, b: 2 }], @filters.reverse(a: 1, b: 2)) assert_equal [{ a: 1, b: 2 }], @filters.reverse(a: 1, b: 2)
end end
def test_map def test_map
assert_equal([1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a')) assert_equal [1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a')
assert_template_result('abc', "{{ ary | map:'foo' | map:'bar' }}", assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }]) 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }]
end end
def test_map_doesnt_call_arbitrary_stuff def test_map_doesnt_call_arbitrary_stuff
assert_template_result("", '{{ "foo" | map: "__id__" }}') assert_template_result "", '{{ "foo" | map: "__id__" }}'
assert_template_result("", '{{ "foo" | map: "inspect" }}') assert_template_result "", '{{ "foo" | map: "inspect" }}'
end end
def test_map_calls_to_liquid def test_map_calls_to_liquid
t = TestThing.new t = TestThing.new
assert_template_result("woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t]) assert_template_result "woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t]
end end
def test_map_on_hashes def test_map_on_hashes
assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}', assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}',
"thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] }) "thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] }
end end
def test_legacy_map_on_hashes_with_dynamic_key def test_legacy_map_on_hashes_with_dynamic_key
template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}" template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}"
hash = { "foo" => { "bar" => 42 } } hash = { "foo" => { "bar" => 42 } }
assert_template_result("42", template, "thing" => hash) assert_template_result "42", template, "thing" => hash
end end
def test_sort_calls_to_liquid def test_sort_calls_to_liquid
t = TestThing.new t = TestThing.new
Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t]) Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t])
assert(t.foo > 0) assert t.foo > 0
end end
def test_map_over_proc def test_map_over_proc
drop = TestDrop.new drop = TestDrop.new
p = proc { drop } p = proc { drop }
templ = '{{ procs | map: "test" }}' templ = '{{ procs | map: "test" }}'
assert_template_result("testfoo", templ, "procs" => [p]) assert_template_result "testfoo", templ, "procs" => [p]
end end
def test_map_over_drops_returning_procs def test_map_over_drops_returning_procs
@@ -416,11 +412,11 @@ class StandardFiltersTest < Minitest::Test
}, },
] ]
templ = '{{ drops | map: "proc" }}' templ = '{{ drops | map: "proc" }}'
assert_template_result("foobar", templ, "drops" => drops) assert_template_result "foobar", templ, "drops" => drops
end end
def test_map_works_on_enumerables def test_map_works_on_enumerables
assert_template_result("123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new) assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
end end
def test_map_returns_empty_on_2d_input_array def test_map_returns_empty_on_2d_input_array
@@ -447,42 +443,42 @@ class StandardFiltersTest < Minitest::Test
end end
def test_sort_works_on_enumerables def test_sort_works_on_enumerables
assert_template_result("213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new) assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
end end
def test_first_and_last_call_to_liquid def test_first_and_last_call_to_liquid
assert_template_result('foobar', '{{ foo | first }}', 'foo' => [ThingWithToLiquid.new]) assert_template_result 'foobar', '{{ foo | first }}', 'foo' => [ThingWithToLiquid.new]
assert_template_result('foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new]) assert_template_result 'foobar', '{{ foo | last }}', 'foo' => [ThingWithToLiquid.new]
end end
def test_truncate_calls_to_liquid def test_truncate_calls_to_liquid
assert_template_result("wo...", '{{ foo | truncate: 5 }}', "foo" => TestThing.new) assert_template_result "wo...", '{{ foo | truncate: 5 }}', "foo" => TestThing.new
end end
def test_date def test_date
assert_equal('May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")) assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
assert_equal('June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")) assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
assert_equal('July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")) assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")
assert_equal('May', @filters.date("2006-05-05 10:00:00", "%B")) assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B")
assert_equal('June', @filters.date("2006-06-05 10:00:00", "%B")) assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B")
assert_equal('July', @filters.date("2006-07-05 10:00:00", "%B")) assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B")
assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")) assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")) assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")) assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal('2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)) assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)
assert_equal('07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")) assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
assert_equal("07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")) assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
assert_equal(Date.today.year.to_s, @filters.date('now', '%Y')) assert_equal Date.today.year.to_s, @filters.date('now', '%Y')
assert_equal(Date.today.year.to_s, @filters.date('today', '%Y')) assert_equal Date.today.year.to_s, @filters.date('today', '%Y')
assert_equal(Date.today.year.to_s, @filters.date('Today', '%Y')) assert_equal Date.today.year.to_s, @filters.date('Today', '%Y')
assert_nil(@filters.date(nil, "%B")) assert_nil @filters.date(nil, "%B")
assert_equal('', @filters.date('', "%B")) assert_equal '', @filters.date('', "%B")
with_timezone("UTC") do with_timezone("UTC") do
assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y") assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y")
@@ -491,169 +487,169 @@ class StandardFiltersTest < Minitest::Test
end end
def test_first_last def test_first_last
assert_equal(1, @filters.first([1, 2, 3])) assert_equal 1, @filters.first([1, 2, 3])
assert_equal(3, @filters.last([1, 2, 3])) assert_equal 3, @filters.last([1, 2, 3])
assert_nil(@filters.first([])) assert_nil @filters.first([])
assert_nil(@filters.last([])) assert_nil @filters.last([])
end end
def test_replace def test_replace
assert_equal('2 2 2 2', @filters.replace('1 1 1 1', '1', 2)) assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2)
assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2)) assert_equal '2 2 2 2', @filters.replace('1 1 1 1', 1, 2)
assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)) assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)
assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)) assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)
assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}") assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}"
end end
def test_remove def test_remove
assert_equal(' ', @filters.remove("a a a a", 'a')) assert_equal ' ', @filters.remove("a a a a", 'a')
assert_equal(' ', @filters.remove("1 1 1 1", 1)) assert_equal ' ', @filters.remove("1 1 1 1", 1)
assert_equal('a a a', @filters.remove_first("a a a a", 'a ')) assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1)) assert_equal ' 1 1 1', @filters.remove_first("1 1 1 1", 1)
assert_template_result('a a a', "{{ 'a a a a' | remove_first: 'a ' }}") assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}"
end end
def test_pipes_in_string_arguments def test_pipes_in_string_arguments
assert_template_result('foobar', "{{ 'foo|bar' | remove: '|' }}") assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}"
end end
def test_strip def test_strip
assert_template_result('ab c', "{{ source | strip }}", 'source' => " ab c ") assert_template_result 'ab c', "{{ source | strip }}", 'source' => " ab c "
assert_template_result('ab c', "{{ source | strip }}", 'source' => " \tab c \n \t") assert_template_result 'ab c', "{{ source | strip }}", 'source' => " \tab c \n \t"
end end
def test_lstrip def test_lstrip
assert_template_result('ab c ', "{{ source | lstrip }}", 'source' => " ab c ") assert_template_result 'ab c ', "{{ source | lstrip }}", 'source' => " ab c "
assert_template_result("ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t") assert_template_result "ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t"
end end
def test_rstrip def test_rstrip
assert_template_result(" ab c", "{{ source | rstrip }}", 'source' => " ab c ") assert_template_result " ab c", "{{ source | rstrip }}", 'source' => " ab c "
assert_template_result(" \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t") assert_template_result " \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t"
end end
def test_strip_newlines def test_strip_newlines
assert_template_result('abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc") assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc"
assert_template_result('abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc") assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc"
end end
def test_newlines_to_br def test_newlines_to_br
assert_template_result("a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc") assert_template_result "a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
end end
def test_plus def test_plus
assert_template_result("2", "{{ 1 | plus:1 }}") assert_template_result "2", "{{ 1 | plus:1 }}"
assert_template_result("2.0", "{{ '1' | plus:'1.0' }}") assert_template_result "2.0", "{{ '1' | plus:'1.0' }}"
assert_template_result("5", "{{ price | plus:'2' }}", 'price' => NumberLikeThing.new(3)) assert_template_result "5", "{{ price | plus:'2' }}", 'price' => NumberLikeThing.new(3)
end end
def test_minus def test_minus
assert_template_result("4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1) assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
assert_template_result("2.3", "{{ '4.3' | minus:'2' }}") assert_template_result "2.3", "{{ '4.3' | minus:'2' }}"
assert_template_result("5", "{{ price | minus:'2' }}", 'price' => NumberLikeThing.new(7)) assert_template_result "5", "{{ price | minus:'2' }}", 'price' => NumberLikeThing.new(7)
end end
def test_abs def test_abs
assert_template_result("17", "{{ 17 | abs }}") assert_template_result "17", "{{ 17 | abs }}"
assert_template_result("17", "{{ -17 | abs }}") assert_template_result "17", "{{ -17 | abs }}"
assert_template_result("17", "{{ '17' | abs }}") assert_template_result "17", "{{ '17' | abs }}"
assert_template_result("17", "{{ '-17' | abs }}") assert_template_result "17", "{{ '-17' | abs }}"
assert_template_result("0", "{{ 0 | abs }}") assert_template_result "0", "{{ 0 | abs }}"
assert_template_result("0", "{{ '0' | abs }}") assert_template_result "0", "{{ '0' | abs }}"
assert_template_result("17.42", "{{ 17.42 | abs }}") assert_template_result "17.42", "{{ 17.42 | abs }}"
assert_template_result("17.42", "{{ -17.42 | abs }}") assert_template_result "17.42", "{{ -17.42 | abs }}"
assert_template_result("17.42", "{{ '17.42' | abs }}") assert_template_result "17.42", "{{ '17.42' | abs }}"
assert_template_result("17.42", "{{ '-17.42' | abs }}") assert_template_result "17.42", "{{ '-17.42' | abs }}"
end end
def test_times def test_times
assert_template_result("12", "{{ 3 | times:4 }}") assert_template_result "12", "{{ 3 | times:4 }}"
assert_template_result("0", "{{ 'foo' | times:4 }}") assert_template_result "0", "{{ 'foo' | times:4 }}"
assert_template_result("6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}") assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}"
assert_template_result("7.25", "{{ 0.0725 | times:100 }}") assert_template_result "7.25", "{{ 0.0725 | times:100 }}"
assert_template_result("-7.25", '{{ "-0.0725" | times:100 }}') assert_template_result "-7.25", '{{ "-0.0725" | times:100 }}'
assert_template_result("7.25", '{{ "-0.0725" | times: -100 }}') assert_template_result "7.25", '{{ "-0.0725" | times: -100 }}'
assert_template_result("4", "{{ price | times:2 }}", 'price' => NumberLikeThing.new(2)) assert_template_result "4", "{{ price | times:2 }}", 'price' => NumberLikeThing.new(2)
end end
def test_divided_by def test_divided_by
assert_template_result("4", "{{ 12 | divided_by:3 }}") assert_template_result "4", "{{ 12 | divided_by:3 }}"
assert_template_result("4", "{{ 14 | divided_by:3 }}") assert_template_result "4", "{{ 14 | divided_by:3 }}"
assert_template_result("5", "{{ 15 | divided_by:3 }}") assert_template_result "5", "{{ 15 | divided_by:3 }}"
assert_equal("Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render) assert_equal "Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render
assert_template_result("0.5", "{{ 2.0 | divided_by:4 }}") assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}"
assert_raises(Liquid::ZeroDivisionError) do assert_raises(Liquid::ZeroDivisionError) do
assert_template_result "4", "{{ 1 | modulo: 0 }}" assert_template_result "4", "{{ 1 | modulo: 0 }}"
end end
assert_template_result("5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10)) assert_template_result "5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10)
end end
def test_modulo def test_modulo
assert_template_result("1", "{{ 3 | modulo:2 }}") assert_template_result "1", "{{ 3 | modulo:2 }}"
assert_raises(Liquid::ZeroDivisionError) do assert_raises(Liquid::ZeroDivisionError) do
assert_template_result "4", "{{ 1 | modulo: 0 }}" assert_template_result "4", "{{ 1 | modulo: 0 }}"
end end
assert_template_result("1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3)) assert_template_result "1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3)
end end
def test_round def test_round
assert_template_result("5", "{{ input | round }}", 'input' => 4.6) assert_template_result "5", "{{ input | round }}", 'input' => 4.6
assert_template_result("4", "{{ '4.3' | round }}") assert_template_result "4", "{{ '4.3' | round }}"
assert_template_result("4.56", "{{ input | round: 2 }}", 'input' => 4.5612) assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612
assert_raises(Liquid::FloatDomainError) do assert_raises(Liquid::FloatDomainError) do
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | round }}" assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | round }}"
end end
assert_template_result("5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6)) assert_template_result "5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6)
assert_template_result("4", "{{ price | round }}", 'price' => NumberLikeThing.new(4.3)) assert_template_result "4", "{{ price | round }}", 'price' => NumberLikeThing.new(4.3)
end end
def test_ceil def test_ceil
assert_template_result("5", "{{ input | ceil }}", 'input' => 4.6) assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6
assert_template_result("5", "{{ '4.3' | ceil }}") assert_template_result "5", "{{ '4.3' | ceil }}"
assert_raises(Liquid::FloatDomainError) do assert_raises(Liquid::FloatDomainError) do
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | ceil }}" assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | ceil }}"
end end
assert_template_result("5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6)) assert_template_result "5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6)
end end
def test_floor def test_floor
assert_template_result("4", "{{ input | floor }}", 'input' => 4.6) assert_template_result "4", "{{ input | floor }}", 'input' => 4.6
assert_template_result("4", "{{ '4.3' | floor }}") assert_template_result "4", "{{ '4.3' | floor }}"
assert_raises(Liquid::FloatDomainError) do assert_raises(Liquid::FloatDomainError) do
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | floor }}" assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | floor }}"
end end
assert_template_result("5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)) assert_template_result "5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)
end end
def test_at_most def test_at_most
assert_template_result("4", "{{ 5 | at_most:4 }}") assert_template_result "4", "{{ 5 | at_most:4 }}"
assert_template_result("5", "{{ 5 | at_most:5 }}") assert_template_result "5", "{{ 5 | at_most:5 }}"
assert_template_result("5", "{{ 5 | at_most:6 }}") assert_template_result "5", "{{ 5 | at_most:6 }}"
assert_template_result("4.5", "{{ 4.5 | at_most:5 }}") assert_template_result "4.5", "{{ 4.5 | at_most:5 }}"
assert_template_result("5", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(6)) assert_template_result "5", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(6)
assert_template_result("4", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(4)) assert_template_result "4", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(4)
assert_template_result("4", "{{ 5 | at_most: width }}", 'width' => NumberLikeThing.new(4)) assert_template_result "4", "{{ 5 | at_most: width }}", 'width' => NumberLikeThing.new(4)
end end
def test_at_least def test_at_least
assert_template_result("5", "{{ 5 | at_least:4 }}") assert_template_result "5", "{{ 5 | at_least:4 }}"
assert_template_result("5", "{{ 5 | at_least:5 }}") assert_template_result "5", "{{ 5 | at_least:5 }}"
assert_template_result("6", "{{ 5 | at_least:6 }}") assert_template_result "6", "{{ 5 | at_least:6 }}"
assert_template_result("5", "{{ 4.5 | at_least:5 }}") assert_template_result "5", "{{ 4.5 | at_least:5 }}"
assert_template_result("6", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(6)) assert_template_result "6", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(6)
assert_template_result("5", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(4)) assert_template_result "5", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(4)
assert_template_result("6", "{{ 5 | at_least: width }}", 'width' => NumberLikeThing.new(6)) assert_template_result "6", "{{ 5 | at_least: width }}", 'width' => NumberLikeThing.new(6)
end end
def test_append def test_append
@@ -663,9 +659,9 @@ class StandardFiltersTest < Minitest::Test
end end
def test_concat def test_concat
assert_equal([1, 2, 3, 4], @filters.concat([1, 2], [3, 4])) assert_equal [1, 2, 3, 4], @filters.concat([1, 2], [3, 4])
assert_equal([1, 2, 'a'], @filters.concat([1, 2], ['a'])) assert_equal [1, 2, 'a'], @filters.concat([1, 2], ['a'])
assert_equal([1, 2, 10], @filters.concat([1, 2], [10])) assert_equal [1, 2, 10], @filters.concat([1, 2], [10])
assert_raises(Liquid::ArgumentError, "concat filter requires an array argument") do assert_raises(Liquid::ArgumentError, "concat filter requires an array argument") do
@filters.concat([1, 2], 10) @filters.concat([1, 2], 10)
@@ -679,23 +675,12 @@ class StandardFiltersTest < Minitest::Test
end end
def test_default def test_default
assert_equal("foo", @filters.default("foo", "bar")) assert_equal "foo", @filters.default("foo", "bar")
assert_equal("bar", @filters.default(nil, "bar")) assert_equal "bar", @filters.default(nil, "bar")
assert_equal("bar", @filters.default("", "bar")) assert_equal "bar", @filters.default("", "bar")
assert_equal("bar", @filters.default(false, "bar")) assert_equal "bar", @filters.default(false, "bar")
assert_equal("bar", @filters.default([], "bar")) assert_equal "bar", @filters.default([], "bar")
assert_equal("bar", @filters.default({}, "bar")) assert_equal "bar", @filters.default({}, "bar")
assert_template_result('bar', "{{ false | default: 'bar' }}")
end
def test_default_handle_false
assert_equal("foo", @filters.default("foo", "bar", "allow_false" => true))
assert_equal("bar", @filters.default(nil, "bar", "allow_false" => true))
assert_equal("bar", @filters.default("", "bar", "allow_false" => true))
assert_equal(false, @filters.default(false, "bar", "allow_false" => true))
assert_equal("bar", @filters.default([], "bar", "allow_false" => true))
assert_equal("bar", @filters.default({}, "bar", "allow_false" => true))
assert_template_result('false', "{{ false | default: 'bar', allow_false: true }}")
end end
def test_cannot_access_private_methods def test_cannot_access_private_methods
@@ -720,8 +705,8 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "delta", "ok" => true }, { "handle" => "delta", "ok" => true },
] ]
assert_equal(expectation, @filters.where(input, "ok", true)) assert_equal expectation, @filters.where(input, "ok", true)
assert_equal(expectation, @filters.where(input, "ok")) assert_equal expectation, @filters.where(input, "ok")
end end
def test_where_no_key_set def test_where_no_key_set
@@ -737,13 +722,13 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "delta", "ok" => true }, { "handle" => "delta", "ok" => true },
] ]
assert_equal(expectation, @filters.where(input, "ok", true)) assert_equal expectation, @filters.where(input, "ok", true)
assert_equal(expectation, @filters.where(input, "ok")) assert_equal expectation, @filters.where(input, "ok")
end end
def test_where_non_array_map_input def test_where_non_array_map_input
assert_equal([{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok")) assert_equal [{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok")
assert_equal([], @filters.where({ "a" => "not ok" }, "a", "ok")) assert_equal [], @filters.where({ "a" => "not ok" }, "a", "ok")
end end
def test_where_indexable_but_non_map_value def test_where_indexable_but_non_map_value
@@ -758,14 +743,14 @@ class StandardFiltersTest < Minitest::Test
{ "message" => "Hallo!", "language" => "German" }, { "message" => "Hallo!", "language" => "German" },
] ]
assert_equal([{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")) assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")
assert_equal([{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German")) assert_equal [{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German")
assert_equal([{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English")) assert_equal [{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English")
end end
def test_where_array_of_only_unindexable_values def test_where_array_of_only_unindexable_values
assert_nil(@filters.where([nil], "ok", true)) assert_nil @filters.where([nil], "ok", true)
assert_nil(@filters.where([nil], "ok")) assert_nil @filters.where([nil], "ok")
end end
def test_where_no_target_value def test_where_no_target_value
@@ -776,7 +761,7 @@ class StandardFiltersTest < Minitest::Test
{ "bar" => true }, { "bar" => true },
] ]
assert_equal([{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")) assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")
end end
private private

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class BreakTagTest < Minitest::Test class BreakTagTest < Minitest::Test

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class ContinueTagTest < Minitest::Test class ContinueTagTest < Minitest::Test

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class EchoTest < Minitest::Test class EchoTest < Minitest::Test

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class ThingWithValue < Liquid::Drop class ThingWithValue < Liquid::Drop
@@ -350,7 +348,7 @@ HERE
end end
def test_inner_for_over_empty_input def test_inner_for_over_empty_input
assert_template_result('oo', '{% for a in (1..2) %}o{% for b in empty %}{% endfor %}{% endfor %}') assert_template_result 'oo', '{% for a in (1..2) %}o{% for b in empty %}{% endfor %}{% endfor %}'
end end
def test_blank_string_not_iterable def test_blank_string_not_iterable
@@ -394,8 +392,8 @@ HERE
expected = '12345' expected = '12345'
template = '{% for item in items %}{{item}}{% endfor %}' template = '{% for item in items %}{{item}}{% endfor %}'
assert_template_result(expected, template, assigns) assert_template_result(expected, template, assigns)
assert(loader.each_called) assert loader.each_called
assert(!loader.load_slice_called) assert !loader.load_slice_called
end end
def test_iterate_with_load_slice_when_limit_applied def test_iterate_with_load_slice_when_limit_applied
@@ -404,8 +402,8 @@ HERE
expected = '1' expected = '1'
template = '{% for item in items limit:1 %}{{item}}{% endfor %}' template = '{% for item in items limit:1 %}{{item}}{% endfor %}'
assert_template_result(expected, template, assigns) assert_template_result(expected, template, assigns)
assert(!loader.each_called) assert !loader.each_called
assert(loader.load_slice_called) assert loader.load_slice_called
end end
def test_iterate_with_load_slice_when_limit_and_offset_applied def test_iterate_with_load_slice_when_limit_and_offset_applied
@@ -414,8 +412,8 @@ HERE
expected = '34' expected = '34'
template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}' template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}'
assert_template_result(expected, template, assigns) assert_template_result(expected, template, assigns)
assert(!loader.each_called) assert !loader.each_called
assert(loader.load_slice_called) assert loader.load_slice_called
end end
def test_iterate_with_load_slice_returns_same_results_as_without def test_iterate_with_load_slice_returns_same_results_as_without
@@ -435,6 +433,6 @@ HERE
Liquid::Template.parse('{% for i in (1..2) %}{{ standard_error }}{% endfor %}').render!(context) Liquid::Template.parse('{% for i in (1..2) %}{{ standard_error }}{% endfor %}').render!(context)
end end
assert(context.registers[:for_stack].empty?) assert context.registers[:for_stack].empty?
end end
end end

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class IfElseTagTest < Minitest::Test class IfElseTagTest < Minitest::Test

View File

@@ -1,5 +1,3 @@
# frozen_string_literal: true
require 'test_helper' require 'test_helper'
class TestFileSystem class TestFileSystem
@@ -8,9 +6,6 @@ class TestFileSystem
when "product" when "product"
"Product: {{ product.title }} " "Product: {{ product.title }} "
when "product_alias"
"Product: {{ product.title }} "
when "locale_variables" when "locale_variables"
"Locale: {{echo1}} {{echo2}}" "Locale: {{echo1}} {{echo2}}"
@@ -85,66 +80,56 @@ class IncludeTagTest < Minitest::Test
end end
def test_include_tag_looks_for_file_system_in_registers_first def test_include_tag_looks_for_file_system_in_registers_first
assert_equal('from OtherFileSystem', assert_equal 'from OtherFileSystem',
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: OtherFileSystem.new })) Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: OtherFileSystem.new })
end end
def test_include_tag_with def test_include_tag_with
assert_template_result("Product: Draft 151cm ", assert_template_result "Product: Draft 151cm ",
"{% include 'product' with products[0] %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }]) "{% include 'product' with products[0] %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }]
end
def test_include_tag_with_alias
assert_template_result("Product: Draft 151cm ",
"{% include 'product_alias' with products[0] as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
end
def test_include_tag_for_alias
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
"{% include 'product_alias' for products as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
end end
def test_include_tag_with_default_name def test_include_tag_with_default_name
assert_template_result("Product: Draft 151cm ", assert_template_result "Product: Draft 151cm ",
"{% include 'product' %}", "product" => { 'title' => 'Draft 151cm' }) "{% include 'product' %}", "product" => { 'title' => 'Draft 151cm' }
end end
def test_include_tag_for def test_include_tag_for
assert_template_result("Product: Draft 151cm Product: Element 155cm ", assert_template_result "Product: Draft 151cm Product: Element 155cm ",
"{% include 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }]) "{% include 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }]
end end
def test_include_tag_with_local_variables def test_include_tag_with_local_variables
assert_template_result("Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}") assert_template_result "Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}"
end end
def test_include_tag_with_multiple_local_variables def test_include_tag_with_multiple_local_variables
assert_template_result("Locale: test123 test321", assert_template_result "Locale: test123 test321",
"{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}") "{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}"
end end
def test_include_tag_with_multiple_local_variables_from_context def test_include_tag_with_multiple_local_variables_from_context
assert_template_result("Locale: test123 test321", assert_template_result "Locale: test123 test321",
"{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}", "{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}",
'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' }) 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' }
end end
def test_included_templates_assigns_variables def test_included_templates_assigns_variables
assert_template_result("bar", "{% include 'assignments' %}{{ foo }}") assert_template_result "bar", "{% include 'assignments' %}{{ foo }}"
end end
def test_nested_include_tag def test_nested_include_tag
assert_template_result("body body_detail", "{% include 'body' %}") assert_template_result "body body_detail", "{% include 'body' %}"
assert_template_result("header body body_detail footer", "{% include 'nested_template' %}") assert_template_result "header body body_detail footer", "{% include 'nested_template' %}"
end end
def test_nested_include_with_variable def test_nested_include_with_variable
assert_template_result("Product: Draft 151cm details ", assert_template_result "Product: Draft 151cm details ",
"{% include 'nested_product_template' with product %}", "product" => { "title" => 'Draft 151cm' }) "{% include 'nested_product_template' with product %}", "product" => { "title" => 'Draft 151cm' }
assert_template_result("Product: Draft 151cm details Product: Element 155cm details ", assert_template_result "Product: Draft 151cm details Product: Element 155cm details ",
"{% include 'nested_product_template' for products %}", "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }]) "{% include 'nested_product_template' for products %}", "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }]
end end
def test_recursively_included_template_does_not_produce_endless_loop def test_recursively_included_template_does_not_produce_endless_loop
@@ -162,41 +147,41 @@ class IncludeTagTest < Minitest::Test
end end
def test_dynamically_choosen_template def test_dynamically_choosen_template
assert_template_result("Test123", "{% include template %}", "template" => 'Test123') assert_template_result "Test123", "{% include template %}", "template" => 'Test123'
assert_template_result("Test321", "{% include template %}", "template" => 'Test321') assert_template_result "Test321", "{% include template %}", "template" => 'Test321'
assert_template_result("Product: Draft 151cm ", "{% include template for product %}", assert_template_result "Product: Draft 151cm ", "{% include template for product %}",
"template" => 'product', 'product' => { 'title' => 'Draft 151cm' }) "template" => 'product', 'product' => { 'title' => 'Draft 151cm' }
end end
def test_include_tag_caches_second_read_of_same_partial def test_include_tag_caches_second_read_of_same_partial
file_system = CountingFileSystem.new file_system = CountingFileSystem.new
assert_equal('from CountingFileSystemfrom CountingFileSystem', assert_equal 'from CountingFileSystemfrom CountingFileSystem',
Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system })) Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system })
assert_equal(1, file_system.count) assert_equal 1, file_system.count
end end
def test_include_tag_doesnt_cache_partials_across_renders def test_include_tag_doesnt_cache_partials_across_renders
file_system = CountingFileSystem.new file_system = CountingFileSystem.new
assert_equal('from CountingFileSystem', assert_equal 'from CountingFileSystem',
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system })) Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system })
assert_equal(1, file_system.count) assert_equal 1, file_system.count
assert_equal('from CountingFileSystem', assert_equal 'from CountingFileSystem',
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system })) Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system })
assert_equal(2, file_system.count) assert_equal 2, file_system.count
end end
def test_include_tag_within_if_statement def test_include_tag_within_if_statement
assert_template_result("foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}") assert_template_result "foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}"
end end
def test_custom_include_tag def test_custom_include_tag
original_tag = Liquid::Template.tags['include'] original_tag = Liquid::Template.tags['include']
Liquid::Template.tags['include'] = CustomInclude Liquid::Template.tags['include'] = CustomInclude
begin begin
assert_equal("custom_foo", assert_equal "custom_foo",
Template.parse("{% include 'custom_foo' %}").render!) Template.parse("{% include 'custom_foo' %}").render!
ensure ensure
Liquid::Template.tags['include'] = original_tag Liquid::Template.tags['include'] = original_tag
end end
@@ -206,8 +191,8 @@ class IncludeTagTest < Minitest::Test
original_tag = Liquid::Template.tags['include'] original_tag = Liquid::Template.tags['include']
Liquid::Template.tags['include'] = CustomInclude Liquid::Template.tags['include'] = CustomInclude
begin begin
assert_equal("custom_foo_if_true", assert_equal "custom_foo_if_true",
Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render!) Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render!
ensure ensure
Liquid::Template.tags['include'] = original_tag Liquid::Template.tags['include'] = original_tag
end end
@@ -218,7 +203,7 @@ class IncludeTagTest < Minitest::Test
a = Liquid::Template.parse(' {% include "nested_template" %}') a = Liquid::Template.parse(' {% include "nested_template" %}')
a.render! a.render!
assert_empty(a.errors) assert_empty a.errors
end end
def test_passing_options_to_included_templates def test_passing_options_to_included_templates
@@ -248,22 +233,22 @@ class IncludeTagTest < Minitest::Test
end end
def test_including_via_variable_value def test_including_via_variable_value
assert_template_result("from TestFileSystem", "{% assign page = 'pick_a_source' %}{% include page %}") assert_template_result "from TestFileSystem", "{% assign page = 'pick_a_source' %}{% include page %}"
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page %}", "product" => { 'title' => 'Draft 151cm' }) assert_template_result "Product: Draft 151cm ", "{% assign page = 'product' %}{% include page %}", "product" => { 'title' => 'Draft 151cm' }
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}", "foo" => { 'title' => 'Draft 151cm' }) assert_template_result "Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}", "foo" => { 'title' => 'Draft 151cm' }
end end
def test_including_with_strict_variables def test_including_with_strict_variables
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn) template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
template.render(nil, strict_variables: true) template.render(nil, strict_variables: true)
assert_equal([], template.errors) assert_equal [], template.errors
end end
def test_break_through_include def test_break_through_include
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}") assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}"
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}") assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}"
end end
end # IncludeTagTest end # IncludeTagTest

Some files were not shown because too many files have changed in this diff Show More