Compare commits

...

15 Commits

Author SHA1 Message Date
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
7b309dc75d Merge pull request #1135 from Shopify/fix-failing-rubocop
Resolve failing rubocop issues
2019-08-29 01:11:25 +10:00
Mike Angell
8f68cffdf1 Resolve failing rubocop issues 2019-08-29 00:45:38 +10:00
Mike Angell
dd27d0fd1d Merge pull request #1133 from Shopify/liquid-tag-fixes
Bugfix for new Liquid tag
2019-08-29 00:36:13 +10:00
Mike Angell
7a26e6b3d8 Merge pull request #1131 from Shopify/bump-ruby-2-4
Rubocop upgrade, Ruby 2.4 minimum and TruffleRuby
2019-08-29 00:33:42 +10:00
Mike Angell
cf4e77ab0c Merge branch 'master' into bump-ruby-2-4 2019-08-29 00:24:45 +10:00
Mike Angell
7bae55dd39 Bugfix for new Liquid tag 2019-08-28 23:39:19 +10:00
Tobias Lütke
0ce8aef229 Merge pull request #1103 from ashmaroli/ci-profile-memory
Add a CI job to profile memory usage of commit
2019-08-27 15:11:55 -04:00
Tobias Lütke
6eab595fae Merge pull request #1086 from Shopify/liquid-tag
Add {% liquid %} and {% echo %} tags
2019-08-27 15:10:20 -04:00
Mike Angell
b16b109a80 Bump Minimum version to 2.4 and bump Rubocop 2019-08-28 00:31:44 +10:00
Ashwin Maroli
ab698191b9 Add a CI job to profile memory usage of commit 2019-05-17 22:47:05 +05:30
Justin Li
7dc488a73b Simplifications from review 2019-04-09 15:19:47 -04:00
Justin Li
e6ed804ca5 Fix line number tracking after a non-empty blank token 2019-04-08 18:43:09 -04:00
Justin Li
951abb67ee Remove {% local %} tag 2019-04-08 18:34:39 -04:00
Justin Li
8d1cd41453 Add {% liquid %}, {% echo %}, and {% local %} tags 2019-04-01 20:08:38 -04:00
16 changed files with 1602 additions and 307 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,132 +1,16 @@
inherit_from:
- https://shopify.github.io/ruby-style-guide/rubocop.yml
- .rubocop_todo.yml
- ./.rubocop_todo.yml
require: rubocop-performance
Performance:
Enabled: true
AllCops:
Exclude:
- 'performance/shopify/*'
- '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
Layout/IndentHash:
EnforcedStyle: consistent
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
Performance/Count:
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
- 'vendor/bundle/**/*'
Naming/MethodName:
Exclude:
- 'example/server/liquid_servlet.rb'
- 'example/server/liquid_servlet.rb'

View File

