Compare commits

...

31 Commits

Author SHA1 Message Date
Mike Angell
cd347008ee Expand usage tracking 2019-09-24 05:22:14 +10:00
Mike Angell
9a9564499a New liquid tracking. 2019-09-24 05:19:48 +10:00
Mike Angell
f4d134cd5c Remove jruby and truffleruby testing (#1167) 2019-09-20 02:28:43 +10:00
Mike Angell
b667bcb48b Shopify stye guide fixes (#1160) 2019-09-20 02:08:11 +10:00
Ashwin Maroli
2c14e0b2ba Use Regexp#match? when MatchData is not used (#1165)
* Use `Regexp#match?` when `MatchData` is not used

* Add `TargetRubyVersion: 2.4` to RuboCop config
2019-09-20 02:07:52 +10:00
Ashwin Maroli
ca207ed93f Cleanup RuboCop configuration file (#1161) 2019-09-20 00:55:01 +10:00
Mike Angell
ef13343591 Changes static registers to not be frozen (#1163)
* Changes static registers to not be frozen

* Add frozen test to static registers
2019-09-20 00:24:48 +10:00
Mike Angell
adb40c41b7 Enable frozen_string_literal 2019-09-18 13:40:07 +10:00
Mike Angell
d8403af515 Reimplementation of Static Registers (#1157) 2019-09-18 13:25:55 +10:00
Mike Angell
0d26f05bb8 Enabled frozen string literals (#1154)
* Enabled frozen string literals

* Update rubocop config

* Prefer string interpolation in simple cases

Co-Authored-By: Dylan Thacker-Smith <dylan.smith@shopify.com>
2019-09-18 13:19:45 +10:00
Thierry Joyal
1dcad34b06 Merge pull request #1151 from Shopify/invokable-methods-for-enumerable-reject-include
Invokable methods for enumerable reject include?
2019-09-16 09:49:40 -04:00
Mike Angell
9a42c8c8b2 Merge pull request #1149 from Shopify/liquid-usage
Add usage tracking
2019-09-16 12:14:50 +10:00
Mike Angell
1fcef2133f Merge pull request #1143 from Shopify/styling-fixes-1
Apply simple rubocop fixes
2019-09-16 12:14:32 +10:00
Mike Angell
d7514b1305 Merge pull request #1137 from Shopify/remove-lazy-stacks
Remove lazy load stacks
2019-09-16 12:14:14 +10:00
Thierry Joyal
c0ffee5919 Invokable methods for enumerable reject include? 2019-09-12 12:58:51 +00:00
Mike Angell
724d02e9b3 Disable interrupt fix in this round 2019-09-11 06:35:08 +10:00
Mike Angell
a5b387cdd4 Remove reserved word Interrupt to avoid confusion
Also resolves rubocop conflicts
2019-09-11 06:32:31 +10:00
Mike Angell
8318be2edc Update readme 2019-09-11 05:20:05 +10:00
Mike Angell
b6547f322e Simplify usage 2019-09-11 04:56:25 +10:00
Mike Angell
b316ff8413 Add usage tracking 2019-09-11 04:20:34 +10:00
Justin Li
806b2622da Switch back to Liquid-C master, since https://github.com/Shopify/liquid-c/pull/50 is merged 2019-09-04 15:12:51 -04:00
Mike Angell
1f90a37b63 Merge branch 'master' into styling-fixes-1 2019-09-04 14:26:27 +10:00
Mike Angell
c34f7c9b2c Merge pull request #1145 from Shopify/master-fixes
Render tag styling fixes
2019-09-04 14:25:38 +10:00
Mike Angell
604d899496 Render tag styling fixes 2019-08-31 22:48:25 +10:00
Mike Angell
799da202df Apply simple rubocop fixes 2019-08-31 21:58:33 +10:00
Mike Angell
ddb45cd658 Merge pull request #1139 from Shopify/shopify_ruby_style
Follow Shopify ruby style guide
2019-08-31 21:43:45 +10:00
Mike Angell
dafbb4ae90 Remove hasnling false scopes 2019-08-31 20:03:54 +10:00
Justin Li
9876096cf4 Merge pull request #1141 from ashmaroli/reduce-context-constructor-allocations
Reduce allocations from `Liquid::Context.new`
2019-08-30 12:53:50 -04:00
Ashwin Maroli
8750b4b006 Reduce allocations from Liquid::Context.new 2019-08-30 09:01:47 +05:30
Mike Angell
d67de1c9b2 Follow Shopify ruby style
This is the first step in bringing Liquid style inline with Shopify ruby style
2019-08-29 13:39:57 +10:00
Mike Angell
2324564743 Remove lazy load stacks
Remove lazy load stacks and instead only create a new scope when a tag is known to need one
2019-08-29 09:09:32 +10:00
129 changed files with 2168 additions and 1001 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
inherit_from:
- 'https://shopify.github.io/ruby-style-guide/rubocop.yml'
- .rubocop_todo.yml
- ./.rubocop_todo.yml
require: rubocop-performance
@@ -8,124 +8,9 @@ Performance:
Enabled: true
AllCops:
TargetRubyVersion: 2.4
Exclude:
- 'performance/shopify/*'
- 'vendor/bundle/**/*'
- 'pkg/**'
Metrics/BlockNesting:
Max: 3
Metrics/ModuleLength:
Enabled: false
Metrics/ClassLength:
Enabled: false
Lint/AssignmentInCondition:
Enabled: false
Lint/AmbiguousOperator:
Enabled: false
Lint/AmbiguousRegexpLiteral:
Enabled: false
Lint/ParenthesesAsGroupedExpression:
Enabled: false
Lint/UnusedBlockArgument:
Enabled: false
Layout/EndAlignment:
EnforcedStyleAlignWith: variable
Lint/UnusedMethodArgument:
Enabled: false
Style/SingleLineBlockParams:
Enabled: false
Style/DoubleNegation:
Enabled: false
Style/StringLiteralsInInterpolation:
Enabled: false
Style/AndOr:
Enabled: false
Style/SignalException:
Enabled: false
Style/StringLiterals:
Enabled: false
Style/BracesAroundHashParameters:
Enabled: false
Style/NumericLiterals:
Enabled: false
Layout/SpaceInsideArrayLiteralBrackets:
Enabled: false
Layout/SpaceBeforeBlockBraces:
Enabled: false
Style/Documentation:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/TrailingCommaInArrayLiteral:
Enabled: false
Style/TrailingCommaInHashLiteral:
Enabled: false
Style/FormatString:
Enabled: false
Layout/AlignParameters:
EnforcedStyle: with_fixed_indentation
Layout/MultilineOperationIndentation:
EnforcedStyle: indented
Style/IfUnlessModifier:
Enabled: false
Style/RaiseArgs:
Enabled: false
Style/PreferredHashMethods:
Enabled: false
Style/RegexpLiteral:
Enabled: false
Style/SymbolLiteral:
Enabled: false
Naming/ConstantName:
Enabled: false
Layout/CaseIndentation:
Enabled: false
Style/ClassVars:
Enabled: false
Style/PerlBackrefs:
Enabled: false
Style/TrivialAccessors:
AllowPredicates: true
Style/WordArray:
Enabled: false
Naming/MethodName:
Exclude:

View File

@@ -1,95 +1,11 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-08-29 00:43:36 +1000 using RuboCop version 0.74.0.
# on 2019-09-11 06:34:25 +1000 using RuboCop version 0.74.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: TreatCommentsAsGroupSeparators, Include.
# Include: **/*.gemspec
Gemspec/OrderedDependencies:
Exclude:
- 'liquid.gemspec'
# Offense count: 1
# Configuration parameters: Include.
# Include: **/*.gemspec,
Gemspec/RequiredRubyVersion:
Exclude:
- 'liquid.gemspec'
# Offense count: 124
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: with_first_argument, with_fixed_indentation
Layout/AlignArguments:
Enabled: false
# Offense count: 7
# Cop supports --auto-correct.
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
# SupportedHashRocketStyles: key, separator, table
# SupportedColonStyles: key, separator, table
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
Layout/AlignHash:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/expression.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 6
# Cop supports --auto-correct.
Layout/ClosingHeredocIndentation:
Exclude:
- 'test/integration/tags/for_tag_test.rb'
# Offense count: 27
# Cop supports --auto-correct.
Layout/EmptyLineAfterGuardClause:
Exclude:
- 'lib/liquid/block.rb'
- 'lib/liquid/block_body.rb'
- 'lib/liquid/context.rb'
- 'lib/liquid/drop.rb'
- 'lib/liquid/lexer.rb'
- 'lib/liquid/parser.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/tags/for.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/utils.rb'
- 'lib/liquid/variable.rb'
- 'lib/liquid/variable_lookup.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: squiggly, active_support, powerpack, unindent
Layout/IndentHeredoc:
Exclude:
- 'test/integration/tags/for_tag_test.rb'
- 'test/integration/trim_mode_test.rb'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: symmetrical, new_line, same_line
Layout/MultilineMethodCallBraceLayout:
Exclude:
- 'test/integration/error_handling_test.rb'
- 'test/unit/strainer_unit_test.rb'
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
Layout/SpaceAroundOperators:
Exclude:
- 'lib/liquid/condition.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
@@ -98,248 +14,35 @@ Lint/InheritException:
Exclude:
- 'lib/liquid/interrupts.rb'
# Offense count: 1
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'lib/liquid/parse_context.rb'
# Offense count: 53
Metrics/AbcSize:
Max: 56
# Offense count: 12
Metrics/CyclomaticComplexity:
Max: 13
# Offense count: 118
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/MethodLength:
Max: 38
# Offense count: 9
Metrics/PerceivedComplexity:
Max: 11
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: PreferredName.
Naming/RescuedExceptionsVariableName:
Exclude:
- 'lib/liquid/context.rb'
# Offense count: 20
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
# AllowedNames: io, id, to, by, on, in, at, ip, db
Naming/UncommunicativeMethodParamName:
Exclude:
- 'example/server/example_servlet.rb'
- 'lib/liquid/condition.rb'
- 'lib/liquid/context.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/variable.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/condition_unit_test.rb'
# Offense count: 5
# Configuration parameters: EnforcedStyle.
# SupportedStyles: inline, group
Style/AccessModifierDeclarations:
Exclude:
- 'lib/liquid/block_body.rb'
- 'lib/liquid/tag.rb'
- 'lib/liquid/tags/include.rb'
- 'test/unit/strainer_unit_test.rb'
# Offense count: 10
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: prefer_alias, prefer_alias_method
Style/Alias:
Exclude:
- 'lib/liquid/drop.rb'
- 'lib/liquid/i18n.rb'
- 'lib/liquid/profiler/hooks.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tag.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/variable.rb'
# Offense count: 22
Style/CommentedKeyword:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Exclude:
- 'lib/liquid/errors.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/EmptyCaseCondition:
Exclude:
- 'lib/liquid/lexer.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: compact, expanded
Style/EmptyMethod:
Exclude:
- 'lib/liquid/tag.rb'
- 'lib/liquid/tags/comment.rb'
- 'lib/liquid/tags/include.rb'
- 'test/integration/tags/include_tag_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 3
# Cop supports --auto-correct.
Style/Encoding:
Exclude:
- 'lib/liquid/version.rb'
- 'liquid.gemspec'
- 'test/integration/standard_filter_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/ExpandPathArguments:
Exclude:
- 'Rakefile'
- 'liquid.gemspec'
# Offense count: 7
# Configuration parameters: EnforcedStyle.
# SupportedStyles: annotated, template, unannotated
Style/FormatStringToken:
Exclude:
- 'test/integration/filter_test.rb'
- 'test/integration/hash_ordering_test.rb'
# Offense count: 106
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, never
Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 14
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/lexer.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/tags/assign.rb'
- 'lib/liquid/tags/capture.rb'
- 'lib/liquid/tags/case.rb'
- 'lib/liquid/tags/for.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/tags/raw.rb'
- 'lib/liquid/tags/table_row.rb'
- 'lib/liquid/variable.rb'
- 'test/unit/tokenizer_unit_test.rb'
# Offense count: 53
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
Style/MutableConstant:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength.
# SupportedStyles: skip_modifier_ifs, always
Style/Next:
Exclude:
- 'lib/liquid/tags/for.rb'
# Offense count: 13
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
# SupportedStyles: predicate, comparison
Style/NumericPredicate:
Exclude:
- 'spec/**/*'
- 'lib/liquid/context.rb'
- 'lib/liquid/forloop_drop.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tablerowloop_drop.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/template_test.rb'
# Offense count: 14
# Cop supports --auto-correct.
# Configuration parameters: PreferredDelimiters.
Style/PercentLiteralDelimiters:
Exclude:
- 'lib/liquid/tags/if.rb'
- 'liquid.gemspec'
- 'test/integration/assign_test.rb'
- 'test/integration/standard_filter_test.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantSelf:
Exclude:
- 'lib/liquid/strainer.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, Whitelist.
# Whitelist: present?, blank?, presence, try, try!
Style/SafeNavigation:
Exclude:
- 'lib/liquid/drop.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/tokenizer.rb'
# Offense count: 9
# Cop supports --auto-correct.
# Configuration parameters: AllowAsExpressionSeparator.
Style/Semicolon:
Exclude:
- 'test/integration/error_handling_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 7
# Cop supports --auto-correct.
# Configuration parameters: MinSize.
# SupportedStyles: percent, brackets
Style/SymbolArray:
EnforcedStyle: brackets
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
Style/TernaryParentheses:
Exclude:
- 'lib/liquid/context.rb'
- 'lib/liquid/utils.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/UnneededPercentQ:
Exclude:
- 'test/integration/error_handling_test.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/WhileUntilModifier:
Exclude:
- 'lib/liquid/tags/case.rb'
# Offense count: 665
# Offense count: 98
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 294
# Offense count: 44
Naming/ConstantName:
Exclude:
- 'lib/liquid.rb'
- 'lib/liquid/block_body.rb'
- 'lib/liquid/tags/assign.rb'
- 'lib/liquid/tags/capture.rb'
- 'lib/liquid/tags/case.rb'
- 'lib/liquid/tags/cycle.rb'
- 'lib/liquid/tags/for.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/tags/raw.rb'
- 'lib/liquid/tags/table_row.rb'
- 'lib/liquid/variable.rb'
- 'performance/shopify/comment_form.rb'
- 'performance/shopify/paginate.rb'
- 'test/integration/tags/include_tag_test.rb'
# Offense count: 5
Style/ClassVars:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/template.rb'

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
source 'https://rubygems.org'
git_source(:github) do |repo_name|
"https://github.com/#{repo_name}.git"
@@ -20,6 +22,6 @@ group :test do
gem 'rubocop-performance', require: false
platform :mri, :truffleruby do
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'liquid-tag'
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'master'
end
end

View File

@@ -106,3 +106,9 @@ template = Liquid::Template.parse("{{x}} {{y}}")
template.render!({ 'x' => 1}, { strict_variables: true })
#=> Liquid::UndefinedVariable: Liquid error: undefined variable y
```
### Usage tracking
To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.

View File

@@ -1,18 +1,20 @@
# frozen_string_literal: true
require 'rake'
require 'rake/testtask'
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
require "liquid/version"
task default: [:test, :rubocop]
task(default: [:test, :rubocop])
desc 'run test suite with default parser'
desc('run test suite with default parser')
Rake::TestTask.new(:base_test) do |t|
t.libs << '.' << 'lib' << 'test'
t.test_files = FileList['test/{integration,unit}/**/*_test.rb']
t.verbose = false
end
desc 'run test suite with warn error mode'
desc('run test suite with warn error mode')
task :warn_test do
ENV['LIQUID_PARSER_MODE'] = 'warn'
Rake::Task['base_test'].invoke
@@ -25,7 +27,7 @@ task :rubocop do
end
end
desc 'runs test suite with both strict and lax parsers'
desc('runs test suite with both strict and lax parsers')
task :test do
ENV['LIQUID_PARSER_MODE'] = 'lax'
Rake::Task['base_test'].invoke
@@ -47,7 +49,7 @@ task :test do
end
end
task gem: :build
task(gem: :build)
task :build do
system "gem build liquid.gemspec"
end
@@ -94,7 +96,7 @@ namespace :memory_profile do
end
end
desc "Run example"
desc("Run example")
task :example do
ruby "-w -d -Ilib example/server/server.rb"
end

View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true
module ProductsFilter
def price(integer)
sprintf("$%.2d USD", integer / 100.0)
format("$%.2d USD", integer / 100.0)
end
def prettyprint(text)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
def do_GET(req, res)
handle(:get, req, res)
@@ -9,12 +11,12 @@ class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
private
def handle(type, req, res)
def handle(_type, req, res)
@request = req
@response = res
@request.path_info =~ /(\w+)\z/
@action = $1 || 'index'
@action = Regexp.last_match(1) || 'index'
@assigns = send(@action) if respond_to?(@action)
@response['Content-Type'] = "text/html"

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'webrick'
require 'rexml/document'
@@ -8,5 +10,5 @@ require_relative 'example_servlet'
# Setup webrick
server = WEBrick::HTTPServer.new(Port: ARGV[1] || 3000)
server.mount('/', Servlet)
trap("INT"){ server.shutdown }
trap("INT") { server.shutdown }
server.start

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
@@ -21,10 +23,10 @@
module Liquid
FilterSeparator = /\|/
ArgumentSeparator = ','.freeze
FilterArgumentSeparator = ':'.freeze
VariableAttributeSeparator = '.'.freeze
WhitespaceControl = '-'.freeze
ArgumentSeparator = ','
FilterArgumentSeparator = ':'
VariableAttributeSeparator = '.'
WhitespaceControl = '-'
TagStart = /\{\%/
TagEnd = /\%\}/
VariableSignature = /\(?[\w\-\.\[\]]\)?/
@@ -75,6 +77,8 @@ require 'liquid/utils'
require 'liquid/tokenizer'
require 'liquid/parse_context'
require 'liquid/partial_cache'
require 'liquid/usage'
require 'liquid/static_registers'
# Load all the tags of the standard library
#

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,
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))
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
@@ -52,7 +54,7 @@ module Liquid
def parse_body(body, tokens)
if parse_context.depth >= MAX_DEPTH
raise StackLevelError, "Nesting too deep".freeze
raise StackLevelError, "Nesting too deep"
end
parse_context.depth += 1
begin
@@ -61,7 +63,7 @@ module Liquid
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))
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
end
# this tag is not registered with the system

View File

@@ -1,11 +1,13 @@
# frozen_string_literal: true
module Liquid
class BlockBody
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
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
TAGSTART = "{%"
VARSTART = "{{"
attr_reader :nodelist
@@ -25,16 +27,16 @@ module Liquid
end
private def parse_for_liquid_tag(tokenizer, parse_context)
while token = tokenizer.shift
while (token = tokenizer.shift)
unless token.empty? || token =~ WhitespaceOrNothing
unless token =~ LiquidTagToken
# line isn't empty but didn't match tag syntax, yield and let the
# caller raise a syntax error
return yield token, token
end
tag_name = $1
markup = $2
unless tag = registered_tags[tag_name]
tag_name = Regexp.last_match(1)
markup = Regexp.last_match(2)
unless (tag = registered_tags[tag_name])
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed
return yield tag_name, markup
@@ -50,7 +52,7 @@ module Liquid
end
private def parse_for_document(tokenizer, parse_context, &block)
while token = tokenizer.shift
while (token = tokenizer.shift)
next if token.empty?
case
when token.start_with?(TAGSTART)
@@ -58,21 +60,21 @@ module Liquid
unless token =~ FullToken
raise_missing_tag_terminator(token, parse_context)
end
tag_name = $2
markup = $4
tag_name = Regexp.last_match(2)
markup = Regexp.last_match(4)
if parse_context.line_number
# newlines inside the tag should increase the line number,
# particularly important for multiline {% liquid %} tags
parse_context.line_number += $1.count("\n".freeze) + $3.count("\n".freeze)
parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n")
end
if tag_name == 'liquid'.freeze
if tag_name == 'liquid'
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true)
next parse_for_liquid_tag(liquid_tag_tokenizer, parse_context, &block)
end
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
@@ -101,7 +103,7 @@ module Liquid
def whitespace_handler(token, parse_context)
if token[2] == WhitespaceControl
previous_token = @nodelist.last
if previous_token.is_a? String
if previous_token.is_a?(String)
previous_token.rstrip!
end
end
@@ -113,14 +115,14 @@ module Liquid
end
def render(context)
render_to_output_buffer(context, '')
render_to_output_buffer(context, +'')
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
@@ -129,7 +131,7 @@ module Liquid
when Variable
render_node(context, output, node)
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
when Continue, Break
# If we get an Interrupt that means the block must stop processing. An
@@ -163,7 +165,7 @@ 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)
@@ -175,11 +177,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: TagEnd.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: VariableEnd.inspect)
end
def registered_tags

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Container for liquid nodes which conveniently wraps decision making logic
#
@@ -8,21 +10,21 @@ module Liquid
#
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|
'==' => ->(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
@@ -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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Context keeps the variable stack and resolves variables, as well as keywords
#
@@ -16,16 +18,17 @@ module Liquid
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
# rubocop:disable Metrics/ParameterLists
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_registers, static_environments)
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {})
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments)
end
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_registers = {}, static_environments = {})
@environments = [environments].flatten
@static_environments = [static_environments].flatten.map(&:freeze).freeze
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
@environments = [environments]
@environments.flatten!
@static_environments = [static_environments].flat_map(&:freeze).freeze
@scopes = [(outer_scope || {})]
@registers = registers
@static_registers = static_registers.freeze
@errors = []
@partial = false
@strict_variables = false
@@ -33,11 +36,9 @@ module Liquid
@base_scope_depth = 0
squash_instance_assigns_with_environments
@this_stack_used = false
self.exception_renderer = Template.default_exception_renderer
if rethrow_errors
self.exception_renderer = ->(e) { raise }
self.exception_renderer = ->(_e) { raise }
end
@interrupts = []
@@ -120,19 +121,11 @@ module Liquid
# end
#
# context['var] #=> nil
def stack(new_scope = nil)
old_stack_used = @this_stack_used
if new_scope
push(new_scope)
@this_stack_used = true
else
@this_stack_used = false
end
def stack(new_scope = {})
push(new_scope)
yield
ensure
pop if @this_stack_used
@this_stack_used = old_stack_used
pop
end
# Creates a new context inheriting resource limits, filters, environment etc.,
@@ -143,7 +136,7 @@ module Liquid
Context.build(
resource_limits: resource_limits,
static_environments: static_environments,
static_registers: static_registers
registers: StaticRegisters.new(registers)
).tap do |subcontext|
subcontext.base_scope_depth = base_scope_depth + 1
subcontext.exception_renderer = exception_renderer
@@ -160,10 +153,6 @@ module Liquid
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
def []=(key, value)
unless @this_stack_used
@this_stack_used = true
push({})
end
@scopes[0][key] = value
end
@@ -213,7 +202,7 @@ module Liquid
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
@@ -230,12 +219,16 @@ module Liquid
def try_variable_find_in_environments(key, raise_on_not_found:)
@environments.each do |environment|
found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
Usage.increment("environment_has_a_default_proc") if environment.respond_to?(:default_proc) && environment.default_proc
Usage.increment("environment_has_key_but_is_nil") if environment.respond_to?(:key?) && environment.key?(key) && found_variable.nil?
if !found_variable.nil? || @strict_variables && raise_on_not_found
return found_variable
end
end
@static_environments.each do |environment|
found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
Usage.increment("static_environment_has_a_default_proc") if environment.respond_to?(:default_proc) && environment.default_proc
Usage.increment("static_environment_has_key_but_is_nil") if environment.respond_to?(:key?) && environment.key?(key) && found_variable.nil?
if !found_variable.nil? || @strict_variables && raise_on_not_found
return found_variable
end
@@ -244,7 +237,7 @@ module Liquid
end
def check_overflow
raise StackLevelError, "Nesting too deep".freeze if overflow?
raise StackLevelError, "Nesting too deep" if overflow?
end
def overflow?

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Expression
class MethodLiteral
@@ -14,11 +16,11 @@ 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
@@ -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

View File

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

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.
#
@@ -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 %r{\A[^./][a-zA-Z0-9_/]+\z}.match?(template_path)
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)

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'yaml'
module Liquid
@@ -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

View File

@@ -1,10 +1,12 @@
# 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

View File

@@ -1,17 +1,19 @@
# 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 = /'[^\']*'/
@@ -31,16 +33,21 @@ 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))
[:comparison, t]
elsif (t = @ss.scan(SINGLE_STRING_LITERAL))
[:string, t]
elsif (t = @ss.scan(DOUBLE_STRING_LITERAL))
[:string, t]
elsif (t = @ss.scan(NUMBER_LITERAL))
[:number, t]
elsif (t = @ss.scan(IDENTIFIER))
[:id, t]
elsif (t = @ss.scan(DOTDOT))
[:dotdot, t]
else
c = @ss.getch
if s = SPECIALS[c]
if (s = SPECIALS[c])
[s, c]
else
raise SyntaxError, "Unexpected character #{c}"

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

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'liquid/profiler/hooks'
module Liquid

View File

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

View File

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

View File

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

View File

@@ -1,20 +1,22 @@
# 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+));)/
STRIP_HTML_BLOCKS = Regexp.union(
/<script.*?<\/script>/m,
%r{<script.*?</script>}m,
/<!--.*?-->/m,
/<style.*?<\/style>/m
%r{<style.*?</style>}m
)
STRIP_HTML_TAGS = /<.*?>/m
@@ -72,7 +74,7 @@ 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)
@@ -82,13 +84,13 @@ 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 +115,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 +123,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 +222,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 +252,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
@@ -276,7 +278,7 @@ module Liquid
def concat(input, array)
unless array.respond_to?(:to_ary)
raise ArgumentError.new("concat filter requires an array argument")
raise ArgumentError, "concat filter requires an array argument"
end
InputIterator.new(input).concat(array)
end
@@ -288,7 +290,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 +327,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 +421,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 +432,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)

View File

@@ -0,0 +1,36 @@
# 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,3 +1,5 @@
# frozen_string_literal: true
require 'set'
module Liquid
@@ -27,7 +29,7 @@ module Liquid
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(', ')}"
@@ -54,7 +56,7 @@ module Liquid
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

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Tag
attr_reader :nodelist, :tag_name, :line_number, :parse_context
@@ -33,7 +35,7 @@ module Liquid
end
def render(_context)
''.freeze
''
end
# For backwards compatibility with custom tags. In a future release, the semantics

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Assign sets a variable in your template.
#
@@ -11,7 +13,7 @@ module Liquid
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
def self.syntax_error_translation_key
"errors.syntax.assign".freeze
"errors.syntax.assign"
end
attr_reader :to, :from
@@ -19,10 +21,10 @@ module Liquid
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@to = $1
@from = Variable.new($2, options)
@to = Regexp.last_match(1)
@from = Variable.new(Regexp.last_match(2), options)
else
raise SyntaxError.new(options[:locale].t(self.class.syntax_error_translation_key))
raise SyntaxError, options[:locale].t(self.class.syntax_error_translation_key)
end
end
@@ -59,5 +61,5 @@ module Liquid
end
end
Template.register_tag('assign'.freeze, Assign)
Template.register_tag('assign', Assign)
end

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

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.
#
@@ -16,9 +18,9 @@ module Liquid
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@to = $1
@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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Case < Block
Syntax = /(#{QuotedFragment})/o
@@ -10,17 +12,15 @@ module Liquid
@blocks = []
if markup =~ Syntax
@left = Expression.parse($1)
@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
@@ -39,16 +39,14 @@ module Liquid
end
def render_to_output_buffer(context, output)
context.stack do
execute_else_block = true
execute_else_block = true
@blocks.each do |block|
if block.else?
block.attachment.render_to_output_buffer(context, output) if execute_else_block
elsif block.evaluate(context)
execute_else_block = false
block.attachment.render_to_output_buffer(context, output)
end
@blocks.each do |block|
if block.else?
block.attachment.render_to_output_buffer(context, output) if execute_else_block
elsif block.evaluate(context)
execute_else_block = false
block.attachment.render_to_output_buffer(context, output)
end
end
@@ -62,12 +60,12 @@ module Liquid
while markup
unless markup =~ WhenSyntax
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_when".freeze))
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_when")
end
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
@@ -75,7 +73,7 @@ module Liquid
def record_else_condition(markup)
unless markup.strip.empty?
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze))
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_else")
end
block = ElseCondition.new
@@ -90,5 +88,5 @@ module Liquid
end
end
Template.register_tag('case'.freeze, Case)
Template.register_tag('case', Case)
end

View File

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

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

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.
#
@@ -21,38 +23,36 @@ module Liquid
super
case markup
when NamedSyntax
@variables = variables_from_string($2)
@name = Expression.parse($1)
@variables = variables_from_string(Regexp.last_match(2))
@name = Expression.parse(Regexp.last_match(1))
when SimpleSyntax
@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
def render_to_output_buffer(context, output)
context.registers[:cycle] ||= {}
context.stack do
key = context.evaluate(@name)
iteration = context.registers[:cycle][key].to_i
key = context.evaluate(@name)
iteration = context.registers[:cycle][key].to_i
val = context.evaluate(@variables[iteration])
val = context.evaluate(@variables[iteration])
if val.is_a?(Array)
val = val.join
elsif !val.is_a?(String)
val = val.to_s
end
output << val
iteration += 1
iteration = 0 if iteration >= @variables.size
context.registers[:cycle][key] = iteration
if val.is_a?(Array)
val = val.join
elsif !val.is_a?(String)
val = val.to_s
end
output << val
iteration += 1
iteration = 0 if iteration >= @variables.size
context.registers[:cycle][key] = iteration
output
end
@@ -61,7 +61,7 @@ module Liquid
def variables_from_string(markup)
markup.split(',').collect do |var|
var =~ /\s*(#{QuotedFragment})\s*/o
$1 ? Expression.parse($1) : nil
Regexp.last_match(1) ? Expression.parse(Regexp.last_match(1)) : nil
end.compact
end

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

View File

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

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.
@@ -66,7 +68,7 @@ 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
@@ -86,31 +88,31 @@ module Liquid
def lax_parse(markup)
if markup =~ Syntax
@variable_name = $1
collection_name = $2
@reversed = !!$3
@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|
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)
@@ -162,7 +164,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 +172,10 @@ 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 +187,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 +213,5 @@ module Liquid
end
end
Template.register_tag('for'.freeze, For)
Template.register_tag('for', For)
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# If is the conditional block
#
@@ -19,7 +21,7 @@ module Liquid
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 ['elsif', 'else'].include?(tag)
push_block(tag, markup)
else
super
@@ -40,11 +42,9 @@ module Liquid
end
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
@blocks.each do |block|
if block.evaluate(context)
return block.attachment.render_to_output_buffer(context, output)
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)
@@ -66,17 +66,17 @@ module Liquid
def lax_parse(markup)
expressions = markup.scan(ExpressionsAndOperators)
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
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($1), $2, Expression.parse($3))
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
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.send(operator, condition)
condition = new_condition
end
@@ -94,7 +94,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 +104,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 +119,5 @@ module Liquid
end
end
Template.register_tag('if'.freeze, If)
Template.register_tag('if', If)
end

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
# Include allows templates to relate with other templates
#
@@ -23,8 +25,8 @@ module Liquid
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)
@@ -35,7 +37,7 @@ module Liquid
end
else
raise SyntaxError.new(options[:locale].t("errors.syntax.include".freeze))
raise SyntaxError, options[:locale].t("errors.syntax.include")
end
end
@@ -44,7 +46,7 @@ module Liquid
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 = PartialCache.load(
template_name,
@@ -52,7 +54,7 @@ module Liquid
parse_context: parse_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)
@@ -95,11 +97,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

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
@@ -29,5 +31,5 @@ module Liquid
end
end
Template.register_tag('increment'.freeze, Increment)
Template.register_tag('increment', Increment)
end

View File

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

View File

@@ -1,15 +1,17 @@
# frozen_string_literal: true
module Liquid
class Render < Tag
Syntax = /(#{QuotedString})#{QuotedFragment}*/o
SYNTAX = /(#{QuotedString})#{QuotedFragment}*/o
attr_reader :template_name_expr, :attributes
def initialize(tag_name, markup, options)
super
raise SyntaxError.new(options[:locale].t("errors.syntax.render".freeze)) unless markup =~ Syntax
raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX
template_name = $1
template_name = Regexp.last_match(1)
@template_name_expr = Expression.parse(template_name)
@@ -22,7 +24,7 @@ module Liquid
def render_to_output_buffer(context, output)
# Though we evaluate this here we will only ever parse it as a string literal.
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 = PartialCache.load(
template_name,
@@ -50,5 +52,5 @@ module Liquid
end
end
Template.register_tag('render'.freeze, Render)
Template.register_tag('render', Render)
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class TableRow < Block
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
@@ -7,33 +9,33 @@ module Liquid
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@variable_name = $1
@collection_name = Expression.parse($2)
@variable_name = Regexp.last_match(1)
@collection_name = Expression.parse(Regexp.last_match(2))
@attributes = {}
markup.scan(TagAttributes) 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
@@ -61,5 +63,5 @@ module Liquid
end
end
Template.register_tag('tablerow'.freeze, TableRow)
Template.register_tag('tablerow', TableRow)
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'if'
module Liquid
@@ -7,18 +9,16 @@ module Liquid
#
class Unless < If
def render_to_output_buffer(context, output)
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
# 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
# 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
# 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
end
@@ -26,5 +26,5 @@ module Liquid
end
end
Template.register_tag('unless'.freeze, Unless)
Template.register_tag('unless', Unless)
end

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
@@ -165,14 +167,14 @@ 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 }
c.exception_renderer = ->(_e) { raise }
end
c
@@ -208,7 +210,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 || +'')
end
rescue Liquid::MemoryError => e
context.handle_error(e)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module Liquid
class Tokenizer
attr_reader :line_number, :for_liquid_tag
@@ -10,7 +12,7 @@ module Liquid
end
def shift
token = @tokens.shift or return
(token = @tokens.shift) || return
if @line_number
@line_number += @for_liquid_tag ? 1 : token.count("\n")
@@ -29,7 +31,7 @@ module Liquid
tokens = @source.split(TemplateParser)
# 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

8
lib/liquid/usage.rb Normal file
View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Liquid
module Usage
def self.increment(name)
end
end
end

View File

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

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
@@ -43,11 +45,11 @@ module Liquid
@filters = []
return unless markup =~ MarkupWithQuotedFragment
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)
filters = Regexp.last_match(1).scan(FilterParser)
filters.each do |f|
next unless f =~ /\w+/
filtername = Regexp.last_match(0)
@@ -108,7 +110,7 @@ module Liquid
filter_args = []
keyword_args = nil
unparsed_args.each do |a|
if matches = a.match(JustTagAttributes)
if (matches = a.match(JustTagAttributes))
keyword_args ||= {}
keyword_args[matches[1]] = Expression.parse(matches[2])
else
@@ -121,7 +123,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|

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
COMMAND_METHODS = ['size', 'first', 'last'].freeze
attr_reader :name, :lookups
@@ -14,7 +16,7 @@ module Liquid
name = lookups.shift
if name =~ SQUARE_BRACKETED
name = Expression.parse($1)
name = Expression.parse(Regexp.last_match(1))
end
@name = name
@@ -24,7 +26,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

View File

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

View File

@@ -1,7 +1,8 @@
# encoding: utf-8
# frozen_string_literal: true
lib = File.expand_path('../lib/', __FILE__)
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "liquid/version"
@@ -26,6 +27,6 @@ Gem::Specification.new do |s|
s.require_path = "lib"
s.add_development_dependency 'rake', '~> 11.3'
s.add_development_dependency 'minitest'
s.add_development_dependency('rake', '~> 11.3')
s.add_development_dependency('minitest')
end

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'stackprof'
require_relative 'theme_runner'
@@ -13,7 +15,7 @@ profiler.run
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|
StackProf::Report.new(results).print_graphviz(nil, f)
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
class CommentForm < Liquid::Block
Syntax = /(#{Liquid::VariableSignature}+)/
@@ -5,10 +7,10 @@ class CommentForm < Liquid::Block
super
if markup =~ Syntax
@variable_name = $1
@variable_name = Regexp.last_match(1)
@attributes = {}
else
raise SyntaxError.new("Syntax Error in 'comment_form' - Valid syntax: comment_form [article]")
raise SyntaxError, "Syntax Error in 'comment_form' - Valid syntax: comment_form [article]"
end
end
@@ -20,8 +22,8 @@ class CommentForm < Liquid::Block
'posted_successfully?' => context.registers[:posted_successfully],
'errors' => context['comment.errors'],
'author' => context['comment.author'],
'email' => context['comment.email'],
'body' => context['comment.body']
'email' => context['comment.email'],
'body' => context['comment.body'],
}
output << wrap_in_form(article, render_all(@nodelist, context, output))

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'yaml'
module Database
@@ -16,9 +18,10 @@ module Database
end
# key the tables by handles, as this is how liquid expects it.
db = db.inject({}) do |assigns, (key, values)|
assigns[key] = values.inject({}) { |h, v| h[v['handle']] = v; h; }
assigns
db = db.each_with_object({}) do |(key, values), assigns|
assigns[key] = values.each_with_object({}) do |v, h|
h[v['handle']] = v
end
end
# Some standard direct accessors so that the specialized templates
@@ -29,9 +32,9 @@ module Database
db['article'] = db['blog']['articles'].first
db['cart'] = {
'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'] },
'items' => db['line_items'].values
'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'] },
'items' => db['line_items'].values,
}
db
@@ -40,6 +43,6 @@ module Database
end
if __FILE__ == $PROGRAM_NAME
p Database.tables['collections']['frontpage'].keys
p(Database.tables['collections']['frontpage'].keys)
# p Database.tables['blog']['articles']
end

View File

@@ -1,7 +1,9 @@
# frozen_string_literal: true
require 'json'
module JsonFilter
def json(object)
JSON.dump(object.reject { |k, v| k == "collections" })
JSON.dump(object.reject { |k, _v| k == "collections" })
end
end

View File

@@ -1,4 +1,6 @@
$:.unshift __dir__ + '/../../lib'
# frozen_string_literal: true
$LOAD_PATH.unshift(__dir__ + '/../../lib')
require_relative '../../lib/liquid'
require_relative 'comment_form'
@@ -9,11 +11,11 @@ require_relative 'shop_filter'
require_relative 'tag_filter'
require_relative 'weight_filter'
Liquid::Template.register_tag 'paginate', Paginate
Liquid::Template.register_tag 'form', CommentForm
Liquid::Template.register_tag('paginate', Paginate)
Liquid::Template.register_tag('form', CommentForm)
Liquid::Template.register_filter JsonFilter
Liquid::Template.register_filter MoneyFilter
Liquid::Template.register_filter WeightFilter
Liquid::Template.register_filter ShopFilter
Liquid::Template.register_filter TagFilter
Liquid::Template.register_filter(JsonFilter)
Liquid::Template.register_filter(MoneyFilter)
Liquid::Template.register_filter(WeightFilter)
Liquid::Template.register_filter(ShopFilter)
Liquid::Template.register_filter(TagFilter)

View File

@@ -1,12 +1,14 @@
# frozen_string_literal: true
module MoneyFilter
def money_with_currency(money)
return '' if money.nil?
sprintf("$ %.2f USD", money / 100.0)
format("$ %.2f USD", money / 100.0)
end
def money(money)
return '' if money.nil?
sprintf("$ %.2f", money / 100.0)
format("$ %.2f", money / 100.0)
end
private

View File

@@ -1,13 +1,15 @@
# frozen_string_literal: true
class Paginate < Liquid::Block
Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/
Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/
def initialize(tag_name, markup, options)
super
if markup =~ Syntax
@collection_name = $1
@page_size = if $2
$3.to_i
@collection_name = Regexp.last_match(1)
@page_size = if Regexp.last_match(2)
Regexp.last_match(3).to_i
else
20
end
@@ -17,7 +19,7 @@ class Paginate < Liquid::Block
@attributes[key] = value
end
else
raise SyntaxError.new("Syntax Error in tag 'paginate' - Valid syntax: paginate [collection] by number")
raise SyntaxError, "Syntax Error in tag 'paginate' - Valid syntax: paginate [collection] by number"
end
end
@@ -25,19 +27,19 @@ class Paginate < Liquid::Block
@context = context
context.stack do
current_page = context['current_page'].to_i
current_page = context['current_page'].to_i
pagination = {
'page_size' => @page_size,
'current_page' => 5,
'current_offset' => @page_size * 5
'page_size' => @page_size,
'current_page' => 5,
'current_offset' => @page_size * 5,
}
context['paginate'] = pagination
collection_size = context[@collection_name].size
collection_size = context[@collection_name].size
raise ArgumentError.new("Cannot paginate array '#{@collection_name}'. Not found.") if collection_size.nil?
raise ArgumentError, "Cannot paginate array '#{@collection_name}'. Not found." if collection_size.nil?
page_count = (collection_size.to_f / @page_size.to_f).to_f.ceil + 1

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module ShopFilter
def asset_url(input)
"/files/1/[shop_id]/[shop_id]/assets/#{input}"
@@ -52,7 +54,7 @@ module ShopFilter
end
def product_img_url(url, style = 'small')
unless url =~ /\Aproducts\/([\w\-\_]+)\.(\w{2,4})/
unless url =~ %r{\Aproducts/([\w\-\_]+)\.(\w{2,4})}
raise ArgumentError, 'filter "size" can only be called on product images'
end
@@ -60,7 +62,7 @@ module ShopFilter
when 'original'
return '/files/shops/random_number/' + url
when 'grande', 'large', 'medium', 'compact', 'small', 'thumb', 'icon'
"/files/shops/random_number/products/#{$1}_#{style}.#{$2}"
"/files/shops/random_number/products/#{Regexp.last_match(1)}_#{style}.#{Regexp.last_match(2)}"
else
raise ArgumentError, 'valid parameters for filter "size" are: original, grande, large, medium, compact, small, thumb and icon '
end
@@ -70,16 +72,14 @@ module ShopFilter
html = []
html << %(<span class="prev">#{link_to(paginate['previous']['title'], paginate['previous']['url'])}</span>) if paginate['previous']
for part in paginate['parts']
if part['is_link']
html << %(<span class="page">#{link_to(part['title'], part['url'])}</span>)
paginate['parts'].each do |part|
html << if part['is_link']
%(<span class="page">#{link_to(part['title'], part['url'])}</span>)
elsif part['title'].to_i == paginate['current_page'].to_i
html << %(<span class="page current">#{part['title']}</span>)
%(<span class="page current">#{part['title']}</span>)
else
html << %(<span class="deco">#{part['title']}</span>)
%(<span class="deco">#{part['title']}</span>)
end
end
html << %(<span class="next">#{link_to(paginate['next']['title'], paginate['next']['url'])}</span>) if paginate['next']

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module TagFilter
def link_to_tag(label, tag)
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tag}\">#{label}</a>"
@@ -13,11 +15,11 @@ module TagFilter
def link_to_add_tag(label, tag)
tags = (@context['current_tags'] + [tag]).uniq
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join("+")}\">#{label}</a>"
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join('+')}\">#{label}</a>"
end
def link_to_remove_tag(label, tag)
tags = (@context['current_tags'] - [tag]).uniq
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join("+")}\">#{label}</a>"
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join('+')}\">#{label}</a>"
end
end

View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true
module WeightFilter
def weight(grams)
sprintf("%.2f", grams / 1000)
format("%.2f", grams / 1000)
end
def weight_with_unit(grams)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
# This profiler run simulates Shopify.
# 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.
@@ -31,7 +33,7 @@ class ThemeRunner
{
liquid: File.read(test),
layout: (File.file?(theme_path) ? File.read(theme_path) : nil),
template_name: test
template_name: test,
}
end.compact

View File

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

View File

@@ -1,7 +1,9 @@
# frozen_string_literal: true
require 'test_helper'
class FoobarTag < Liquid::Tag
def render_to_output_buffer(context, output)
def render_to_output_buffer(_context, output)
output << ' '
output
end

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class ContextDrop < Liquid::Drop
@@ -31,7 +33,7 @@ class ProductDrop < Liquid::Drop
class CatchallDrop < Liquid::Drop
def liquid_method_missing(method)
'catchall_method: ' << method.to_s
"catchall_method: #{method}"
end
end
@@ -48,7 +50,7 @@ class ProductDrop < Liquid::Drop
end
def user_input
"foo".taint
(+"foo").taint
end
protected
@@ -201,9 +203,9 @@ class DropsTest < Minitest::Test
end
def test_scope_though_proc
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 '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 '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 '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
def test_scope_with_assigns
@@ -241,7 +243,7 @@ class DropsTest < Minitest::Test
end
def test_some_enumerable_methods_still_get_invoked
[ :count, :max ].each do |method|
[:count, :max].each do |method|
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
@@ -250,7 +252,7 @@ class DropsTest < Minitest::Test
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)
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
@@ -270,4 +272,11 @@ class DropsTest < Minitest::Test
assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.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 # DropsTest

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class ErrorHandlingTest < Minitest::Test
@@ -83,15 +85,14 @@ class ErrorHandlingTest < Minitest::Test
def test_with_line_numbers_adds_numbers_to_parser_errors
err = assert_raises(SyntaxError) do
Liquid::Template.parse(%q(
Liquid::Template.parse('
foobar
{% "cat" | foobar %}
bla
),
line_numbers: true
)
',
line_numbers: true)
end
assert_match(/Liquid syntax error \(line 4\)/, err.message)
@@ -99,15 +100,14 @@ class ErrorHandlingTest < Minitest::Test
def test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespace_trim
err = assert_raises(SyntaxError) do
Liquid::Template.parse(%q(
Liquid::Template.parse('
foobar
{%- "cat" | foobar -%}
bla
),
line_numbers: true
)
',
line_numbers: true)
end
assert_match(/Liquid syntax error \(line 4\)/, err.message)
@@ -122,8 +122,7 @@ class ErrorHandlingTest < Minitest::Test
bla
',
error_mode: :warn,
line_numbers: true
)
line_numbers: true)
assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
template.warnings.map(&:message)
@@ -139,8 +138,7 @@ class ErrorHandlingTest < Minitest::Test
bla
',
error_mode: :strict,
line_numbers: true
)
line_numbers: true)
end
assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
@@ -157,8 +155,7 @@ class ErrorHandlingTest < Minitest::Test
bla
',
line_numbers: true
)
line_numbers: true)
end
assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
@@ -205,7 +202,7 @@ class ErrorHandlingTest < Minitest::Test
def test_default_exception_renderer_with_internal_error
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
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 [Liquid::InternalError], template.errors.map(&:class)
@@ -214,10 +211,13 @@ class ErrorHandlingTest < Minitest::Test
def test_setting_default_exception_renderer
old_exception_renderer = Liquid::Template.default_exception_renderer
exceptions = []
Liquid::Template.default_exception_renderer = ->(e) { exceptions << e; '' }
Liquid::Template.default_exception_renderer = ->(e) {
exceptions << e
''
}
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 [Liquid::ArgumentError], template.errors.map(&:class)
@@ -228,7 +228,10 @@ class ErrorHandlingTest < Minitest::Test
def test_exception_renderer_exposing_non_liquid_error
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
exceptions = []
handler = ->(e) { exceptions << e; e.cause }
handler = ->(e) {
exceptions << e
e.cause
}
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
@@ -239,7 +242,7 @@ class ErrorHandlingTest < Minitest::Test
end
class TestFileSystem
def read_template_file(template_path)
def read_template_file(_template_path)
"{{ errors.argument_error }}"
end
end

View File

@@ -1,24 +1,26 @@
# frozen_string_literal: true
require 'test_helper'
module MoneyFilter
def money(input)
sprintf(' %d$ ', input)
format(' %d$ ', input)
end
def money_with_underscore(input)
sprintf(' %d$ ', input)
format(' %d$ ', input)
end
end
module CanadianMoneyFilter
def money(input)
sprintf(' %d$ CAD ', input)
format(' %d$ CAD ', input)
end
end
module SubstituteFilter
def substitute(input, params = {})
input.gsub(/%\{(\w+)\}/) { |match| params[$1] }
input.gsub(/%\{(\w+)\}/) { |_match| params[Regexp.last_match(1)] }
end
end
@@ -26,7 +28,7 @@ class FiltersTest < Minitest::Test
include Liquid
module OverrideObjectMethodFilter
def tap(input)
def tap(_input)
"tap overridden"
end
end
@@ -149,7 +151,7 @@ class FiltersTest < Minitest::Test
assert_equal "tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter])
# 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

View File

@@ -1,15 +1,17 @@
# frozen_string_literal: true
require 'test_helper'
class HashOrderingTest < Minitest::Test
module MoneyFilter
def money(input)
sprintf(' %d$ ', input)
format(' %d$ ', input)
end
end
module CanadianMoneyFilter
def money(input)
sprintf(' %d$ CAD ', input)
format(' %d$ CAD ', input)
end
end

View File

@@ -1,7 +1,9 @@
# frozen_string_literal: true
require 'test_helper'
module FunnyFilter
def make_funny(input)
def make_funny(_input)
'LOL'
end
@@ -32,7 +34,7 @@ class OutputTest < Minitest::Test
def setup
@assigns = {
'best_cars' => 'bmw',
'car' => { 'bmw' => 'good', 'gm' => 'bad' }
'car' => { 'bmw' => 'good', 'gm' => 'bad' },
}
end

View File

@@ -227,7 +227,7 @@ class ParseTreeVisitorTest < Minitest::Test
[[nil, [
[nil, [[nil, [["other", []]]]]],
["test", []],
["xs", []]
["xs", []],
]]],
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
)

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class RenderProfilingTest < Minitest::Test
@@ -128,7 +130,7 @@ class RenderProfilingTest < Minitest::Test
t.render!
timing_count = 0
t.profiler.each do |timing|
t.profiler.each do |_timing|
timing_count += 1
end
@@ -145,7 +147,7 @@ class RenderProfilingTest < Minitest::Test
def test_profiling_marks_children_of_for_blocks
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
# Will profile each invocation of the for block

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
module SecurityFilter

View File

@@ -1,4 +1,5 @@
# encoding: utf-8
# frozen_string_literal: true
require 'test_helper'
@@ -17,7 +18,7 @@ class TestThing
"woot: #{@foo}"
end
def [](whatever)
def [](_whatever)
to_s
end
@@ -37,7 +38,7 @@ class TestEnumerable < Liquid::Drop
include Enumerable
def each(&block)
[ { "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 } ].each(&block)
[{ "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 }].each(&block)
end
end
@@ -208,14 +209,14 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "beta" },
{ "price" => 1, "handle" => "gamma" },
{ "handle" => "delta" },
{ "price" => 2, "handle" => "epsilon" }
{ "price" => 2, "handle" => "epsilon" },
]
expectation = [
{ "price" => 1, "handle" => "gamma" },
{ "price" => 2, "handle" => "epsilon" },
{ "price" => 4, "handle" => "alpha" },
{ "handle" => "delta" },
{ "handle" => "beta" }
{ "handle" => "beta" },
]
assert_equal expectation, @filters.sort(input, "price")
end
@@ -236,14 +237,14 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "beta" },
{ "price" => "1", "handle" => "gamma" },
{ "handle" => "delta" },
{ "price" => 2, "handle" => "epsilon" }
{ "price" => 2, "handle" => "epsilon" },
]
expectation = [
{ "price" => "1", "handle" => "gamma" },
{ "price" => 2, "handle" => "epsilon" },
{ "price" => "4", "handle" => "alpha" },
{ "handle" => "delta" },
{ "handle" => "beta" }
{ "handle" => "beta" },
]
assert_equal expectation, @filters.sort_natural(input, "price")
end
@@ -256,7 +257,7 @@ class StandardFiltersTest < Minitest::Test
{ "fake" => "t" },
{ "key" => "a" },
{ "key" => "b" },
{ "key" => "c" }
{ "key" => "c" },
]
expectation = [
{ "key" => "a" },
@@ -265,7 +266,7 @@ class StandardFiltersTest < Minitest::Test
{ "key" => "X" },
{ "key" => "Y" },
{ "key" => "Z" },
{ "fake" => "t" }
{ "fake" => "t" },
]
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"])
@@ -279,7 +280,7 @@ class StandardFiltersTest < Minitest::Test
foo = [
[1],
[2],
[3]
[3],
]
assert_raises Liquid::ArgumentError do
@@ -295,7 +296,7 @@ class StandardFiltersTest < Minitest::Test
foo = [
[1],
[2],
[3]
[3],
]
assert_raises Liquid::ArgumentError do
@@ -304,7 +305,7 @@ class StandardFiltersTest < Minitest::Test
end
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
def test_numerical_vs_lexicographical_sort
@@ -330,7 +331,7 @@ class StandardFiltersTest < Minitest::Test
foo = [
[1],
[2],
[3]
[3],
]
assert_raises Liquid::ArgumentError do
@@ -346,7 +347,7 @@ class StandardFiltersTest < Minitest::Test
foo = [
[1],
[2],
[3]
[3],
]
assert_raises Liquid::ArgumentError do
@@ -380,7 +381,7 @@ class StandardFiltersTest < Minitest::Test
def test_map_on_hashes
assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}',
"thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] }
"thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] }
end
def test_legacy_map_on_hashes_with_dynamic_key
@@ -397,7 +398,7 @@ class StandardFiltersTest < Minitest::Test
def test_map_over_proc
drop = TestDrop.new
p = proc{ drop }
p = proc { drop }
templ = '{{ procs | map: "test" }}'
assert_template_result "testfoo", templ, "procs" => [p]
end
@@ -405,10 +406,10 @@ class StandardFiltersTest < Minitest::Test
def test_map_over_drops_returning_procs
drops = [
{
"proc" => ->{ "foo" },
"proc" => -> { "foo" },
},
{
"proc" => ->{ "bar" },
"proc" => -> { "bar" },
},
]
templ = '{{ drops | map: "proc" }}'
@@ -423,7 +424,7 @@ class StandardFiltersTest < Minitest::Test
foo = [
[1],
[2],
[3]
[3],
]
assert_raises Liquid::ArgumentError do
@@ -435,7 +436,7 @@ class StandardFiltersTest < Minitest::Test
foo = [
[1],
[2],
[3]
[3],
]
assert_raises Liquid::ArgumentError do
@filters.map(foo, nil)
@@ -697,12 +698,12 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "alpha", "ok" => true },
{ "handle" => "beta", "ok" => false },
{ "handle" => "gamma", "ok" => false },
{ "handle" => "delta", "ok" => true }
{ "handle" => "delta", "ok" => true },
]
expectation = [
{ "handle" => "alpha", "ok" => true },
{ "handle" => "delta", "ok" => true }
{ "handle" => "delta", "ok" => true },
]
assert_equal expectation, @filters.where(input, "ok", true)
@@ -714,12 +715,12 @@ class StandardFiltersTest < Minitest::Test
{ "handle" => "alpha", "ok" => true },
{ "handle" => "beta" },
{ "handle" => "gamma" },
{ "handle" => "delta", "ok" => true }
{ "handle" => "delta", "ok" => true },
]
expectation = [
{ "handle" => "alpha", "ok" => true },
{ "handle" => "delta", "ok" => true }
{ "handle" => "delta", "ok" => true },
]
assert_equal expectation, @filters.where(input, "ok", true)
@@ -740,7 +741,7 @@ class StandardFiltersTest < Minitest::Test
input = [
{ "message" => "Bonjour!", "language" => "French" },
{ "message" => "Hello!", "language" => "English" },
{ "message" => "Hallo!", "language" => "German" }
{ "message" => "Hallo!", "language" => "German" },
]
assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")
@@ -758,7 +759,7 @@ class StandardFiltersTest < Minitest::Test
{ "foo" => false },
{ "foo" => true },
{ "foo" => "for sure" },
{ "bar" => true }
{ "bar" => true },
]
assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")

View File

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

View File

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

View File

@@ -1,10 +1,12 @@
# frozen_string_literal: true
require 'test_helper'
class EchoTest < Minitest::Test
include Liquid
def test_echo_outputs_its_input
assert_template_result('BAR', <<~LIQUID, { 'variable-name' => 'bar' })
assert_template_result('BAR', <<~LIQUID, 'variable-name' => 'bar')
{%- echo variable-name | upcase -%}
LIQUID
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class ThingWithValue < Liquid::Drop
@@ -23,16 +25,16 @@ class ForTagTest < Minitest::Test
yo
HERE
template = <<HERE
{%for item in array%}
yo
{%endfor%}
HERE
template = <<~HERE
{%for item in array%}
yo
{%endfor%}
HERE
assert_template_result(expected, template, 'array' => [1, 2, 3])
end
def test_for_reversed
assigns = { 'array' => [ 1, 2, 3] }
assigns = { 'array' => [1, 2, 3] }
assert_template_result('321', '{%for item in array reversed %}{{item}}{%endfor%}', assigns)
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class IfElseTagTest < Minitest::Test
@@ -132,7 +134,7 @@ class IfElseTagTest < Minitest::Test
end
def test_syntax_error_no_variable
assert_raises(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}') }
assert_raises(SyntaxError) { assert_template_result('', '{% if jerry == 1 %}') }
end
def test_syntax_error_no_expression

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class TestFileSystem
@@ -40,14 +42,14 @@ class TestFileSystem
end
class OtherFileSystem
def read_template_file(template_path)
def read_template_file(_template_path)
'from OtherFileSystem'
end
end
class CountingFileSystem
attr_reader :count
def read_template_file(template_path)
def read_template_file(_template_path)
@count ||= 0
@count += 1
'from CountingFileSystem'
@@ -59,14 +61,14 @@ class CustomInclude < Liquid::Tag
def initialize(tag_name, markup, tokens)
markup =~ Syntax
@template_name = $1
@template_name = Regexp.last_match(1)
super
end
def parse(tokens)
end
def render_to_output_buffer(context, output)
def render_to_output_buffer(_context, output)
output << @template_name[1..-2]
output
end
@@ -86,7 +88,7 @@ class IncludeTagTest < Minitest::Test
def test_include_tag_with
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_default_name
@@ -96,7 +98,7 @@ class IncludeTagTest < Minitest::Test
def test_include_tag_for
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
def test_include_tag_with_local_variables
@@ -134,7 +136,7 @@ class IncludeTagTest < Minitest::Test
def test_recursively_included_template_does_not_produce_endless_loop
infinite_file_system = Class.new do
def read_template_file(template_path)
def read_template_file(_template_path)
"-{% include 'loop' %}"
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class IncrementTagTest < Minitest::Test
@@ -13,11 +15,11 @@ class IncrementTagTest < Minitest::Test
end
def test_dec
assert_template_result('9', '{%decrement port %}', { 'port' => 10 })
assert_template_result('9', '{%decrement port %}', 'port' => 10)
assert_template_result('-1 -2', '{%decrement port %} {%decrement port%}', {})
assert_template_result('1 5 2 2 5',
'{%increment port %} {%increment starboard%} ' \
'{%increment port %} {%decrement port%} ' \
'{%decrement starboard %}', { 'port' => 1, 'starboard' => 5 })
'{%decrement starboard %}', 'port' => 1, 'starboard' => 5)
end
end

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