Upgrade rubocop

This commit is contained in:
Mike Angell
2019-08-27 17:46:09 +10:00
parent 831355dfbd
commit da9051eb18
121 changed files with 2736 additions and 1872 deletions
+22 -20
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
# Copyright (c) 2005 Tobias Luetke
#
# Permission is hereby granted, free of charge, to any person obtaining
@@ -20,31 +22,31 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
module Liquid
FilterSeparator = /\|/
ArgumentSeparator = ','.freeze
FilterArgumentSeparator = ':'.freeze
VariableAttributeSeparator = '.'.freeze
WhitespaceControl = '-'.freeze
TagStart = /\{\%/
TagEnd = /\%\}/
VariableSignature = /\(?[\w\-\.\[\]]\)?/
VariableSegment = /[\w\-]/
VariableStart = /\{\{/
VariableEnd = /\}\}/
VariableIncompleteEnd = /\}\}?/
QuotedString = /"[^"]*"|'[^']*'/
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
FILTER_SEPARATOR = /\|/.freeze
ARGUMENT_SEPARATOR = ','
FILTER_ARGUMENT_SEPARATOR = ':'
VARIABLE_ATTRIBUTE_SEPARATOR = '.'
WHITESPACE_CONTROL = '-'
TAG_START = /\{\%/.freeze
TAG_END = /\%\}/.freeze
VARIABLE_SIGNATURE = /\(?[\w\-\.\[\]]\)?/.freeze
VARIABLE_SEGMENT = /[\w\-]/.freeze
VARIABLE_START = /\{\{/.freeze
VARIABLE_END = /\}\}/.freeze
VARIABLE_INCOMPLETE_END = /\}\}?/.freeze
QUOTED_STRING = /"[^"]*"|'[^']*'/.freeze
QUOTED_FRAGMENT = /#{QUOTED_STRING}|(?:[^\s,\|'"]|#{QUOTED_STRING})+/o.freeze
TAG_ATTRIBUTES = /(\w+)\s*\:\s*(#{QUOTED_FRAGMENT})/o.freeze
ANY_STARTING_TAG = /#{TAG_START}|#{VARIABLE_START}/o.freeze
PARTIAL_TEMPLATE_PARSER = /#{TAG_START}.*?#{TAG_END}|#{VARIABLE_START}.*?#{VARIABLE_INCOMPLETE_END}/om.freeze
TEMPLATE_PARSER = /(#{PARTIAL_TEMPLATE_PARSER}|#{ANY_STARTING_TAG})/om.freeze
VARIABLE_PARSER = /\[[^\]]+\]|#{VARIABLE_SEGMENT}+\??/o.freeze
singleton_class.send(:attr_accessor, :cache_classes)
self.cache_classes = true
end
require "liquid/version"
require 'liquid/version'
require 'liquid/parse_tree_visitor'
require 'liquid/lexer'
require 'liquid/parser'
+14 -15
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Block < Tag
MAX_DEPTH = 100
@@ -27,16 +29,16 @@ module Liquid
end
def unknown_tag(tag, _params, _tokens)
if tag == 'else'.freeze
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_else".freeze,
block_name: block_name))
elsif tag.start_with?('end'.freeze)
raise SyntaxError.new(parse_context.locale.t("errors.syntax.invalid_delimiter".freeze,
tag: tag,
block_name: block_name,
block_delimiter: block_delimiter))
if tag == 'else'
raise SyntaxError, parse_context.locale.t('errors.syntax.unexpected_else',
block_name: block_name)
elsif tag.start_with?('end')
raise SyntaxError, parse_context.locale.t('errors.syntax.invalid_delimiter',
tag: tag,
block_name: block_name,
block_delimiter: block_delimiter)
else
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
raise SyntaxError, parse_context.locale.t('errors.syntax.unknown_tag', tag: tag)
end
end
@@ -51,18 +53,15 @@ module Liquid
protected
def parse_body(body, tokens)
if parse_context.depth >= MAX_DEPTH
raise StackLevelError, "Nesting too deep".freeze
end
raise StackLevelError, 'Nesting too deep' if parse_context.depth >= MAX_DEPTH
parse_context.depth += 1
begin
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
@blank &&= body.blank?
return false if end_tag_name == block_delimiter
unless end_tag_name
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
end
raise SyntaxError, parse_context.locale.t('errors.syntax.tag_never_closed', block_name: block_name) unless end_tag_name
# this tag is not registered with the system
# pass it to the current block for special handling or error reporting
+29 -30
View File
@@ -1,10 +1,12 @@
# frozen_string_literal: true
module Liquid
class BlockBody
FullToken = /\A#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
WhitespaceOrNothing = /\A\s*\z/
TAGSTART = "{%".freeze
VARSTART = "{{".freeze
FULL_TOKEN = /\A#{TAG_START}#{WHITESPACE_CONTROL}?\s*(\w+)\s*(.*?)#{WHITESPACE_CONTROL}?#{TAG_END}\z/om.freeze
CONTENT_OF_VARIABLE = /\A#{VARIABLE_START}#{WHITESPACE_CONTROL}?(.*?)#{WHITESPACE_CONTROL}?#{VARIABLE_END}\z/om.freeze
WHITESPACE_OR_NOTHING = /\A\s*\z/.freeze
TAG_START = '{%'
VAR_START = '{{'
attr_reader :nodelist
@@ -15,36 +17,34 @@ module Liquid
def parse(tokenizer, parse_context)
parse_context.line_number = tokenizer.line_number
while token = tokenizer.shift
while (token = tokenizer.shift)
next if token.empty?
case
when token.start_with?(TAGSTART)
when token.start_with?(TAG_START)
whitespace_handler(token, parse_context)
unless token =~ FullToken
raise_missing_tag_terminator(token, parse_context)
end
tag_name = $1
markup = $2
raise_missing_tag_terminator(token, parse_context) unless token =~ FULL_TOKEN
tag_name = Regexp.last_match(1)
markup = Regexp.last_match(2)
# fetch the tag from registered blocks
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
# determine how to proceed
return yield tag_name, markup
end
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
@blank &&= new_tag.blank?
@nodelist << new_tag
when token.start_with?(VARSTART)
when token.start_with?(VAR_START)
whitespace_handler(token, parse_context)
@nodelist << create_variable(token, parse_context)
@blank = false
else
if parse_context.trim_whitespace
token.lstrip!
end
token.lstrip! if parse_context.trim_whitespace
parse_context.trim_whitespace = false
@nodelist << token
@blank &&= !!(token =~ WhitespaceOrNothing)
@blank &&= !!(token =~ WHITESPACE_OR_NOTHING)
end
parse_context.line_number = tokenizer.line_number
end
@@ -53,13 +53,11 @@ module Liquid
end
def whitespace_handler(token, parse_context)
if token[2] == WhitespaceControl
if token[2] == WHITESPACE_CONTROL
previous_token = @nodelist.last
if previous_token.is_a? String
previous_token.rstrip!
end
previous_token.rstrip! if previous_token.is_a?(String)
end
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
parse_context.trim_whitespace = (token[-3] == WHITESPACE_CONTROL)
end
def blank?
@@ -67,14 +65,14 @@ module Liquid
end
def render(context)
render_to_output_buffer(context, '')
render_to_output_buffer(context, ''.dup)
end
def render_to_output_buffer(context, output)
context.resource_limits.render_score += @nodelist.length
idx = 0
while node = @nodelist[idx]
while (node = @nodelist[idx])
previous_output_size = output.bytesize
case node
@@ -83,7 +81,7 @@ module Liquid
when Variable
render_node(context, output, node)
when Block
render_node(context, node.blank? ? '' : output, node)
render_node(context, node.blank? ? ''.dup : output, node)
break if context.interrupt? # might have happened in a for-block
when Continue, Break
# If we get an Interrupt that means the block must stop processing. An
@@ -117,11 +115,12 @@ module Liquid
def raise_if_resource_limits_reached(context, length)
context.resource_limits.render_length += length
return unless context.resource_limits.reached?
raise MemoryError.new("Memory limits exceeded".freeze)
raise MemoryError, 'Memory limits exceeded'
end
def create_variable(token, parse_context)
token.scan(ContentOfVariable) do |content|
token.scan(CONTENT_OF_VARIABLE) do |content|
markup = content.first
return Variable.new(markup, parse_context)
end
@@ -129,11 +128,11 @@ module Liquid
end
def raise_missing_tag_terminator(token, parse_context)
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_termination".freeze, token: token, tag_end: TagEnd.inspect))
raise SyntaxError, parse_context.locale.t('errors.syntax.tag_termination', token: token, tag_end: TAG_END.inspect)
end
def raise_missing_variable_terminator(token, parse_context)
raise SyntaxError.new(parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect))
raise SyntaxError, parse_context.locale.t('errors.syntax.variable_termination', token: token, tag_end: VARIABLE_END.inspect)
end
def registered_tags
+18 -16
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Container for liquid nodes which conveniently wraps decision making logic
#
@@ -7,26 +9,26 @@ module Liquid
# c.evaluate #=> true
#
class Condition #:nodoc:
@@operators = {
'=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
'!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
'<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
'<'.freeze => :<,
'>'.freeze => :>,
'>='.freeze => :>=,
'<='.freeze => :<=,
'contains'.freeze => lambda do |cond, left, right|
@operators = {
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
'!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
'<>' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
'<' => :<,
'>' => :>,
'>=' => :>=,
'<=' => :<=,
'contains' => lambda do |_cond, left, right|
if left && right && left.respond_to?(:include?)
right = right.to_s if left.is_a?(String)
left.include?(right)
else
false
end
end
end,
}
def self.operators
@@operators
class << self
attr_accessor :operators
end
attr_reader :attachment, :child_condition
@@ -36,7 +38,7 @@ module Liquid
@left = left
@operator = operator
@right = right
@child_relation = nil
@child_relation = nil
@child_condition = nil
end
@@ -78,7 +80,7 @@ module Liquid
end
def inspect
"#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
"#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
end
protected
@@ -116,7 +118,7 @@ module Liquid
left = context.evaluate(left)
right = context.evaluate(right)
operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
operation = self.class.operators[op] || raise(Liquid::ArgumentError, "Unknown operator #{op}")
if operation.respond_to?(:call)
operation.call(self, left, right)
@@ -124,7 +126,7 @@ module Liquid
begin
left.send(operation, right)
rescue ::ArgumentError => e
raise Liquid::ArgumentError.new(e.message)
raise Liquid::ArgumentError, e.message
end
end
end
+9 -10
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Context keeps the variable stack and resolves variables, as well as keywords
#
@@ -28,9 +30,7 @@ module Liquid
@this_stack_used = false
self.exception_renderer = Template.default_exception_renderer
if rethrow_errors
self.exception_renderer = ->(e) { raise }
end
self.exception_renderer = ->(_e) { raise } if rethrow_errors
@interrupts = []
@filters = []
@@ -89,7 +89,7 @@ module Liquid
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
def push(new_scope = {})
@scopes.unshift(new_scope)
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH
raise StackLevelError, 'Nesting too deep' if @scopes.length > Block::MAX_DEPTH
end
# Merge a hash of variables in the current local scope
@@ -100,6 +100,7 @@ module Liquid
# Pop from the stack. use <tt>Context#stack</tt> instead
def pop
raise ContextError if @scopes.size == 1
@scopes.shift
end
@@ -190,14 +191,12 @@ module Liquid
end
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
raise Liquid::UndefinedVariable, "undefined variable #{key}"
end
raise Liquid::UndefinedVariable, "undefined variable #{key}" if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
value = obj[key]
if value.is_a?(Proc) && obj.respond_to?(:[]=)
obj[key] = (value.arity == 0) ? value.call : value.call(self)
obj[key] = value.arity == 0 ? value.call : value.call(self)
else
value
end
@@ -208,8 +207,8 @@ module Liquid
def internal_error
# raise and catch to set backtrace and cause on exception
raise Liquid::InternalError, 'internal'
rescue Liquid::InternalError => exc
exc
rescue Liquid::InternalError => e
e
end
def squash_instance_assigns_with_environments
+6 -4
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Document < BlockBody
def self.parse(tokens, parse_context)
@@ -7,7 +9,7 @@ module Liquid
end
def parse(tokens, parse_context)
super do |end_tag_name, end_tag_params|
super do |end_tag_name, _end_tag_params|
unknown_tag(end_tag_name, parse_context) if end_tag_name
end
rescue SyntaxError => e
@@ -17,10 +19,10 @@ module Liquid
def unknown_tag(tag, parse_context)
case tag
when 'else'.freeze, 'end'.freeze
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_outer_tag".freeze, tag: tag))
when 'else', 'end'
raise SyntaxError, parse_context.locale.t('errors.syntax.unexpected_outer_tag', tag: tag)
else
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
raise SyntaxError, parse_context.locale.t('errors.syntax.unknown_tag', tag: tag)
end
end
end
+5 -2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'set'
module Liquid
@@ -25,7 +27,8 @@ module Liquid
# Catch all for the method
def liquid_method_missing(method)
return nil unless @context && @context.strict_variables
return nil unless @context&.strict_variables
raise Liquid::UndefinedDropMethod, "undefined method #{method}"
end
@@ -67,7 +70,7 @@ module Liquid
if include?(Enumerable)
blacklist += Enumerable.public_instance_methods
blacklist -= [:sort, :count, :first, :min, :max, :include?]
blacklist -= %i[sort count first min max include?]
end
whitelist = [:to_liquid] + (public_instance_methods - blacklist)
+12 -10
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Error < ::StandardError
attr_accessor :line_number
@@ -5,12 +7,12 @@ module Liquid
attr_accessor :markup_context
def to_s(with_prefix = true)
str = ""
str = ''.dup
str << message_prefix if with_prefix
str << super()
if markup_context
str << " "
str << ' '
str << markup_context
end
@@ -20,20 +22,20 @@ module Liquid
private
def message_prefix
str = ""
if is_a?(SyntaxError)
str << "Liquid syntax error"
str = ''.dup
str << if is_a?(SyntaxError)
'Liquid syntax error'
else
str << "Liquid error"
'Liquid error'
end
if line_number
str << " ("
str << template_name << " " if template_name
str << "line " << line_number.to_s << ")"
str << ' ('
str << template_name << ' ' if template_name
str << 'line ' << line_number.to_s << ')'
end
str << ": "
str << ': '
str
end
end
+16 -14
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Expression
class MethodLiteral
@@ -14,18 +16,18 @@ module Liquid
end
LITERALS = {
nil => nil, 'nil'.freeze => nil, 'null'.freeze => nil, ''.freeze => nil,
'true'.freeze => true,
'false'.freeze => false,
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
'true' => true,
'false' => false,
'blank' => MethodLiteral.new(:blank?, '').freeze,
'empty' => MethodLiteral.new(:empty?, '').freeze
}.freeze
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m
DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m
INTEGERS_REGEX = /\A(-?\d+)\z/
FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/
RANGES_REGEX = /\A\((\S+)\.\.(\S+)\)\z/
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m.freeze
DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m.freeze
INTEGERS_REGEX = /\A(-?\d+)\z/.freeze
FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/.freeze
RANGES_REGEX = /\A\((\S+)\.\.(\S+)\)\z/.freeze
def self.parse(markup)
if LITERALS.key?(markup)
@@ -33,13 +35,13 @@ module Liquid
else
case markup
when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
$1
Regexp.last_match(1)
when INTEGERS_REGEX
$1.to_i
Regexp.last_match(1).to_i
when RANGES_REGEX
RangeLookup.parse($1, $2)
RangeLookup.parse(Regexp.last_match(1), Regexp.last_match(2))
when FLOATS_REGEX
$1.to_f
Regexp.last_match(1).to_f
else
VariableLookup.parse(markup)
end
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'time'
require 'date'
+6 -4
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# A Liquid file system is a way to let your templates retrieve other templates for use with the include tag.
#
@@ -15,7 +17,7 @@ module Liquid
class BlankFileSystem
# Called by Liquid to retrieve a template file
def read_template_file(_template_path)
raise FileSystemError, "This liquid context does not allow includes."
raise FileSystemError, 'This liquid context does not allow includes.'
end
end
@@ -44,7 +46,7 @@ module Liquid
class LocalFileSystem
attr_accessor :root
def initialize(root, pattern = "_%s.liquid".freeze)
def initialize(root, pattern = '_%s.liquid')
@root = root
@pattern = pattern
end
@@ -57,9 +59,9 @@ module Liquid
end
def full_path(template_path)
raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /\A[^.\/][a-zA-Z0-9_\/]+\z/
raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ %r{\A[^./][a-zA-Z0-9_/]+\z}
full_path = if template_path.include?('/'.freeze)
full_path = if template_path.include?('/')
File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
else
File.join(root, @pattern % template_path)
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class ForloopDrop < Drop
def initialize(name, length, parentloop)
+6 -4
View File
@@ -1,8 +1,10 @@
# frozen_string_literal: true
require 'yaml'
module Liquid
class I18n
DEFAULT_LOCALE = File.join(File.expand_path(__dir__), "locales", "en.yml")
DEFAULT_LOCALE = File.join(File.expand_path(__dir__), 'locales', 'en.yml')
TranslationError = Class.new(StandardError)
@@ -26,13 +28,13 @@ module Liquid
def interpolate(name, vars)
name.gsub(/%\{(\w+)\}/) do
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
(vars[$1.to_sym]).to_s
(vars[Regexp.last_match(1).to_sym]).to_s
end
end
def deep_fetch_translation(name)
name.split('.'.freeze).reduce(locale) do |level, cur|
level[cur] or raise TranslationError, "Translation for #{name} does not exist in locale #{path}"
name.split('.').reduce(locale) do |level, cur|
level[cur] || raise(TranslationError, "Translation for #{name} does not exist in locale #{path}")
end
end
end
+5 -3
View File
@@ -1,16 +1,18 @@
# frozen_string_literal: true
module Liquid
# An interrupt is any command that breaks processing of a block (ex: a for loop).
class Interrupt
attr_reader :message
def initialize(message = nil)
@message = message || "interrupt".freeze
@message = message || 'interrupt'
end
end
# Interrupt that is thrown whenever a {% break %} is called.
class BreakInterrupt < Interrupt; end
class BreakInterrupt < RuntimeError; end
# Interrupt that is thrown whenever a {% continue %} is called.
class ContinueInterrupt < Interrupt; end
class ContinueInterrupt < RuntimeError; end
end
+28 -26
View File
@@ -1,25 +1,27 @@
require "strscan"
# frozen_string_literal: true
require 'strscan'
module Liquid
class Lexer
SPECIALS = {
'|'.freeze => :pipe,
'.'.freeze => :dot,
':'.freeze => :colon,
','.freeze => :comma,
'['.freeze => :open_square,
']'.freeze => :close_square,
'('.freeze => :open_round,
')'.freeze => :close_round,
'?'.freeze => :question,
'-'.freeze => :dash
'|' => :pipe,
'.' => :dot,
':' => :colon,
',' => :comma,
'[' => :open_square,
']' => :close_square,
'(' => :open_round,
')' => :close_round,
'?' => :question,
'-' => :dash,
}.freeze
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
SINGLE_STRING_LITERAL = /'[^\']*'/
DOUBLE_STRING_LITERAL = /"[^\"]*"/
NUMBER_LITERAL = /-?\d+(\.\d+)?/
DOTDOT = /\.\./
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
WHITESPACE_OR_NOTHING = /\s*/
IDENTIFIER = /[a-zA-Z_][\w-]*\??/.freeze
SINGLE_STRING_LITERAL = /'[^\']*'/.freeze
DOUBLE_STRING_LITERAL = /"[^\"]*"/.freeze
NUMBER_LITERAL = /-?\d+(\.\d+)?/.freeze
DOTDOT = /\.\./.freeze
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/.freeze
WHITESPACE_OR_NOTHING = /\s*/.freeze
def initialize(input)
@ss = StringScanner.new(input)
@@ -31,16 +33,16 @@ module Liquid
until @ss.eos?
@ss.skip(WHITESPACE_OR_NOTHING)
break if @ss.eos?
tok = case
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]
when t = @ss.scan(DOUBLE_STRING_LITERAL) then [:string, t]
when t = @ss.scan(NUMBER_LITERAL) then [:number, t]
when t = @ss.scan(IDENTIFIER) then [:id, t]
when t = @ss.scan(DOTDOT) then [:dotdot, t]
tok = if (t = @ss.scan(COMPARISON_OPERATOR)) then [:comparison, t]
elsif (t = @ss.scan(SINGLE_STRING_LITERAL)) then [:string, t]
elsif (t = @ss.scan(DOUBLE_STRING_LITERAL)) then [:string, t]
elsif (t = @ss.scan(NUMBER_LITERAL)) then [:number, t]
elsif (t = @ss.scan(IDENTIFIER)) then [:id, t]
elsif (t = @ss.scan(DOTDOT)) then [:dotdot, t]
else
c = @ss.getch
if s = SPECIALS[c]
if (s = SPECIALS[c])
[s, c]
else
raise SyntaxError, "Unexpected character #{c}"
+3 -2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class ParseContext
attr_accessor :locale, :line_number, :trim_whitespace, :depth
@@ -19,7 +21,6 @@ module Liquid
@partial = value
@options = value ? partial_options : @template_options
@error_mode = @options[:error_mode] || Template.error_mode
value
end
def partial_options
@@ -28,7 +29,7 @@ module Liquid
if dont_pass == true
{ locale: locale }
elsif dont_pass.is_a?(Array)
@template_options.reject { |k, v| dont_pass.include?(k) }
@template_options.reject { |k, _v| dont_pass.include?(k) }
else
@template_options
end
+1 -1
View File
@@ -28,7 +28,7 @@ module Liquid
item, new_context = @callbacks[node.class].call(node, context)
[
item,
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context)
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context),
]
end
end
+11 -9
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Parser
def initialize(input)
@@ -12,9 +14,8 @@ module Liquid
def consume(type = nil)
token = @tokens[@p]
if type && token[0] != type
raise SyntaxError, "Expected #{type} but found #{@tokens[@p].first}"
end
raise SyntaxError, "Expected #{type} but found #{@tokens[@p].first}" if type && token[0] != type
@p += 1
token[1]
end
@@ -25,6 +26,7 @@ module Liquid
def consume?(type)
token = @tokens[@p]
return false unless token && token[0] == type
@p += 1
token[1]
end
@@ -34,6 +36,7 @@ module Liquid
token = @tokens[@p]
return false unless token && token[0] == :id
return false unless token[1] == str
@p += 1
token[1]
end
@@ -41,17 +44,18 @@ module Liquid
def look(type, ahead = 0)
tok = @tokens[@p + ahead]
return false unless tok
tok[0] == type
end
SINGLE_TOKEN_EXPRESSION_TYPES = [:string, :number].freeze
SINGLE_TOKEN_EXPRESSION_TYPES = %i[string number].freeze
private_constant :SINGLE_TOKEN_EXPRESSION_TYPES
def expression
token = @tokens[@p]
if token[0] == :id
variable_signature
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include? token[0]
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include?(token[0])
consume
elsif token.first == :open_round
consume
@@ -66,11 +70,9 @@ module Liquid
end
def argument
str = ""
str = ''.dup
# might be a keyword argument (identifier: expression)
if look(:id) && look(:colon, 1)
str << consume << consume << ' '.freeze
end
str << consume << consume << ' ' if look(:id) && look(:colon, 1)
str << expression
str
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
module ParserSwitching
def parse_with_selected_parser(markup)
+4 -2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'liquid/profiler/hooks'
module Liquid
@@ -97,9 +99,9 @@ module Liquid
end
def initialize
@partial_stack = ["<root>"]
@partial_stack = ['<root>']
@root_timing = Timing.new("", current_partial)
@root_timing = Timing.new('', current_partial)
@timing_stack = [@root_timing]
@render_start_at = Time.now
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class BlockBody
def render_node_with_profiling(context, output, node)
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class RangeLookup
def self.parse(start_markup, end_markup)
+3 -1
View File
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module Liquid
class ResourceLimits
attr_accessor :render_length, :render_score, :assign_score,
:render_length_limit, :render_score_limit, :assign_score_limit
:render_length_limit, :render_score_limit, :assign_score_limit
def initialize(limits)
@render_length_limit = limits[:render_length_limit]
+30 -27
View File
@@ -1,22 +1,24 @@
# frozen_string_literal: true
require 'cgi'
require 'bigdecimal'
module Liquid
module StandardFilters
HTML_ESCAPE = {
'&'.freeze => '&amp;'.freeze,
'>'.freeze => '&gt;'.freeze,
'<'.freeze => '&lt;'.freeze,
'"'.freeze => '&quot;'.freeze,
"'".freeze => '&#39;'.freeze
'&' => '&amp;',
'>' => '&gt;',
'<' => '&lt;',
'"' => '&quot;',
"'" => '&#39;',
}.freeze
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/.freeze
STRIP_HTML_BLOCKS = Regexp.union(
/<script.*?<\/script>/m,
%r{<script.*?</script>}m,
/<!--.*?-->/m,
/<style.*?<\/style>/m
%r{<style.*?</style>}m
)
STRIP_HTML_TAGS = /<.*?>/m
STRIP_HTML_TAGS = /<.*?>/m.freeze
# Return the size of an array or of an string
def size(input)
@@ -72,8 +74,9 @@ module Liquid
end
# Truncate a string down to x characters
def truncate(input, length = 50, truncate_string = "...".freeze)
def truncate(input, length = 50, truncate_string = '...')
return if input.nil?
input_str = input.to_s
length = Utils.to_integer(length)
truncate_string_str = truncate_string.to_s
@@ -82,13 +85,14 @@ module Liquid
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
end
def truncatewords(input, words = 15, truncate_string = "...".freeze)
def truncatewords(input, words = 15, truncate_string = '...')
return if input.nil?
wordlist = input.to_s.split
words = Utils.to_integer(words)
l = words - 1
l = 0 if l < 0
wordlist.length > l ? wordlist[0..l].join(" ".freeze).concat(truncate_string.to_s) : input
wordlist.length > l ? wordlist[0..l].join(' ').concat(truncate_string.to_s) : input
end
# Split input string into an array of substrings separated by given pattern.
@@ -113,7 +117,7 @@ module Liquid
end
def strip_html(input)
empty = ''.freeze
empty = ''
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
result.gsub!(STRIP_HTML_TAGS, empty)
result
@@ -121,11 +125,11 @@ module Liquid
# Remove all newlines from the string
def strip_newlines(input)
input.to_s.gsub(/\r?\n/, ''.freeze)
input.to_s.gsub(/\r?\n/, '')
end
# Join elements of the array with certain character between them
def join(input, glue = ' '.freeze)
def join(input, glue = ' ')
InputIterator.new(input).join(glue)
end
@@ -220,7 +224,7 @@ module Liquid
InputIterator.new(input).map do |e|
e = e.call if e.is_a?(Proc)
if property == "to_liquid".freeze
if property == 'to_liquid'
e
elsif e.respond_to?(:[])
r = e[property]
@@ -250,23 +254,23 @@ module Liquid
end
# Replace occurrences of a string with another
def replace(input, string, replacement = ''.freeze)
def replace(input, string, replacement = '')
input.to_s.gsub(string.to_s, replacement.to_s)
end
# Replace the first occurrences of a string with another
def replace_first(input, string, replacement = ''.freeze)
def replace_first(input, string, replacement = '')
input.to_s.sub(string.to_s, replacement.to_s)
end
# remove a substring
def remove(input, string)
input.to_s.gsub(string.to_s, ''.freeze)
input.to_s.gsub(string.to_s, '')
end
# remove the first occurrences of a substring
def remove_first(input, string)
input.to_s.sub(string.to_s, ''.freeze)
input.to_s.sub(string.to_s, '')
end
# add one string to another
@@ -275,9 +279,8 @@ module Liquid
end
def concat(input, array)
unless array.respond_to?(:to_ary)
raise ArgumentError.new("concat filter requires an array argument")
end
raise ArgumentError, 'concat filter requires an array argument' unless array.respond_to?(:to_ary)
InputIterator.new(input).concat(array)
end
@@ -288,7 +291,7 @@ module Liquid
# Add <br /> tags in front of all newlines in input string
def newline_to_br(input)
input.to_s.gsub(/\n/, "<br />\n".freeze)
input.to_s.gsub(/\n/, "<br />\n")
end
# Reformat a date using Ruby's core Time#strftime( string ) -> string
@@ -325,7 +328,7 @@ module Liquid
def date(input, format)
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)
end
@@ -419,7 +422,7 @@ module Liquid
result.is_a?(BigDecimal) ? result.to_f : result
end
def default(input, default_value = ''.freeze)
def default(input, default_value = '')
if !input || input.respond_to?(:empty?) && input.empty?
default_value
else
@@ -430,7 +433,7 @@ module Liquid
private
def raise_property_error(property)
raise Liquid::ArgumentError.new("cannot select the property '#{property}'")
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
end
def apply_operation(input, operand, operation)
+13 -9
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'set'
module Liquid
@@ -7,12 +9,12 @@ module Liquid
# The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
# Context#add_filters or Template.register_filter
class Strainer #:nodoc:
@@global_strainer = Class.new(Strainer) do
@global_strainer = Class.new(Strainer) do
@filter_methods = Set.new
end
@@strainer_class_cache = Hash.new do |hash, filters|
hash[filters] = Class.new(@@global_strainer) do
@filter_methods = @@global_strainer.filter_methods.dup
@strainer_class_cache = Hash.new do |hash, filters|
hash[filters] = Class.new(@global_strainer) do
@filter_methods = Strainer.global_strainer.filter_methods.dup
filters.each { |f| add_filter(f) }
end
end
@@ -22,12 +24,14 @@ module Liquid
end
class << self
attr_accessor :strainer_class_cache, :global_strainer
attr_reader :filter_methods
end
def self.add_filter(filter)
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
unless self.include?(filter)
unless include?(filter)
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
if invokable_non_public_methods.any?
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
@@ -39,8 +43,8 @@ module Liquid
end
def self.global_filter(filter)
@@strainer_class_cache.clear
@@global_strainer.add_filter(filter)
@strainer_class_cache.clear
@global_strainer.add_filter(filter)
end
def self.invokable?(method)
@@ -48,13 +52,13 @@ module Liquid
end
def self.create(context, filters = [])
@@strainer_class_cache[filters].new(context)
@strainer_class_cache[filters].new(context)
end
def invoke(method, *args)
if self.class.invokable?(method)
send(method, *args)
elsif @context && @context.strict_filters
elsif @context&.strict_filters
raise Liquid::UndefinedFilter, "undefined filter #{method}"
else
args.first
+2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class TablerowloopDrop < Drop
def initialize(length, cols)
+4 -3
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Tag
attr_reader :nodelist, :tag_name, :line_number, :parse_context
@@ -21,8 +23,7 @@ module Liquid
@line_number = parse_context.line_number
end
def parse(_tokens)
end
def parse(_tokens); end
def raw
"#{@tag_name} #{@markup}"
@@ -33,7 +34,7 @@ module Liquid
end
def render(_context)
''.freeze
''
end
# For backwards compatibility with custom tags. In a future release, the semantics
+8 -6
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Assign sets a variable in your template.
#
@@ -8,17 +10,17 @@ module Liquid
# {{ foo }}
#
class Assign < Tag
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
SYNTAX = /(#{VARIABLE_SIGNATURE}+)\s*=\s*(.*)\s*/om.freeze
attr_reader :to, :from
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@to = $1
@from = Variable.new($2, options)
if markup =~ SYNTAX
@to = Regexp.last_match(1)
@from = Variable.new(Regexp.last_match(2), options)
else
raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
raise SyntaxError, options[:locale].t('errors.syntax.assign')
end
end
@@ -55,5 +57,5 @@ module Liquid
end
end
Template.register_tag('assign'.freeze, Assign)
Template.register_tag('assign', Assign)
end
+3 -1
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Break tag to be used to break out of a for loop.
#
@@ -14,5 +16,5 @@ module Liquid
end
end
Template.register_tag('break'.freeze, Break)
Template.register_tag('break', Break)
end
+7 -5
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Capture stores the result of a block into a variable without rendering it inplace.
#
@@ -11,14 +13,14 @@ module Liquid
# in a sidebar or footer.
#
class Capture < Block
Syntax = /(#{VariableSignature}+)/o
SYNTAX = /(#{VARIABLE_SIGNATURE}+)/o.freeze
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@to = $1
if markup =~ SYNTAX
@to = Regexp.last_match(1)
else
raise SyntaxError.new(options[:locale].t("errors.syntax.capture"))
raise SyntaxError, options[:locale].t('errors.syntax.capture')
end
end
@@ -35,5 +37,5 @@ module Liquid
end
end
Template.register_tag('capture'.freeze, Capture)
Template.register_tag('capture', Capture)
end
+15 -19
View File
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module Liquid
class Case < Block
Syntax = /(#{QuotedFragment})/o
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
SYNTAX = /(#{QUOTED_FRAGMENT})/o.freeze
WHEN_SYNTAX = /(#{QUOTED_FRAGMENT})(?:(?:\s+or\s+|\s*\,\s*)(#{QUOTED_FRAGMENT}.*))?/om.freeze
attr_reader :blocks, :left
@@ -9,18 +11,16 @@ module Liquid
super
@blocks = []
if markup =~ Syntax
@left = Expression.parse($1)
if markup =~ SYNTAX
@left = Expression.parse(Regexp.last_match(1))
else
raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze))
raise SyntaxError, options[:locale].t('errors.syntax.case')
end
end
def parse(tokens)
body = BlockBody.new
while parse_body(body, tokens)
body = @blocks.last.attachment
end
body = @blocks.last.attachment while parse_body(body, tokens)
end
def nodelist
@@ -29,9 +29,9 @@ module Liquid
def unknown_tag(tag, markup, tokens)
case tag
when 'when'.freeze
when 'when'
record_when_condition(markup)
when 'else'.freeze
when 'else'
record_else_condition(markup)
else
super
@@ -61,22 +61,18 @@ module Liquid
body = BlockBody.new
while markup
unless markup =~ WhenSyntax
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_when".freeze))
end
raise SyntaxError, options[:locale].t('errors.syntax.case_invalid_when') unless markup =~ WHEN_SYNTAX
markup = $2
markup = Regexp.last_match(2)
block = Condition.new(@left, '=='.freeze, Expression.parse($1))
block = Condition.new(@left, '==', Expression.parse(Regexp.last_match(1)))
block.attach(body)
@blocks << block
end
end
def record_else_condition(markup)
unless markup.strip.empty?
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze))
end
raise SyntaxError, options[:locale].t('errors.syntax.case_invalid_else') unless markup.strip.empty?
block = ElseCondition.new
block.attach(BlockBody.new)
@@ -90,5 +86,5 @@ module Liquid
end
end
Template.register_tag('case'.freeze, Case)
Template.register_tag('case', Case)
end
+4 -3
View File
@@ -1,16 +1,17 @@
# frozen_string_literal: true
module Liquid
class Comment < Block
def render_to_output_buffer(_context, output)
output
end
def unknown_tag(_tag, _markup, _tokens)
end
def unknown_tag(_tag, _markup, _tokens); end
def blank?
true
end
end
Template.register_tag('comment'.freeze, Comment)
Template.register_tag('comment', Comment)
end
+3 -1
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Continue tag to be used to break out of a for loop.
#
@@ -14,5 +16,5 @@ module Liquid
end
end
Template.register_tag('continue'.freeze, Continue)
Template.register_tag('continue', Continue)
end
+11 -9
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
#
@@ -12,22 +14,22 @@ module Liquid
# <div class="green"> Item five</div>
#
class Cycle < Tag
SimpleSyntax = /\A#{QuotedFragment}+/o
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
SIMPLE_SYNTAX = /\A#{QUOTED_FRAGMENT}+/o.freeze
NAMED_SYNTAX = /\A(#{QUOTED_FRAGMENT})\s*\:\s*(.*)/om.freeze
attr_reader :variables
def initialize(tag_name, markup, options)
super
case markup
when NamedSyntax
@variables = variables_from_string($2)
@name = Expression.parse($1)
when SimpleSyntax
when NAMED_SYNTAX
@variables = variables_from_string(Regexp.last_match(2))
@name = Expression.parse(Regexp.last_match(1))
when SIMPLE_SYNTAX
@variables = variables_from_string(markup)
@name = @variables.to_s
else
raise SyntaxError.new(options[:locale].t("errors.syntax.cycle".freeze))
raise SyntaxError, options[:locale].t('errors.syntax.cycle')
end
end
@@ -60,8 +62,8 @@ module Liquid
def variables_from_string(markup)
markup.split(',').collect do |var|
var =~ /\s*(#{QuotedFragment})\s*/o
$1 ? Expression.parse($1) : nil
var =~ /\s*(#{QUOTED_FRAGMENT})\s*/o
Regexp.last_match(1) ? Expression.parse(Regexp.last_match(1)) : nil
end.compact
end
+3 -1
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# decrement is used in a place where one needs to insert a counter
# into a template, and needs the counter to survive across
@@ -32,5 +34,5 @@ module Liquid
end
end
Template.register_tag('decrement'.freeze, Decrement)
Template.register_tag('decrement', Decrement)
end
+28 -22
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# "For" iterates over an array or collection.
# Several useful variables are available to you within the loop.
@@ -44,7 +46,7 @@ module Liquid
# forloop.parentloop:: Provides access to the parent loop, if present.
#
class For < Block
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
SYNTAX = /\A(#{VARIABLE_SEGMENT}+)\s+in\s+(#{QUOTED_FRAGMENT}+)\s*(reversed)?/o.freeze
attr_reader :collection_name, :variable_name, :limit, :from
@@ -58,6 +60,7 @@ module Liquid
def parse(tokens)
return unless parse_body(@for_block, tokens)
parse_body(@else_block, tokens)
end
@@ -66,7 +69,8 @@ module Liquid
end
def unknown_tag(tag, markup, tokens)
return super unless tag == 'else'.freeze
return super unless tag == 'else'
@else_block = BlockBody.new
end
@@ -85,33 +89,35 @@ module Liquid
protected
def lax_parse(markup)
if markup =~ Syntax
@variable_name = $1
collection_name = $2
@reversed = !!$3
if markup =~ SYNTAX
@variable_name = Regexp.last_match(1)
collection_name = Regexp.last_match(2)
@reversed = !!Regexp.last_match(3)
@name = "#{@variable_name}-#{collection_name}"
@collection_name = Expression.parse(collection_name)
markup.scan(TagAttributes) do |key, value|
markup.scan(TAG_ATTRIBUTES) do |key, value|
set_attribute(key, value)
end
else
raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
raise SyntaxError, options[:locale].t('errors.syntax.for')
end
end
def strict_parse(markup)
p = Parser.new(markup)
@variable_name = p.consume(:id)
raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
raise SyntaxError, options[:locale].t('errors.syntax.for_invalid_in') unless p.id?('in')
collection_name = p.expression
@name = "#{@variable_name}-#{collection_name}"
@collection_name = Expression.parse(collection_name)
@reversed = p.id?('reversed'.freeze)
@reversed = p.id?('reversed')
while p.look(:id) && p.look(:colon, 1)
unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
unless (attribute = p.id?('limit') || p.id?('offset'))
raise SyntaxError, options[:locale].t('errors.syntax.for_invalid_attribute')
end
p.consume
set_attribute(attribute, p.expression)
end
@@ -162,7 +168,7 @@ module Liquid
for_stack.push(loop_vars)
begin
context['forloop'.freeze] = loop_vars
context['forloop'] = loop_vars
segment.each do |item|
context[@variable_name] = item
@@ -170,11 +176,11 @@ module Liquid
loop_vars.send(:increment!)
# Handle any interrupts if they exist.
if context.interrupt?
interrupt = context.pop_interrupt
break if interrupt.is_a? BreakInterrupt
next if interrupt.is_a? ContinueInterrupt
end
next unless context.interrupt?
interrupt = context.pop_interrupt
break if interrupt.is_a?(BreakInterrupt)
next if interrupt.is_a?(ContinueInterrupt)
end
ensure
for_stack.pop
@@ -186,13 +192,13 @@ module Liquid
def set_attribute(key, expr)
case key
when 'offset'.freeze
@from = if expr == 'continue'.freeze
when 'offset'
@from = if expr == 'continue'
:continue
else
Expression.parse(expr)
end
when 'limit'.freeze
when 'limit'
@limit = Expression.parse(expr)
end
end
@@ -212,5 +218,5 @@ module Liquid
end
end
Template.register_tag('for'.freeze, For)
Template.register_tag('for', For)
end
+19 -18
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# If is the conditional block
#
@@ -10,16 +12,16 @@ module Liquid
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
#
class If < Block
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
BOOLEAN_OPERATORS = %w(and or).freeze
SYNTAX = /(#{QUOTED_FRAGMENT})\s*([=!<>a-z_]+)?\s*(#{QUOTED_FRAGMENT})?/o.freeze
EXPRESSIONS_AND_OPERATORS = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QUOTED_FRAGMENT}|\S+)\s*)+)/o.freeze
BOOLEAN_OPERATORS = %w[and or].freeze
attr_reader :blocks
def initialize(tag_name, markup, options)
super
@blocks = []
push_block('if'.freeze, markup)
push_block('if', markup)
end
def nodelist
@@ -32,7 +34,7 @@ module Liquid
end
def unknown_tag(tag, markup, tokens)
if ['elsif'.freeze, 'else'.freeze].include?(tag)
if %w[elsif else].include?(tag)
push_block(tag, markup)
else
super
@@ -42,9 +44,7 @@ module Liquid
def render_to_output_buffer(context, output)
context.stack do
@blocks.each do |block|
if block.evaluate(context)
return block.attachment.render_to_output_buffer(context, output)
end
return block.attachment.render_to_output_buffer(context, output) if block.evaluate(context)
end
end
@@ -54,7 +54,7 @@ module Liquid
private
def push_block(tag, markup)
block = if tag == 'else'.freeze
block = if tag == 'else'
ElseCondition.new
else
parse_with_selected_parser(markup)
@@ -65,18 +65,19 @@ module Liquid
end
def lax_parse(markup)
expressions = markup.scan(ExpressionsAndOperators)
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
expressions = markup.scan(EXPRESSIONS_AND_OPERATORS)
raise SyntaxError, options[:locale].t('errors.syntax.if') unless expressions.pop =~ SYNTAX
condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3)))
until expressions.empty?
operator = expressions.pop.to_s.strip
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
raise SyntaxError, options[:locale].t('errors.syntax.if') 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)))
raise SyntaxError, options[:locale].t('errors.syntax.if') unless BOOLEAN_OPERATORS.include?(operator)
new_condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
new_condition.send(operator, condition)
condition = new_condition
end
@@ -94,7 +95,7 @@ module Liquid
def parse_binary_comparisons(p)
condition = parse_comparison(p)
first_condition = condition
while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
while (op = (p.id?('and') || p.id?('or')))
child_condition = parse_comparison(p)
condition.send(op, child_condition)
condition = child_condition
@@ -104,7 +105,7 @@ module Liquid
def parse_comparison(p)
a = Expression.parse(p.expression)
if op = p.consume?(:comparison)
if (op = p.consume?(:comparison))
b = Expression.parse(p.expression)
Condition.new(a, op, b)
else
@@ -119,5 +120,5 @@ module Liquid
end
end
Template.register_tag('if'.freeze, If)
Template.register_tag('if', If)
end
+4 -2
View File
@@ -1,8 +1,10 @@
# frozen_string_literal: true
module Liquid
class Ifchanged < Block
def render_to_output_buffer(context, output)
context.stack do
block_output = ''
block_output = ''.dup
super(context, block_output)
if block_output != context.registers[:ifchanged]
@@ -15,5 +17,5 @@ module Liquid
end
end
Template.register_tag('ifchanged'.freeze, Ifchanged)
Template.register_tag('ifchanged', Ifchanged)
end
+15 -13
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Include allows templates to relate with other templates
#
@@ -14,40 +16,39 @@ module Liquid
# {% include 'product' for products %}
#
class Include < Tag
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
SYNTAX = /(#{QUOTED_FRAGMENT}+)(\s+(?:with|for)\s+(#{QUOTED_FRAGMENT}+))?/o.freeze
attr_reader :template_name_expr, :variable_name_expr, :attributes
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
if markup =~ SYNTAX
template_name = $1
variable_name = $3
template_name = Regexp.last_match(1)
variable_name = Regexp.last_match(3)
@variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
@template_name_expr = Expression.parse(template_name)
@attributes = {}
markup.scan(TagAttributes) do |key, value|
markup.scan(TAG_ATTRIBUTES) do |key, value|
@attributes[key] = Expression.parse(value)
end
else
raise SyntaxError.new(options[:locale].t("errors.syntax.include".freeze))
raise SyntaxError, options[:locale].t('errors.syntax.include')
end
end
def parse(_tokens)
end
def parse(_tokens); end
def render_to_output_buffer(context, output)
template_name = context.evaluate(@template_name_expr)
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
raise ArgumentError, options[:locale].t('errors.argument.include') unless template_name
partial = load_cached_partial(template_name, context)
context_variable_name = template_name.split('/'.freeze).last
context_variable_name = template_name.split('/').last
variable = if @variable_name_expr
context.evaluate(@variable_name_expr)
@@ -91,9 +92,10 @@ module Liquid
def load_cached_partial(template_name, context)
cached_partials = context.registers[:cached_partials] || {}
if cached = cached_partials[template_name]
if (cached = cached_partials[template_name])
return cached
end
source = read_template_from_file_system(context)
begin
parse_context.partial = true
@@ -116,11 +118,11 @@ module Liquid
def children
[
@node.template_name_expr,
@node.variable_name_expr
@node.variable_name_expr,
] + @node.attributes.values
end
end
end
Template.register_tag('include'.freeze, Include)
Template.register_tag('include', Include)
end
+3 -1
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# increment is used in a place where one needs to insert a counter
# into a template, and needs the counter to survive across
@@ -28,5 +30,5 @@ module Liquid
end
end
Template.register_tag('increment'.freeze, Increment)
Template.register_tag('increment', Increment)
end
+12 -12
View File
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module Liquid
class Raw < Block
Syntax = /\A\s*\z/
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
SYNTAX = /\A\s*\z/.freeze
FULL_TOKEN_POSSIBLY_INVALID = /\A(.*)#{TAG_START}\s*(\w+)\s*(.*)?#{TAG_END}\z/om.freeze
def initialize(tag_name, markup, parse_context)
super
@@ -10,16 +12,16 @@ module Liquid
end
def parse(tokens)
@body = ''
while token = tokens.shift
if token =~ FullTokenPossiblyInvalid
@body << $1 if $1 != "".freeze
return if block_delimiter == $2
@body = ''.dup
while (token = tokens.shift)
if token =~ FULL_TOKEN_POSSIBLY_INVALID
@body << Regexp.last_match(1) if Regexp.last_match(1) != ''
return if block_delimiter == Regexp.last_match(2)
end
@body << token unless token.empty?
end
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
raise SyntaxError, parse_context.locale.t('errors.syntax.tag_never_closed', block_name: block_name)
end
def render_to_output_buffer(_context, output)
@@ -38,11 +40,9 @@ module Liquid
protected
def ensure_valid_markup(tag_name, markup, parse_context)
unless markup =~ Syntax
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_unexpected_args".freeze, tag: tag_name))
end
raise SyntaxError, parse_context.locale.t('errors.syntax.tag_unexpected_args', tag: tag_name) unless markup =~ SYNTAX
end
end
Template.register_tag('raw'.freeze, Raw)
Template.register_tag('raw', Raw)
end
+15 -15
View File
@@ -1,39 +1,41 @@
# frozen_string_literal: true
module Liquid
class TableRow < Block
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
SYNTAX = /(\w+)\s+in\s+(#{QUOTED_FRAGMENT}+)/o.freeze
attr_reader :variable_name, :collection_name, :attributes
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@variable_name = $1
@collection_name = Expression.parse($2)
if markup =~ SYNTAX
@variable_name = Regexp.last_match(1)
@collection_name = Expression.parse(Regexp.last_match(2))
@attributes = {}
markup.scan(TagAttributes) do |key, value|
markup.scan(TAG_ATTRIBUTES) do |key, value|
@attributes[key] = Expression.parse(value)
end
else
raise SyntaxError.new(options[:locale].t("errors.syntax.table_row".freeze))
raise SyntaxError, options[:locale].t('errors.syntax.table_row')
end
end
def render_to_output_buffer(context, output)
collection = context.evaluate(@collection_name) or return ''.freeze
(collection = context.evaluate(@collection_name)) || (return '')
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
to = @attributes.key?('limit'.freeze) ? from + context.evaluate(@attributes['limit'.freeze]).to_i : nil
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset']).to_i : 0
to = @attributes.key?('limit') ? from + context.evaluate(@attributes['limit']).to_i : nil
collection = Utils.slice_collection(collection, from, to)
length = collection.length
cols = context.evaluate(@attributes['cols'.freeze]).to_i
cols = context.evaluate(@attributes['cols']).to_i
output << "<tr class=\"row1\">\n"
context.stack do
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
context['tablerowloop'.freeze] = tablerowloop
context['tablerowloop'] = tablerowloop
collection.each do |item|
context[@variable_name] = item
@@ -42,9 +44,7 @@ module Liquid
super
output << '</td>'
if tablerowloop.col_last && !tablerowloop.last
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
end
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">" if tablerowloop.col_last && !tablerowloop.last
tablerowloop.send(:increment!)
end
@@ -61,5 +61,5 @@ module Liquid
end
end
Template.register_tag('tablerow'.freeze, TableRow)
Template.register_tag('tablerow', TableRow)
end
+5 -7
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'if'
module Liquid
@@ -10,15 +12,11 @@ module Liquid
context.stack do
# First condition is interpreted backwards ( if not )
first_block = @blocks.first
unless first_block.evaluate(context)
return first_block.attachment.render_to_output_buffer(context, output)
end
return first_block.attachment.render_to_output_buffer(context, output) unless first_block.evaluate(context)
# After the first condition unless works just like if
@blocks[1..-1].each do |block|
if block.evaluate(context)
return block.attachment.render_to_output_buffer(context, output)
end
return block.attachment.render_to_output_buffer(context, output) if block.evaluate(context)
end
end
@@ -26,5 +24,5 @@ module Liquid
end
end
Template.register_tag('unless'.freeze, Unless)
Template.register_tag('unless', Unless)
end
+11 -15
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Templates are central to liquid.
# Interpretating templates is a two step process. First you compile the
@@ -16,7 +18,11 @@ module Liquid
attr_accessor :root
attr_reader :resource_limits, :warnings
@@file_system = BlankFileSystem.new
@file_system = BlankFileSystem.new
class << self
attr_accessor :file_system
end
class TagRegistry
include Enumerable
@@ -74,14 +80,6 @@ module Liquid
exception
end
def file_system
@@file_system
end
def file_system=(obj)
@@file_system = obj
end
def register_tag(name, klass)
tags[name.to_s] = klass
end
@@ -165,15 +163,13 @@ module Liquid
# filters and tags and might be useful to integrate liquid more with its host application
#
def render(*args)
return ''.freeze if @root.nil?
return '' if @root.nil?
context = case args.first
when Liquid::Context
c = args.shift
if @rethrow_errors
c.exception_renderer = ->(e) { raise }
end
c.exception_renderer = ->(_e) { raise } if @rethrow_errors
c
when Liquid::Drop
@@ -184,7 +180,7 @@ module Liquid
when nil
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits)
else
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
raise ArgumentError, 'Expected Hash or Liquid::Context as parameter'
end
output = nil
@@ -208,7 +204,7 @@ module Liquid
# render the nodelist.
# for performance reasons we get an array back here. join will make a string out of it.
with_profiling(context) do
@root.render_to_output_buffer(context, output || '')
@root.render_to_output_buffer(context, output || ''.dup)
end
rescue Liquid::MemoryError => e
context.handle_error(e)
+4 -2
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Tokenizer
attr_reader :line_number
@@ -20,10 +22,10 @@ module Liquid
@source = @source.source if @source.respond_to?(:source)
return [] if @source.to_s.empty?
tokens = @source.split(TemplateParser)
tokens = @source.split(TEMPLATE_PARSER)
# removes the rogue empty element at the beginning of the array
tokens.shift if tokens[0] && tokens[0].empty?
tokens.shift if tokens[0]&.empty?
tokens
end
+9 -9
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
module Utils
def self.slice_collection(collection, from, to)
@@ -19,13 +21,9 @@ module Liquid
return [] unless collection.respond_to?(:each)
collection.each do |item|
if to && to <= index
break
end
break if to && to <= index
if from <= index
segments << item
end
segments << item if from <= index
index += 1
end
@@ -35,11 +33,12 @@ module Liquid
def self.to_integer(num)
return num if num.is_a?(Integer)
num = num.to_s
begin
Integer(num)
rescue ::ArgumentError
raise Liquid::ArgumentError, "invalid integer"
raise Liquid::ArgumentError, 'invalid integer'
end
end
@@ -50,7 +49,7 @@ module Liquid
when Numeric
obj
when String
(obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal(obj) : obj.to_i
obj.strip =~ /\A-?\d+\.\d+\z/ ? BigDecimal(obj) : obj.to_i
else
if obj.respond_to?(:to_number)
obj.to_number
@@ -65,11 +64,12 @@ module Liquid
if obj.is_a?(String)
return nil if obj.empty?
obj = obj.downcase
end
case obj
when 'now'.freeze, 'today'.freeze
when 'now', 'today'
Time.now
when /\A\d+\z/, Integer
Time.at(obj.to_i)
+17 -14
View File
@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Holds variables. Variables are only loaded "just in time"
# and are not evaluated as part of the render stage
@@ -10,11 +12,11 @@ module Liquid
# {{ user | link }}
#
class Variable
FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
JustTagAttributes = /\A#{TagAttributes}\z/o
MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
FILTER_MARKUP_REGEX = /#{FILTER_SEPARATOR}\s*(.*)/om.freeze
FILTER_PARSER = /(?:\s+|#{QUOTED_FRAGMENT}|#{ARGUMENT_SEPARATOR})+/o.freeze
FILTER_ARGS_REGEX = /(?:#{FILTER_ARGUMENT_SEPARATOR}|#{ARGUMENT_SEPARATOR})\s*((?:\w+\s*\:\s*)?#{QUOTED_FRAGMENT})/o.freeze
JUST_TAG_ATTRIBUTES = /\A#{TAG_ATTRIBUTES}\z/o.freeze
MARKUP_WITH_QUOTED_FRAGMENT = /(#{QUOTED_FRAGMENT})(.*)/om.freeze
attr_accessor :filters, :name, :line_number
attr_reader :parse_context
@@ -41,17 +43,18 @@ module Liquid
def lax_parse(markup)
@filters = []
return unless markup =~ MarkupWithQuotedFragment
return unless markup =~ MARKUP_WITH_QUOTED_FRAGMENT
name_markup = $1
filter_markup = $2
name_markup = Regexp.last_match(1)
filter_markup = Regexp.last_match(2)
@name = Expression.parse(name_markup)
if filter_markup =~ FilterMarkupRegex
filters = $1.scan(FilterParser)
if filter_markup =~ FILTER_MARKUP_REGEX
filters = Regexp.last_match(1).scan(FILTER_PARSER)
filters.each do |f|
next unless f =~ /\w+/
filtername = Regexp.last_match(0)
filterargs = f.scan(FilterArgsRegex).flatten
filterargs = f.scan(FILTER_ARGS_REGEX).flatten
@filters << parse_filter_expressions(filtername, filterargs)
end
end
@@ -108,7 +111,7 @@ module Liquid
filter_args = []
keyword_args = nil
unparsed_args.each do |a|
if matches = a.match(JustTagAttributes)
if (matches = a.match(JUST_TAG_ATTRIBUTES))
keyword_args ||= {}
keyword_args[matches[1]] = Expression.parse(matches[2])
else
@@ -121,7 +124,7 @@ module Liquid
end
def evaluate_filter_expressions(context, filter_args, filter_kwargs)
parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
parsed_args = filter_args.map { |expr| context.evaluate(expr) }
if filter_kwargs
parsed_kwargs = {}
filter_kwargs.each do |key, expr|
@@ -136,7 +139,7 @@ module Liquid
return unless obj.tainted?
return if Template.taint_mode == :lax
@markup =~ QuotedFragment
@markup =~ QUOTED_FRAGMENT
name = Regexp.last_match(0)
error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
+10 -9
View File
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module Liquid
class VariableLookup
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
SQUARE_BRACKETED = /\A\[(.*)\]\z/m.freeze
COMMAND_METHODS = %w[size first last].freeze
attr_reader :name, :lookups
@@ -10,12 +12,10 @@ module Liquid
end
def initialize(markup)
lookups = markup.scan(VariableParser)
lookups = markup.scan(VARIABLE_PARSER)
name = lookups.shift
if name =~ SQUARE_BRACKETED
name = Expression.parse($1)
end
name = Expression.parse(Regexp.last_match(1)) if name =~ SQUARE_BRACKETED
@name = name
@lookups = lookups
@@ -24,7 +24,7 @@ module Liquid
@lookups.each_index do |i|
lookup = lookups[i]
if lookup =~ SQUARE_BRACKETED
lookups[i] = Expression.parse($1)
lookups[i] = Expression.parse(Regexp.last_match(1))
elsif COMMAND_METHODS.include?(lookup)
@command_flags |= 1 << i
end
@@ -41,8 +41,8 @@ module Liquid
# If object is a hash- or array-like object we look for the
# presence of the key and if its available we return it
if object.respond_to?(:[]) &&
((object.respond_to?(:key?) && object.key?(key)) ||
(object.respond_to?(:fetch) && key.is_a?(Integer)))
((object.respond_to?(:key?) && object.key?(key)) ||
(object.respond_to?(:fetch) && key.is_a?(Integer)))
# if its a proc we will replace the entry with the proc
res = context.lookup_and_evaluate(object, key)
@@ -59,6 +59,7 @@ module Liquid
# raise an exception if `strict_variables` option is set to true
else
return nil unless context.strict_variables
raise Liquid::UndefinedVariable, "undefined variable #{key}"
end
+2 -2
View File
@@ -1,5 +1,5 @@
# encoding: utf-8
# frozen_string_literal: true
module Liquid
VERSION = "4.0.3".freeze
VERSION = '4.0.3'
end