@@ -1,23 +1,37 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-04-22 19:11:24 -0400 using RuboCop version 0.53.0.
# on 2019-08-29 12:16: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
# Offense count: 13
# Cop supports --auto-correct.
# Configuration parameters: Include, TreatCommentsAsGroupSeparators.
# Include: **/*.gemspec
Gemspec/OrderedDependencies:
# 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:
- 'liquid.gemspec'
- 'lib/liquid/condition.rb'
- 'lib/liquid/expression.rb'
- 'performance/shopify/comment_form.rb'
- 'performance/shopify/database.rb'
- 'performance/shopify/paginate.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
Layout/ExtraSpacing:
Exclude:
- 'performance/shopify/paginate.rb'
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent
# SupportedStyles: squiggly, active_support, powerpack, unindent
Layout/IndentHeredoc:
Exclude:
- 'test/integration/tags/for_tag_test.rb'
@@ -32,6 +46,62 @@ Layout/MultilineMethodCallBraceLayout:
- 'test/integration/error_handling_test.rb'
- 'test/unit/strainer_unit_test.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: AllowForAlignment.
Layout/SpaceAroundOperators:
Exclude:
- 'lib/liquid/condition.rb'
- 'performance/shopify/paginate.rb'
# Offense count: 9
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
# SupportedStyles: space, no_space
# SupportedStylesForEmptyBraces: space, no_space
Layout/SpaceBeforeBlockBraces:
Exclude:
- 'example/server/server.rb'
- 'lib/liquid/variable.rb'
- 'test/integration/drop_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/tags/if_else_tag_test.rb'
# Offense count: 19
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets.
# SupportedStyles: space, no_space, compact
# SupportedStylesForEmptyBrackets: space, no_space
Layout/SpaceInsideArrayLiteralBrackets:
Exclude:
- 'test/integration/drop_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/tags/for_tag_test.rb'
- 'test/integration/tags/include_tag_test.rb'
- 'test/integration/tags/standard_tag_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 2
Lint/AmbiguousOperator:
Exclude:
- 'test/unit/condition_unit_test.rb'
# Offense count: 16
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Exclude:
- 'lib/liquid/block_body.rb'
- 'lib/liquid/lexer.rb'
- 'lib/liquid/standardfilters.rb'
- 'lib/liquid/tags/for.rb'
- 'lib/liquid/tags/if.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/tags/raw.rb'
- 'lib/liquid/variable.rb'
- 'performance/profile.rb'
- 'test/test_helper.rb'
- 'test/unit/tokenizer_unit_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
@@ -40,145 +110,184 @@ Lint/InheritException:
Exclude:
- 'lib/liquid/interrupts.rb'
# Offense count: 10
# Cop supports --auto-correct.
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
Lint/UnusedBlockArgument:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/context.rb'
- 'lib/liquid/document.rb'
- 'lib/liquid/parse_context.rb'
- 'lib/liquid/template.rb'
- 'performance/shopify/json_filter.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/render_profiling_test.rb'
- 'test/integration/variable_test.rb'
- 'test/unit/condition_unit_test.rb'
# Offense count: 12
# Cop supports --auto-correct.
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
Lint/UnusedMethodArgument:
Exclude:
- 'example/server/liquid_servlet.rb'
- 'test/integration/blank_test.rb'
- 'test/integration/error_handling_test.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/output_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/tags/include_tag_test.rb'
- 'test/unit/strainer_unit_test.rb'
# Offense count: 2
Lint/UselessAssignment:
Exclude:
- 'performance/shopify/database.rb'
# Offense count: 1
# Configuration parameters: CheckForMethodsWithNoSideEffects.
Lint/Void:
Exclude:
- 'lib/liquid/parse_context.rb'
# Offense count: 53
Metrics/AbcSize:
Max: 56
# Offense count: 95
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 294
# Offense count: 12
Metrics/CyclomaticComplexity:
Max: 13
# Offense count: 112
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 38
# Offense count: 8
Metrics/PerceivedComplexity:
Max: 11
# Offense count: 52
# Configuration parameters: Blacklist.
# Blacklist: END, (?-mix:EO[A-Z]{1})
Naming/HeredocDelimiterNaming:
# Offense count: 44
Naming/ConstantName:
Exclude:
- 'test/integration/assign_test.rb'
- 'test/integration/capture_test.rb'
- 'test/integration/trim_mode_test.rb'
# Offense count: 23
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
# AllowedNames: io, id
Naming/UncommunicativeMethodParamName:
Exclude:
- 'example/server/example_servlet.rb'
- 'lib/liquid/condition.rb'
- 'lib/liquid/context.rb'
- 'lib/liquid/standardfilters.rb'
- '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/utils.rb'
- 'lib/liquid/tags/include.rb'
- 'lib/liquid/tags/raw.rb'
- 'lib/liquid/tags/table_row.rb'
- 'lib/liquid/variable.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/tags/for_tag_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/condition_unit_test.rb'
- 'performance/shopify/comment_form.rb'
- 'performance/shopify/paginate.rb'
- 'test/integration/tags/include_tag_test.rb'
# Offense count: 12
# Offense count: 2
# Configuration parameters: .
# SupportedStyles: snake_case, camelCase
Naming/MethodName:
EnforcedStyle: snake_case
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: prefer_alias, prefer_alias_method
Style/Alias:
# SupportedStyles: always, conditionals
Style/AndOr:
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'
- 'lib/liquid/tags/table_row.rb'
- 'lib/liquid/tokenizer.rb'
# Offense count: 22
Style/CommentedKeyword:
Enabled: false
# Offense count: 40
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: braces, no_braces, context_dependent
Style/BracesAroundHashParameters:
Exclude:
- 'test/integration/error_handling_test.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/render_profiling_test.rb'
- 'test/integration/standard_filter_test.rb'
- 'test/integration/tags/echo_test.rb'
- 'test/integration/tags/increment_tag_test.rb'
- 'test/integration/tags/standard_tag_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/condition_unit_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 1
# Offense count: 5
Style/ClassVars:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/strainer.rb'
- 'lib/liquid/template.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
# SupportedStyles: assign_to_condition, assign_inside_condition
Style/ConditionalAssignment:
Exclude:
- 'lib/liquid/errors.rb'
- 'performance/shopify/shop_filter.rb'
# Offense count: 1
# Configuration parameters: AllowCoercion.
Style/DateTime:
Exclude:
- 'test/unit/context_unit_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/EachWithObject:
Exclude:
- 'performance/shopify/database.rb'
# Offense count: 1
# Cop supports --auto-correct.
Style/EmptyCaseCondition:
Exclude:
- 'lib/liquid/block_body.rb'
- 'lib/liquid/lexer.rb'
# Offense count: 5
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: compact, expanded
Style/EmptyMethod:
# SupportedStyles: each, for
Style/For:
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'
- 'performance/shopify/shop_filter.rb'
# Offense count: 3
# Offense count: 9
# 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:
# SupportedStyles: format, sprintf, percent
Style/FormatString:
Exclude:
- 'example/server/example_servlet.rb'
- 'performance/shopify/money_filter.rb'
- 'performance/shopify/weight_filter.rb'
- 'test/integration/filter_test.rb'
- 'test/integration/hash_ordering_test.rb'
# Offense count: 14
# Configuration parameters: MinBodyLength.
Style/GuardClause:
# Offense count: 115
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, never
Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 30
# Cop supports --auto-correct.
# Configuration parameters: IgnoreMacros, IgnoredMethods, IncludedMacros, AllowParenthesesInMultilineCall, AllowParenthesesInChaining, AllowParenthesesInCamelCaseMethod, EnforcedStyle.
# SupportedStyles: require_parentheses, omit_parentheses
Style/MethodCallWithArgsParentheses:
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'
- 'Gemfile'
- 'Rakefile'
- 'lib/liquid/block_body.rb'
- 'lib/liquid/parser.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'
- 'liquid.gemspec'
- 'performance/shopify/database.rb'
- 'performance/shopify/liquid.rb'
- 'test/test_helper.rb'
- 'test/unit/condition_unit_test.rb'
- 'test/unit/tags/if_tag_unit_test.rb'
# Offense count: 1
# Cop supports --auto-correct.
@@ -188,27 +297,17 @@ Style/Next:
Exclude:
- 'lib/liquid/tags/for.rb'
# Offense count: 4
# Offense count: 52
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle.
# 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'
Style/PerlBackrefs:
Enabled: false
# Offense count: 14
# Offense count: 33
# 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'
# Configuration parameters: EnforcedStyle.
# SupportedStyles: compact, exploded
Style/RaiseArgs:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
@@ -216,21 +315,52 @@ Style/RedundantSelf:
Exclude:
- 'lib/liquid/strainer.rb'
# Offense count: 9
# Offense count: 5
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
# SupportedStyles: slashes, percent_r, mixed
Style/RegexpLiteral:
Exclude:
- 'lib/liquid/file_system.rb'
- 'lib/liquid/standardfilters.rb'
- 'performance/shopify/shop_filter.rb'
- 'test/unit/condition_unit_test.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: 10
# Cop supports --auto-correct.
# Configuration parameters: AllowAsExpressionSeparator.
Style/Semicolon:
Exclude:
- 'performance/shopify/database.rb'
- 'test/integration/error_handling_test.rb'
- 'test/integration/template_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 7
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: MinSize.
# SupportedStyles: percent, brackets
Style/SymbolArray:
EnforcedStyle: brackets
# Configuration parameters: EnforcedStyle.
# SupportedStyles: use_perl_names, use_english_names
Style/SpecialGlobalVars:
Exclude:
- 'performance/shopify/liquid.rb'
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiteralsInInterpolation:
Exclude:
- 'performance/shopify/tag_filter.rb'
# Offense count: 2
# Cop supports --auto-correct.
@@ -241,6 +371,33 @@ Style/TernaryParentheses:
- 'lib/liquid/context.rb'
- 'lib/liquid/utils.rb'
# Offense count: 21
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInArrayLiteral:
Exclude:
- 'lib/liquid/parse_tree_visitor.rb'
- 'lib/liquid/tags/include.rb'
- 'test/integration/parse_tree_visitor_test.rb'
- 'test/integration/standard_filter_test.rb'
# Offense count: 9
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInHashLiteral:
Exclude:
- 'lib/liquid/condition.rb'
- 'lib/liquid/lexer.rb'
- 'lib/liquid/standardfilters.rb'
- 'performance/shopify/comment_form.rb'
- 'performance/shopify/database.rb'
- 'performance/shopify/paginate.rb'
- 'performance/theme_runner.rb'
- 'test/integration/output_test.rb'
- 'test/unit/context_unit_test.rb'
# Offense count: 2
# Cop supports --auto-correct.
Style/UnneededPercentQ:
@@ -252,9 +409,3 @@ Style/UnneededPercentQ:
Style/WhileUntilModifier:
Exclude:
- 'lib/liquid/tags/case.rb'
# Offense count: 648
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 294

View File

@@ -1,29 +1,26 @@
language: ruby
rvm:
- 2.1
- 2.2
- 2.3
- 2.4
- 2.5
- &latest_ruby 2.6
- 2.7
- ruby-head
- jruby-head
# - rbx-2
- truffleruby
sudo: false
addons:
apt:
packages:
- libgmp3-dev
matrix:
include:
- rvm: *latest_ruby
script: bundle exec rake memory_profile:run
name: Profiling Memory Usage
allow_failures:
- rvm: ruby-head
- rvm: jruby-head
- rvm: truffleruby
install:
- bundle install
cache: bundler
script: bundle exec rake

10
Gemfile
View File

@@ -8,16 +8,18 @@ gemspec
group :benchmark, :test do
gem 'benchmark-ips'
gem 'memory_profiler'
gem 'terminal-table'
install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ } do
install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ && RUBY_ENGINE != 'truffleruby' } do
gem 'stackprof'
end
end
group :test do
gem 'rubocop', '~> 0.53.0'
gem 'rubocop', '~> 0.74.0', require: false
gem 'rubocop-performance', require: false
platform :mri do
gem 'liquid-c', github: 'Shopify/liquid-c', ref: '9168659de45d6d576fce30c735f857e597fa26f6'
platform :mri, :truffleruby do
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'liquid-tag'
end
end

View File

@@ -19,8 +19,10 @@ task :warn_test do
end
task :rubocop do
require 'rubocop/rake_task'
RuboCop::RakeTask.new
if RUBY_ENGINE == 'ruby'
require 'rubocop/rake_task'
RuboCop::RakeTask.new
end
end
desc 'runs test suite with both strict and lax parsers'
@@ -32,8 +34,8 @@ task :test do
Rake::Task['base_test'].reenable
Rake::Task['base_test'].invoke
if RUBY_ENGINE == 'ruby'
ENV['LIQUID-C'] = '1'
if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'truffleruby'
ENV['LIQUID_C'] = '1'
ENV['LIQUID_PARSER_MODE'] = 'lax'
Rake::Task['base_test'].reenable

View File

@@ -1,6 +1,7 @@
module Liquid
class BlockBody
FullToken = /\A#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
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
@@ -13,8 +14,42 @@ module Liquid
@blank = true
end
def parse(tokenizer, parse_context)
def parse(tokenizer, parse_context, &block)
parse_context.line_number = tokenizer.line_number
if tokenizer.for_liquid_tag
parse_for_liquid_tag(tokenizer, parse_context, &block)
else
parse_for_document(tokenizer, parse_context, &block)
end
end
private def parse_for_liquid_tag(tokenizer, parse_context)
while token = tokenizer.shift
unless token.empty? || token =~ WhitespaceOrNothing
unless token =~ LiquidTagToken
# line isn't empty but didn't match tag syntax, yield and let the
# caller raise a syntax error
return yield token, token
end
tag_name = $1
markup = $2
unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed
return yield tag_name, markup
end
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
@blank &&= new_tag.blank?
@nodelist << new_tag
end
parse_context.line_number = tokenizer.line_number
end
yield nil, nil
end
private def parse_for_document(tokenizer, parse_context, &block)
while token = tokenizer.shift
next if token.empty?
case
@@ -23,9 +58,20 @@ module Liquid
unless token =~ FullToken
raise_missing_tag_terminator(token, parse_context)
end
tag_name = $1
markup = $2
# fetch the tag from registered blocks
tag_name = $2
markup = $4
if parse_context.line_number
# newlines inside the tag should increase the line number,
# particularly important for multiline {% liquid %} tags
parse_context.line_number += $1.count("\n".freeze) + $3.count("\n".freeze)
end
if tag_name == 'liquid'.freeze
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true)
next parse_for_liquid_tag(liquid_tag_tokenizer, parse_context, &block)
end
unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed

View File

@@ -5,8 +5,8 @@ module Liquid
include ParserSwitching
class << self
def parse(tag_name, markup, tokenizer, options)
tag = new(tag_name, markup, options)
def parse(tag_name, markup, tokenizer, parse_context)
tag = new(tag_name, markup, parse_context)
tag.parse(tokenizer)
tag
end

View File

@@ -10,6 +10,10 @@ module Liquid
class Assign < Tag
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
def self.syntax_error_translation_key
"errors.syntax.assign".freeze
end
attr_reader :to, :from
def initialize(tag_name, markup, options)
@@ -18,7 +22,7 @@ module Liquid
@to = $1
@from = Variable.new($2, options)
else
raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
raise SyntaxError.new(options[:locale].t(self.class.syntax_error_translation_key))
end
end

24
lib/liquid/tags/echo.rb Normal file
View File

@@ -0,0 +1,24 @@
module Liquid
# Echo outputs an expression
#
# {% echo monkey %}
# {% echo user.name %}
#
# This is identical to variable output syntax, like {{ foo }}, but works
# inside {% liquid %} tags. The full syntax is supported, including filters:
#
# {% echo user | link %}
#
class Echo < Tag
def initialize(tag_name, markup, parse_context)
super
@variable = Variable.new(markup, parse_context)
end
def render(context)
@variable.render_to_output_buffer(context, '')
end
end
Template.register_tag('echo'.freeze, Echo)
end

View File

@@ -1,25 +1,31 @@
module Liquid
class Tokenizer
attr_reader :line_number
attr_reader :line_number, :for_liquid_tag
def initialize(source, line_numbers = false)
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
@source = source
@line_number = line_numbers ? 1 : nil
@line_number = line_number || (line_numbers ? 1 : nil)
@for_liquid_tag = for_liquid_tag
@tokens = tokenize
end
def shift
token = @tokens.shift
@line_number += token.count("\n") if @line_number && token
token = @tokens.shift or return
if @line_number
@line_number += @for_liquid_tag ? 1 : token.count("\n")
end
token
end
private
def tokenize
@source = @source.source if @source.respond_to?(:source)
return [] if @source.to_s.empty?
return @source.split("\n") if @for_liquid_tag
tokens = @source.split(TemplateParser)
# removes the rogue empty element at the beginning of the array

View File

@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
s.license = "MIT"
# s.description = "A secure, non-evaling end user template engine with aesthetic markup."
s.required_ruby_version = ">= 2.1.0"
s.required_ruby_version = ">= 2.4.0"
s.required_rubygems_version = ">= 1.3.7"
s.test_files = Dir.glob("{test}/**/*")

View File

@@ -2,25 +2,62 @@
require 'benchmark/ips'
require 'memory_profiler'
require 'terminal-table'
require_relative 'theme_runner'
def profile(phase, &block)
puts
puts "#{phase}:"
puts
class Profiler
LOG_LABEL = "Profiling: ".rjust(14).freeze
REPORTS_DIR = File.expand_path('.memprof', __dir__).freeze
report = MemoryProfiler.report(&block)
def self.run
puts
yield new
end
report.pretty_print(
color_output: true,
scale_bytes: true,
detailed_report: true
)
def initialize
@allocated = []
@retained = []
@headings = []
end
def profile(phase, &block)
print LOG_LABEL
print "#{phase}.. ".ljust(10)
report = MemoryProfiler.report(&block)
puts 'Done.'
@headings << phase.capitalize
@allocated << "#{report.scale_bytes(report.total_allocated_memsize)} (#{report.total_allocated} objects)"
@retained << "#{report.scale_bytes(report.total_retained_memsize)} (#{report.total_retained} objects)"
return if ENV['CI']
require 'fileutils'
report_file = File.join(REPORTS_DIR, "#{sanitize(phase)}.txt")
FileUtils.mkdir_p(REPORTS_DIR)
report.pretty_print(to_file: report_file, scale_bytes: true)
end
def tabulate
table = Terminal::Table.new(headings: @headings.unshift('Phase')) do |t|
t << @allocated.unshift('Total allocated')
t << @retained.unshift('Total retained')
end
puts
puts table
puts "\nDetailed report(s) saved to #{REPORTS_DIR}/" unless ENV['CI']
end
def sanitize(string)
string.downcase.gsub(/[\W]/, '-').squeeze('-')
end
end
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
profiler = ThemeRunner.new
profile("Parsing") { profiler.compile }
profile("Rendering") { profiler.render }
runner = ThemeRunner.new
Profiler.run do |x|
x.profile('parse') { runner.compile }
x.profile('render') { runner.render }
x.tabulate
end

View File

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

View File

@@ -0,0 +1,104 @@
require 'test_helper'
class LiquidTagTest < Minitest::Test
include Liquid
def test_liquid_tag
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
{%- liquid
echo array | join: " "
-%}
LIQUID
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
{%- liquid
for value in array
echo value
unless forloop.last
echo " "
endunless
endfor
-%}
LIQUID
assert_template_result('4 8 12 6', <<~LIQUID, 'array' => [1, 2, 3])
{%- liquid
for value in array
assign double_value = value | times: 2
echo double_value | times: 2
unless forloop.last
echo " "
endunless
endfor
echo " "
echo double_value
-%}
LIQUID
assert_template_result('abc', <<~LIQUID)
{%- liquid echo "a" -%}
b
{%- liquid echo "c" -%}
LIQUID
end
def test_liquid_tag_errors
assert_match_syntax_error("syntax error (line 1): Unknown tag 'error'", <<~LIQUID)
{%- liquid error no such tag -%}
LIQUID
assert_match_syntax_error("syntax error (line 7): Unknown tag 'error'", <<~LIQUID)
{{ test }}
{%-
liquid
for value in array
error no such tag
endfor
-%}
LIQUID
assert_match_syntax_error("syntax error (line 2): Unknown tag '!!! the guards are vigilant'", <<~LIQUID)
{%- liquid
!!! the guards are vigilant
-%}
LIQUID
assert_match_syntax_error("syntax error (line 4): 'for' tag was never closed", <<~LIQUID)
{%- liquid
for value in array
echo 'forgot to close the for tag'
-%}
LIQUID
end
def test_line_number_is_correct_after_a_blank_token
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n\n error %}")
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n \n error %}")
end
def test_cannot_open_blocks_living_past_a_liquid_tag
assert_match_syntax_error("syntax error (line 3): 'if' tag was never closed", <<~LIQUID)
{%- liquid
if true
-%}
{%- endif -%}
LIQUID
end
def test_quirk_can_close_blocks_created_before_a_liquid_tag
assert_template_result("42", <<~LIQUID)
{%- if true -%}
42
{%- liquid endif -%}
LIQUID
end
def test_liquid_tag_in_raw
assert_template_result("{% liquid echo 'test' %}\n", <<~LIQUID)
{% raw %}{% liquid echo 'test' %}{% endraw %}
LIQUID
end
end

View File

@@ -14,7 +14,7 @@ if env_mode = ENV['LIQUID_PARSER_MODE']
end
Liquid::Template.error_mode = mode
if ENV['LIQUID-C'] == '1'
if ENV['LIQUID_C'] == '1'
puts "-- LIQUID C"
require 'liquid/c'
end
@@ -37,18 +37,18 @@ module Minitest
include Liquid
def assert_template_result(expected, template, assigns = {}, message = nil)
assert_equal expected, Template.parse(template).render!(assigns), message
assert_equal expected, Template.parse(template, line_numbers: true).render!(assigns), message
end
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
assert_match expected, Template.parse(template).render!(assigns), message
assert_match expected, Template.parse(template, line_numbers: true).render!(assigns), message
end
def assert_match_syntax_error(match, template, assigns = {})
exception = assert_raises(Liquid::SyntaxError) do
Template.parse(template).render(assigns)
Template.parse(template, line_numbers: true).render(assigns)
end
assert_match match, exception.message
end