mirror of
https://github.com/kemko/liquid.git
synced 2026-01-03 00:35:40 +03:00
Compare commits
39 Commits
enable-cla
...
implicit-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b3e50f7ae | ||
|
|
3ef7eead27 | ||
|
|
250048717c | ||
|
|
78d2a437ff | ||
|
|
af614f3a2e | ||
|
|
b3b63a683f | ||
|
|
1c577c5b62 | ||
|
|
755d2821f3 | ||
|
|
495b3d312f | ||
|
|
453f6348c2 | ||
|
|
70ed1fc86d | ||
|
|
c2ef247be5 | ||
|
|
1518d3f6f9 | ||
|
|
c67b77709d | ||
|
|
c89ce9c2ed | ||
|
|
b0629f17f7 | ||
|
|
274f078806 | ||
|
|
d7171aa084 | ||
|
|
06c4789dc5 | ||
|
|
f2f467bdbc | ||
|
|
ff99d92c18 | ||
|
|
39fecd06db | ||
|
|
8013df8ca2 | ||
|
|
14cd011cb5 | ||
|
|
e2d9907df2 | ||
|
|
23d669f5e6 | ||
|
|
ed73794f82 | ||
|
|
f59f6dea83 | ||
|
|
7a81fb821a | ||
|
|
cec27ea326 | ||
|
|
14999e8f7c | ||
|
|
b41fc10d8e | ||
|
|
2b3c81cfd0 | ||
|
|
2a2376bfd9 | ||
|
|
ca9e75db53 | ||
|
|
407c8abf30 | ||
|
|
43f181e211 | ||
|
|
89c1ba2b0e | ||
|
|
479d8fb4a4 |
10
.rubocop.yml
10
.rubocop.yml
@@ -1,4 +1,6 @@
|
|||||||
inherit_from: ./.rubocop_todo.yml
|
inherit_from:
|
||||||
|
- .rubocop_todo.yml
|
||||||
|
- ./.rubocop_todo.yml
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -29,7 +31,7 @@ Lint/ParenthesesAsGroupedExpression:
|
|||||||
Lint/UnusedBlockArgument:
|
Lint/UnusedBlockArgument:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Lint/EndAlignment:
|
Layout/EndAlignment:
|
||||||
EnforcedStyleAlignWith: variable
|
EnforcedStyleAlignWith: variable
|
||||||
|
|
||||||
Lint/UnusedMethodArgument:
|
Lint/UnusedMethodArgument:
|
||||||
@@ -59,7 +61,7 @@ Style/BracesAroundHashParameters:
|
|||||||
Style/NumericLiterals:
|
Style/NumericLiterals:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/SpaceInsideBrackets:
|
Layout/SpaceInsideArrayLiteralBrackets:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/SpaceBeforeBlockBraces:
|
Layout/SpaceBeforeBlockBraces:
|
||||||
@@ -107,7 +109,7 @@ Style/SymbolLiteral:
|
|||||||
Performance/Count:
|
Performance/Count:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/ConstantName:
|
Naming/ConstantName:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/CaseIndentation:
|
Layout/CaseIndentation:
|
||||||
|
|||||||
@@ -1,33 +1,22 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2017-11-22 11:35:55 -0500 using RuboCop version 0.49.1.
|
# on 2019-03-19 11:04:37 -0400 using RuboCop version 0.53.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of RuboCop, may require this file to be generated again.
|
# versions of RuboCop, may require this file to be generated again.
|
||||||
|
|
||||||
# Offense count: 3
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Layout/ClosingParenthesisIndentation:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/error_handling_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Layout/EmptyLineAfterMagicComment:
|
# Configuration parameters: Include, TreatCommentsAsGroupSeparators.
|
||||||
|
# Include: **/*.gemspec
|
||||||
|
Gemspec/OrderedDependencies:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/version.rb'
|
- 'liquid.gemspec'
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
|
|
||||||
Layout/ExtraSpacing:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/parsing_quirks_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 5
|
# Offense count: 5
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent
|
# SupportedStyles: auto_detection, squiggly, active_support, powerpack, unindent
|
||||||
Layout/IndentHeredoc:
|
Layout/IndentHeredoc:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -36,7 +25,7 @@ Layout/IndentHeredoc:
|
|||||||
|
|
||||||
# Offense count: 6
|
# Offense count: 6
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: symmetrical, new_line, same_line
|
# SupportedStyles: symmetrical, new_line, same_line
|
||||||
Layout/MultilineMethodCallBraceLayout:
|
Layout/MultilineMethodCallBraceLayout:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -45,43 +34,65 @@ Layout/MultilineMethodCallBraceLayout:
|
|||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: runtime_error, standard_error
|
# SupportedStyles: runtime_error, standard_error
|
||||||
Lint/InheritException:
|
Lint/InheritException:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/interrupts.rb'
|
- 'lib/liquid/interrupts.rb'
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
Lint/ScriptPermission:
|
# Configuration parameters: CheckForMethodsWithNoSideEffects.
|
||||||
|
Lint/Void:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'test/test_helper.rb'
|
- 'lib/liquid/parse_context.rb'
|
||||||
|
|
||||||
# Offense count: 52
|
# Offense count: 54
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 56
|
Max: 56
|
||||||
|
|
||||||
# Offense count: 13
|
# Offense count: 12
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 12
|
Max: 12
|
||||||
|
|
||||||
# Offense count: 620
|
# Offense count: 112
|
||||||
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
|
||||||
# URISchemes: http, https
|
|
||||||
Metrics/LineLength:
|
|
||||||
Max: 294
|
|
||||||
|
|
||||||
# Offense count: 102
|
|
||||||
# Configuration parameters: CountComments.
|
# Configuration parameters: CountComments.
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
Max: 37
|
Max: 37
|
||||||
|
|
||||||
# Offense count: 9
|
# Offense count: 8
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 11
|
Max: 11
|
||||||
|
|
||||||
|
# Offense count: 52
|
||||||
|
# Configuration parameters: Blacklist.
|
||||||
|
# Blacklist: END, (?-mix:EO[A-Z]{1})
|
||||||
|
Naming/HeredocDelimiterNaming:
|
||||||
|
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/tags/if.rb'
|
||||||
|
- 'lib/liquid/utils.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'
|
||||||
|
|
||||||
# Offense count: 10
|
# Offense count: 10
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: prefer_alias, prefer_alias_method
|
# SupportedStyles: prefer_alias, prefer_alias_method
|
||||||
Style/Alias:
|
Style/Alias:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -93,14 +104,23 @@ Style/Alias:
|
|||||||
- 'lib/liquid/tags/include.rb'
|
- 'lib/liquid/tags/include.rb'
|
||||||
- 'lib/liquid/variable.rb'
|
- 'lib/liquid/variable.rb'
|
||||||
|
|
||||||
|
# Offense count: 22
|
||||||
|
Style/CommentedKeyword:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions.
|
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
|
||||||
# SupportedStyles: assign_to_condition, assign_inside_condition
|
# SupportedStyles: assign_to_condition, assign_inside_condition
|
||||||
Style/ConditionalAssignment:
|
Style/ConditionalAssignment:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/errors.rb'
|
- 'lib/liquid/errors.rb'
|
||||||
|
|
||||||
|
# Offense count: 1
|
||||||
|
Style/DateTime:
|
||||||
|
Exclude:
|
||||||
|
- 'test/unit/context_unit_test.rb'
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/EmptyCaseCondition:
|
Style/EmptyCaseCondition:
|
||||||
@@ -110,7 +130,7 @@ Style/EmptyCaseCondition:
|
|||||||
|
|
||||||
# Offense count: 5
|
# Offense count: 5
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
# Configuration parameters: EnforcedStyle.
|
||||||
# SupportedStyles: compact, expanded
|
# SupportedStyles: compact, expanded
|
||||||
Style/EmptyMethod:
|
Style/EmptyMethod:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -120,11 +140,28 @@ Style/EmptyMethod:
|
|||||||
- 'test/integration/tags/include_tag_test.rb'
|
- 'test/integration/tags/include_tag_test.rb'
|
||||||
- 'test/unit/context_unit_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
|
# Offense count: 2
|
||||||
# Configuration parameters: SupportedStyles.
|
# Cop supports --auto-correct.
|
||||||
# SupportedStyles: annotated, template
|
Style/ExpandPathArguments:
|
||||||
|
Exclude:
|
||||||
|
- 'Rakefile'
|
||||||
|
- 'liquid.gemspec'
|
||||||
|
|
||||||
|
# Offense count: 7
|
||||||
|
# Configuration parameters: EnforcedStyle.
|
||||||
|
# SupportedStyles: annotated, template, unannotated
|
||||||
Style/FormatStringToken:
|
Style/FormatStringToken:
|
||||||
EnforcedStyle: template
|
Exclude:
|
||||||
|
- 'test/integration/filter_test.rb'
|
||||||
|
- 'test/integration/hash_ordering_test.rb'
|
||||||
|
|
||||||
# Offense count: 14
|
# Offense count: 14
|
||||||
# Configuration parameters: MinBodyLength.
|
# Configuration parameters: MinBodyLength.
|
||||||
@@ -143,26 +180,9 @@ Style/GuardClause:
|
|||||||
- 'lib/liquid/variable.rb'
|
- 'lib/liquid/variable.rb'
|
||||||
- 'test/unit/tokenizer_unit_test.rb'
|
- 'test/unit/tokenizer_unit_test.rb'
|
||||||
|
|
||||||
# Offense count: 4
|
|
||||||
# Configuration parameters: SupportedStyles.
|
|
||||||
# SupportedStyles: snake_case, camelCase
|
|
||||||
Style/MethodName:
|
|
||||||
EnforcedStyle: snake_case
|
|
||||||
|
|
||||||
# Offense count: 6
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/MutableConstant:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/expression.rb'
|
|
||||||
- 'lib/liquid/lexer.rb'
|
|
||||||
- 'lib/liquid/standardfilters.rb'
|
|
||||||
- 'lib/liquid/tags/if.rb'
|
|
||||||
- 'lib/liquid/variable_lookup.rb'
|
|
||||||
- 'lib/liquid/version.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
# Configuration parameters: EnforcedStyle, MinBodyLength.
|
||||||
# SupportedStyles: skip_modifier_ifs, always
|
# SupportedStyles: skip_modifier_ifs, always
|
||||||
Style/Next:
|
Style/Next:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -170,7 +190,7 @@ Style/Next:
|
|||||||
|
|
||||||
# Offense count: 4
|
# Offense count: 4
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
|
# Configuration parameters: AutoCorrect, EnforcedStyle.
|
||||||
# SupportedStyles: predicate, comparison
|
# SupportedStyles: predicate, comparison
|
||||||
Style/NumericPredicate:
|
Style/NumericPredicate:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -190,12 +210,6 @@ Style/PercentLiteralDelimiters:
|
|||||||
- 'test/integration/assign_test.rb'
|
- 'test/integration/assign_test.rb'
|
||||||
- 'test/integration/standard_filter_test.rb'
|
- 'test/integration/standard_filter_test.rb'
|
||||||
|
|
||||||
# Offense count: 2
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/RedundantParentheses:
|
|
||||||
Exclude:
|
|
||||||
- 'test/unit/condition_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/RedundantSelf:
|
Style/RedundantSelf:
|
||||||
@@ -213,27 +227,20 @@ Style/Semicolon:
|
|||||||
|
|
||||||
# Offense count: 7
|
# Offense count: 7
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: MinSize, SupportedStyles.
|
# Configuration parameters: MinSize.
|
||||||
# SupportedStyles: percent, brackets
|
# SupportedStyles: percent, brackets
|
||||||
Style/SymbolArray:
|
Style/SymbolArray:
|
||||||
EnforcedStyle: brackets
|
EnforcedStyle: brackets
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
|
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
|
||||||
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
||||||
Style/TernaryParentheses:
|
Style/TernaryParentheses:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/context.rb'
|
- 'lib/liquid/context.rb'
|
||||||
- 'lib/liquid/utils.rb'
|
- 'lib/liquid/utils.rb'
|
||||||
|
|
||||||
# Offense count: 4
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/UnneededInterpolation:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/i18n.rb'
|
|
||||||
- 'test/integration/standard_filter_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/UnneededPercentQ:
|
Style/UnneededPercentQ:
|
||||||
@@ -242,7 +249,12 @@ Style/UnneededPercentQ:
|
|||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: MaxLineLength.
|
|
||||||
Style/WhileUntilModifier:
|
Style/WhileUntilModifier:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/tags/case.rb'
|
- 'lib/liquid/tags/case.rb'
|
||||||
|
|
||||||
|
# Offense count: 640
|
||||||
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||||
|
# URISchemes: http, https
|
||||||
|
Metrics/LineLength:
|
||||||
|
Max: 294
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ matrix:
|
|||||||
- rvm: jruby-head
|
- rvm: jruby-head
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- gem install rainbow -v 2.2.1
|
|
||||||
- bundle install
|
- bundle install
|
||||||
|
|
||||||
script: bundle exec rake
|
script: bundle exec rake
|
||||||
|
|||||||
9
Gemfile
9
Gemfile
@@ -5,14 +5,17 @@ end
|
|||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem 'stackprof', platforms: :mri
|
|
||||||
|
|
||||||
group :benchmark, :test do
|
group :benchmark, :test do
|
||||||
gem 'benchmark-ips'
|
gem 'benchmark-ips'
|
||||||
|
gem 'memory_profiler'
|
||||||
|
|
||||||
|
install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ } do
|
||||||
|
gem 'stackprof'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 0.49.0'
|
gem 'rubocop', '~> 0.53.0'
|
||||||
|
|
||||||
platform :mri do
|
platform :mri do
|
||||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: '9168659de45d6d576fce30c735f857e597fa26f6'
|
gem 'liquid-c', github: 'Shopify/liquid-c', ref: '9168659de45d6d576fce30c735f857e597fa26f6'
|
||||||
|
|||||||
53
History.md
53
History.md
@@ -1,5 +1,58 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
|
## 4.0.3 / 2019-03-12
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fix break and continue tags inside included templates in loops (#1072) [Justin Li]
|
||||||
|
|
||||||
|
## 4.0.2 / 2019-03-08
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Add `where` filter (#1026) [Samuel Doiron]
|
||||||
|
* Add `ParseTreeVisitor` to iterate the Liquid AST (#1025) [Stephen Paul Weber]
|
||||||
|
* Improve `strip_html` performance (#1032) [printercu]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Add error checking for invalid combinations of inputs to sort, sort_natural, where, uniq, map, compact filters (#1059) [Garland Zhang]
|
||||||
|
* Validate the character encoding in url_decode (#1070) [Clayton Smith]
|
||||||
|
|
||||||
|
## 4.0.1 / 2018-10-09
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Add benchmark group in Gemfile (#855) [Jerry Liu]
|
||||||
|
* Allow benchmarks to benchmark render by itself (#851) [Jerry Liu]
|
||||||
|
* Avoid calling `line_number` on String node when rescuing a render error. (#860) [Dylan Thacker-Smith]
|
||||||
|
* Avoid duck typing to detect whether to call render on a node. [Dylan Thacker-Smith]
|
||||||
|
* Clarify spelling of `reversed` on `for` block tag (#843) [Mark Crossfield]
|
||||||
|
* Replace recursion with loop to avoid potential stack overflow from malicious input (#891, #892) [Dylan Thacker-Smith]
|
||||||
|
* Limit block tag nesting to 100 (#894) [Dylan Thacker-Smith]
|
||||||
|
* Replace `assert_equal nil` with `assert_nil` (#895) [Dylan Thacker-Smith]
|
||||||
|
* Remove Spy Gem (#896) [Dylan Thacker-Smith]
|
||||||
|
* Add `collection_name` and `variable_name` reader to `For` block (#909)
|
||||||
|
* Symbols render as strings (#920) [Justin Li]
|
||||||
|
* Remove default value from Hash objects (#932) [Maxime Bedard]
|
||||||
|
* Remove one level of nesting (#944) [Dylan Thacker-Smith]
|
||||||
|
* Update Rubocop version (#952) [Justin Li]
|
||||||
|
* Add `at_least` and `at_most` filters (#954, #958) [Nithin Bekal]
|
||||||
|
* Add a regression test for a liquid-c trim mode bug (#972) [Dylan Thacker-Smith]
|
||||||
|
* Use https rather than git protocol to fetch liquid-c [Dylan Thacker-Smith]
|
||||||
|
* Add tests against Ruby 2.4 (#963) and 2.5 (#981)
|
||||||
|
* Replace RegExp literals with constants (#988) [Ashwin Maroli]
|
||||||
|
* Replace unnecessary `#each_with_index` with `#each` (#992) [Ashwin Maroli]
|
||||||
|
* Improve the unexpected end delimiter message for block tags. (#1003) [Dylan Thacker-Smith]
|
||||||
|
* Refactor and optimize rendering (#1005) [Christopher Aue]
|
||||||
|
* Add installation instruction (#1006) [Ben Gift]
|
||||||
|
* Remove Circle CI (#1010)
|
||||||
|
* Rename deprecated `BigDecimal.new` to `BigDecimal` (#1024) [Koichi ITO]
|
||||||
|
* Rename deprecated Rubocop name (#1027) [Justin Li]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Handle `join` filter on non String joiners (#857) [Richard Monette]
|
||||||
|
* Fix duplicate inclusion condition logic error of `Liquid::Strainer.add_filter` method (#861)
|
||||||
|
* Fix `escape`, `url_encode`, `url_decode` not handling non-string values (#898) [Thierry Joyal]
|
||||||
|
* Fix raise when variable is defined but nil when using `strict_variables` [Pascal Betz]
|
||||||
|
* Fix `sort` and `sort_natural` to handle arrays with nils (#930) [Eric Chan]
|
||||||
|
|
||||||
## 4.0.0 / 2016-12-14 / branch "4-0-stable"
|
## 4.0.0 / 2016-12-14 / branch "4-0-stable"
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
9
Rakefile
9
Rakefile
@@ -3,7 +3,7 @@ require 'rake/testtask'
|
|||||||
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
||||||
require "liquid/version"
|
require "liquid/version"
|
||||||
|
|
||||||
task default: [:rubocop, :test]
|
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|
|
Rake::TestTask.new(:base_test) do |t|
|
||||||
@@ -85,6 +85,13 @@ namespace :profile do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
namespace :memory_profile do
|
||||||
|
desc "Run memory profiler"
|
||||||
|
task :run do
|
||||||
|
ruby "./performance/memory_profile.rb"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
desc "Run example"
|
desc "Run example"
|
||||||
task :example do
|
task :example do
|
||||||
ruby "-w -d -Ilib example/server/server.rb"
|
ruby "-w -d -Ilib example/server/server.rb"
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ module Liquid
|
|||||||
break
|
break
|
||||||
else # Other non-Block tags
|
else # Other non-Block tags
|
||||||
render_node_to_output(node, output, context)
|
render_node_to_output(node, output, context)
|
||||||
|
break if context.interrupt? # might have happened through an include
|
||||||
end
|
end
|
||||||
idx += 1
|
idx += 1
|
||||||
end
|
end
|
||||||
@@ -114,7 +115,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def check_resources(context, node_output)
|
def check_resources(context, node_output)
|
||||||
context.resource_limits.render_length += node_output.length
|
context.resource_limits.render_length += node_output.bytesize
|
||||||
return unless context.resource_limits.reached?
|
return unless context.resource_limits.reached?
|
||||||
raise MemoryError.new("Memory limits exceeded".freeze)
|
raise MemoryError.new("Memory limits exceeded".freeze)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ module Liquid
|
|||||||
#
|
#
|
||||||
# context['bob'] #=> nil class Context
|
# context['bob'] #=> nil class Context
|
||||||
class Context
|
class Context
|
||||||
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
attr_reader :scope, :errors, :registers, :environments, :resource_limits
|
||||||
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
||||||
|
|
||||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
||||||
@environments = [environments].flatten
|
@environments = [environments].flatten
|
||||||
@scopes = [(outer_scope || {})]
|
@scope = outer_scope || {}
|
||||||
@registers = registers
|
@registers = registers
|
||||||
@errors = []
|
@errors = []
|
||||||
@partial = false
|
@partial = false
|
||||||
@@ -25,8 +25,6 @@ module Liquid
|
|||||||
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
||||||
squash_instance_assigns_with_environments
|
squash_instance_assigns_with_environments
|
||||||
|
|
||||||
@this_stack_used = false
|
|
||||||
|
|
||||||
self.exception_renderer = Template.default_exception_renderer
|
self.exception_renderer = Template.default_exception_renderer
|
||||||
if rethrow_errors
|
if rethrow_errors
|
||||||
self.exception_renderer = ->(e) { raise }
|
self.exception_renderer = ->(e) { raise }
|
||||||
@@ -35,6 +33,8 @@ module Liquid
|
|||||||
@interrupts = []
|
@interrupts = []
|
||||||
@filters = []
|
@filters = []
|
||||||
@global_filter = nil
|
@global_filter = nil
|
||||||
|
|
||||||
|
@stack_level = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def warnings
|
def warnings
|
||||||
@@ -86,21 +86,9 @@ module Liquid
|
|||||||
strainer.invoke(method, *args).to_liquid
|
strainer.invoke(method, *args).to_liquid
|
||||||
end
|
end
|
||||||
|
|
||||||
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
|
||||||
def push(new_scope = {})
|
|
||||||
@scopes.unshift(new_scope)
|
|
||||||
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH
|
|
||||||
end
|
|
||||||
|
|
||||||
# Merge a hash of variables in the current local scope
|
# Merge a hash of variables in the current local scope
|
||||||
def merge(new_scopes)
|
def merge(new_scopes)
|
||||||
@scopes[0].merge!(new_scopes)
|
new_scopes.each { |k, v| self[k] = v }
|
||||||
end
|
|
||||||
|
|
||||||
# Pop from the stack. use <tt>Context#stack</tt> instead
|
|
||||||
def pop
|
|
||||||
raise ContextError if @scopes.size == 1
|
|
||||||
@scopes.shift
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pushes a new local scope on the stack, pops it at the end of the block
|
# Pushes a new local scope on the stack, pops it at the end of the block
|
||||||
@@ -111,32 +99,20 @@ module Liquid
|
|||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# context['var] #=> nil
|
# context['var] #=> nil
|
||||||
def stack(new_scope = nil)
|
def stack(*variable_names)
|
||||||
old_stack_used = @this_stack_used
|
@stack_level += 1
|
||||||
if new_scope
|
raise StackLevelError, "Nesting too deep".freeze if @stack_level > Block::MAX_DEPTH
|
||||||
push(new_scope)
|
|
||||||
@this_stack_used = true
|
|
||||||
else
|
|
||||||
@this_stack_used = false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
begin
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
pop if @this_stack_used
|
@stack_level -= 1
|
||||||
@this_stack_used = old_stack_used
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_instance_assigns
|
|
||||||
@scopes[0] = {}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
||||||
def []=(key, value)
|
def []=(key, value)
|
||||||
unless @this_stack_used
|
(@scope[key] ||= [nil]) << value
|
||||||
@this_stack_used = true
|
|
||||||
push({})
|
|
||||||
end
|
|
||||||
@scopes[0][key] = value
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Look up variable, either resolve directly after considering the name. We can directly handle
|
# Look up variable, either resolve directly after considering the name. We can directly handle
|
||||||
@@ -151,6 +127,29 @@ module Liquid
|
|||||||
evaluate(Expression.parse(expression))
|
evaluate(Expression.parse(expression))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unset(key)
|
||||||
|
if @scope[key].size <= 1
|
||||||
|
@scope.delete(key)
|
||||||
|
else
|
||||||
|
@scope[key].pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_root(key, val)
|
||||||
|
@scope[key] ||= []
|
||||||
|
@scope[key][0] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_level(key, val, int)
|
||||||
|
@scope[key] ||= []
|
||||||
|
@scope[key][int] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_level(key)
|
||||||
|
(@scope[key] ||= [nil]) << nil
|
||||||
|
@scope[key].size - 1
|
||||||
|
end
|
||||||
|
|
||||||
def key?(key)
|
def key?(key)
|
||||||
self[key] != nil
|
self[key] != nil
|
||||||
end
|
end
|
||||||
@@ -161,27 +160,23 @@ module Liquid
|
|||||||
|
|
||||||
# Fetches an object starting at the local scope and then moving up the hierachy
|
# Fetches an object starting at the local scope and then moving up the hierachy
|
||||||
def find_variable(key, raise_on_not_found: true)
|
def find_variable(key, raise_on_not_found: true)
|
||||||
# This was changed from find() to find_index() because this is a very hot
|
trigger = false
|
||||||
# path and find_index() is optimized in MRI to reduce object allocation
|
value = @scope[key]
|
||||||
index = @scopes.find_index { |s| s.key?(key) }
|
scope = @scope unless value.nil?
|
||||||
scope = @scopes[index] if index
|
trigger = true unless value.nil?
|
||||||
|
|
||||||
variable = nil
|
|
||||||
|
|
||||||
if scope.nil?
|
if scope.nil?
|
||||||
@environments.each do |e|
|
index = @environments.find_index do |e|
|
||||||
variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found)
|
variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found)
|
||||||
# When lookup returned a value OR there is no value but the lookup also did not raise
|
# When lookup returned a value OR there is no value but the lookup also did not raise
|
||||||
# then it is the value we are looking for.
|
# then it is the value we are looking for.
|
||||||
if !variable.nil? || @strict_variables && raise_on_not_found
|
!variable.nil? || @strict_variables && raise_on_not_found
|
||||||
scope = e
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope ||= @environments.last || @scopes.last
|
scope = @environments[index || -1]
|
||||||
variable ||= lookup_and_evaluate(scope, key, raise_on_not_found: raise_on_not_found)
|
end
|
||||||
|
|
||||||
|
variable ||= lookup_and_evaluate(scope, key, trigger, raise_on_not_found: raise_on_not_found)
|
||||||
|
|
||||||
variable = variable.to_liquid
|
variable = variable.to_liquid
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
variable.context = self if variable.respond_to?(:context=)
|
||||||
@@ -189,12 +184,16 @@ module Liquid
|
|||||||
variable
|
variable
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
def lookup_and_evaluate(obj, key, trigger = false, raise_on_not_found: true)
|
||||||
if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
|
if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
|
||||||
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
||||||
end
|
end
|
||||||
|
|
||||||
value = obj[key]
|
value = if trigger == true
|
||||||
|
obj[key][-1]
|
||||||
|
else
|
||||||
|
obj[key]
|
||||||
|
end
|
||||||
|
|
||||||
if value.is_a?(Proc) && obj.respond_to?(:[]=)
|
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)
|
||||||
@@ -213,10 +212,10 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def squash_instance_assigns_with_environments
|
def squash_instance_assigns_with_environments
|
||||||
@scopes.last.each_key do |k|
|
@scope.each_key do |k|
|
||||||
@environments.each do |env|
|
@environments.each do |env|
|
||||||
if env.key?(k)
|
if env.key?(k)
|
||||||
scopes.last[k] = lookup_and_evaluate(env, k)
|
@scope[k] = [lookup_and_evaluate(env, k)]
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ module Liquid
|
|||||||
'false'.freeze => false,
|
'false'.freeze => false,
|
||||||
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
|
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
|
||||||
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
|
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
|
||||||
}
|
}.freeze
|
||||||
|
|
||||||
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m
|
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m
|
||||||
DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m
|
DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ module Liquid
|
|||||||
def interpolate(name, vars)
|
def interpolate(name, vars)
|
||||||
name.gsub(/%\{(\w+)\}/) do
|
name.gsub(/%\{(\w+)\}/) do
|
||||||
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
|
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
|
||||||
"#{vars[$1.to_sym]}"
|
(vars[$1.to_sym]).to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ module Liquid
|
|||||||
')'.freeze => :close_round,
|
')'.freeze => :close_round,
|
||||||
'?'.freeze => :question,
|
'?'.freeze => :question,
|
||||||
'-'.freeze => :dash
|
'-'.freeze => :dash
|
||||||
}
|
}.freeze
|
||||||
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
||||||
SINGLE_STRING_LITERAL = /'[^\']*'/
|
SINGLE_STRING_LITERAL = /'[^\']*'/
|
||||||
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
||||||
|
|||||||
@@ -9,8 +9,14 @@ module Liquid
|
|||||||
'<'.freeze => '<'.freeze,
|
'<'.freeze => '<'.freeze,
|
||||||
'"'.freeze => '"'.freeze,
|
'"'.freeze => '"'.freeze,
|
||||||
"'".freeze => '''.freeze
|
"'".freeze => '''.freeze
|
||||||
}
|
}.freeze
|
||||||
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
||||||
|
STRIP_HTML_BLOCKS = Regexp.union(
|
||||||
|
/<script.*?<\/script>/m,
|
||||||
|
/<!--.*?-->/m,
|
||||||
|
/<style.*?<\/style>/m
|
||||||
|
)
|
||||||
|
STRIP_HTML_TAGS = /<.*?>/m
|
||||||
|
|
||||||
# Return the size of an array or of an string
|
# Return the size of an array or of an string
|
||||||
def size(input)
|
def size(input)
|
||||||
@@ -46,7 +52,12 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def url_decode(input)
|
def url_decode(input)
|
||||||
CGI.unescape(input.to_s) unless input.nil?
|
return if input.nil?
|
||||||
|
|
||||||
|
result = CGI.unescape(input.to_s)
|
||||||
|
raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
|
||||||
|
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def slice(input, offset, length = nil)
|
def slice(input, offset, length = nil)
|
||||||
@@ -103,7 +114,9 @@ module Liquid
|
|||||||
|
|
||||||
def strip_html(input)
|
def strip_html(input)
|
||||||
empty = ''.freeze
|
empty = ''.freeze
|
||||||
input.to_s.gsub(/<script.*?<\/script>/m, empty).gsub(/<!--.*?-->/m, empty).gsub(/<style.*?<\/style>/m, empty).gsub(/<.*?>/m, empty)
|
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
||||||
|
result.gsub!(STRIP_HTML_TAGS, empty)
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove all newlines from the string
|
# Remove all newlines from the string
|
||||||
@@ -120,25 +133,18 @@ module Liquid
|
|||||||
# provide optional property with which to sort an array of hashes or drops
|
# provide optional property with which to sort an array of hashes or drops
|
||||||
def sort(input, property = nil)
|
def sort(input, property = nil)
|
||||||
ary = InputIterator.new(input)
|
ary = InputIterator.new(input)
|
||||||
|
|
||||||
|
return [] if ary.empty?
|
||||||
|
|
||||||
if property.nil?
|
if property.nil?
|
||||||
ary.sort do |a, b|
|
ary.sort do |a, b|
|
||||||
if !a.nil? && !b.nil?
|
nil_safe_compare(a, b)
|
||||||
a <=> b
|
|
||||||
else
|
|
||||||
a.nil? ? 1 : -1
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
|
||||||
[]
|
|
||||||
elsif ary.all? { |el| el.respond_to?(:[]) }
|
elsif ary.all? { |el| el.respond_to?(:[]) }
|
||||||
ary.sort do |a, b|
|
begin
|
||||||
a = a[property]
|
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
||||||
b = b[property]
|
rescue TypeError
|
||||||
if !a.nil? && !b.nil?
|
raise_property_error(property)
|
||||||
a <=> b
|
|
||||||
else
|
|
||||||
a.nil? ? 1 : -1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -148,25 +154,17 @@ module Liquid
|
|||||||
def sort_natural(input, property = nil)
|
def sort_natural(input, property = nil)
|
||||||
ary = InputIterator.new(input)
|
ary = InputIterator.new(input)
|
||||||
|
|
||||||
|
return [] if ary.empty?
|
||||||
|
|
||||||
if property.nil?
|
if property.nil?
|
||||||
ary.sort do |a, b|
|
ary.sort do |a, b|
|
||||||
if !a.nil? && !b.nil?
|
nil_safe_casecmp(a, b)
|
||||||
a.to_s.casecmp(b.to_s)
|
|
||||||
else
|
|
||||||
a.nil? ? 1 : -1
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
|
||||||
[]
|
|
||||||
elsif ary.all? { |el| el.respond_to?(:[]) }
|
elsif ary.all? { |el| el.respond_to?(:[]) }
|
||||||
ary.sort do |a, b|
|
begin
|
||||||
a = a[property]
|
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
||||||
b = b[property]
|
rescue TypeError
|
||||||
if !a.nil? && !b.nil?
|
raise_property_error(property)
|
||||||
a.to_s.casecmp(b.to_s)
|
|
||||||
else
|
|
||||||
a.nil? ? 1 : -1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -179,9 +177,17 @@ module Liquid
|
|||||||
if ary.empty?
|
if ary.empty?
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
||||||
ary.where_present(property)
|
begin
|
||||||
|
ary.select { |item| item[property] }
|
||||||
|
rescue TypeError
|
||||||
|
raise_property_error(property)
|
||||||
|
end
|
||||||
elsif ary.first.respond_to?(:[])
|
elsif ary.first.respond_to?(:[])
|
||||||
ary.where(property, target_value)
|
begin
|
||||||
|
ary.select { |item| item[property] == target_value }
|
||||||
|
rescue TypeError
|
||||||
|
raise_property_error(property)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -195,7 +201,11 @@ module Liquid
|
|||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
elsif ary.first.respond_to?(:[])
|
||||||
|
begin
|
||||||
ary.uniq { |a| a[property] }
|
ary.uniq { |a| a[property] }
|
||||||
|
rescue TypeError
|
||||||
|
raise_property_error(property)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -217,6 +227,8 @@ module Liquid
|
|||||||
r.is_a?(Proc) ? r.call : r
|
r.is_a?(Proc) ? r.call : r
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
rescue TypeError
|
||||||
|
raise_property_error(property)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove nils within an array
|
# Remove nils within an array
|
||||||
@@ -229,7 +241,11 @@ module Liquid
|
|||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
elsif ary.first.respond_to?(:[])
|
||||||
|
begin
|
||||||
ary.reject { |a| a[property].nil? }
|
ary.reject { |a| a[property].nil? }
|
||||||
|
rescue TypeError
|
||||||
|
raise_property_error(property)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -413,11 +429,31 @@ module Liquid
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def raise_property_error(property)
|
||||||
|
raise Liquid::ArgumentError.new("cannot select the property '#{property}'")
|
||||||
|
end
|
||||||
|
|
||||||
def apply_operation(input, operand, operation)
|
def apply_operation(input, operand, operation)
|
||||||
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
result.is_a?(BigDecimal) ? result.to_f : result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def nil_safe_compare(a, b)
|
||||||
|
if !a.nil? && !b.nil?
|
||||||
|
a <=> b
|
||||||
|
else
|
||||||
|
a.nil? ? 1 : -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def nil_safe_casecmp(a, b)
|
||||||
|
if !a.nil? && !b.nil?
|
||||||
|
a.to_s.casecmp(b.to_s)
|
||||||
|
else
|
||||||
|
a.nil? ? 1 : -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class InputIterator
|
class InputIterator
|
||||||
include Enumerable
|
include Enumerable
|
||||||
|
|
||||||
@@ -463,24 +499,6 @@ module Liquid
|
|||||||
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def where(property, target_value)
|
|
||||||
select do |item|
|
|
||||||
item[property] == target_value
|
|
||||||
end
|
|
||||||
rescue TypeError
|
|
||||||
# Cannot index with the given property type (eg. indexing integers with strings
|
|
||||||
# which are only allowed to be indexed by other integers).
|
|
||||||
raise ArgumentError.new("cannot select the property `#{property}`")
|
|
||||||
end
|
|
||||||
|
|
||||||
def where_present(property)
|
|
||||||
select { |item| item[property] }
|
|
||||||
rescue TypeError
|
|
||||||
# Cannot index with the given property type (eg. indexing integers with strings
|
|
||||||
# which are only allowed to be indexed by other integers).
|
|
||||||
raise ArgumentError.new("cannot select the property `#{property}`")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ module Liquid
|
|||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
val = @from.render(context)
|
val = @from.render(context)
|
||||||
context.scopes.last[@to] = val
|
context.set_root(@to, val)
|
||||||
context.resource_limits.assign_score += assign_score_of(val)
|
context.resource_limits.assign_score += assign_score_of(val)
|
||||||
''.freeze
|
''.freeze
|
||||||
end
|
end
|
||||||
@@ -37,7 +37,7 @@ module Liquid
|
|||||||
|
|
||||||
def assign_score_of(val)
|
def assign_score_of(val)
|
||||||
if val.instance_of?(String)
|
if val.instance_of?(String)
|
||||||
val.length
|
val.bytesize
|
||||||
elsif val.instance_of?(Array) || val.instance_of?(Hash)
|
elsif val.instance_of?(Array) || val.instance_of?(Hash)
|
||||||
sum = 1
|
sum = 1
|
||||||
# Uses #each to avoid extra allocations.
|
# Uses #each to avoid extra allocations.
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ module Liquid
|
|||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
output = super
|
output = super
|
||||||
context.scopes.last[@to] = output
|
context.set_root(@to, output)
|
||||||
context.resource_limits.assign_score += output.length
|
context.resource_limits.assign_score += output.bytesize
|
||||||
''.freeze
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
context.stack do
|
|
||||||
execute_else_block = true
|
execute_else_block = true
|
||||||
|
|
||||||
output = ''
|
output = ''
|
||||||
@@ -51,9 +50,9 @@ module Liquid
|
|||||||
output << block.attachment.render(context)
|
output << block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ module Liquid
|
|||||||
def render(context)
|
def render(context)
|
||||||
context.registers[:cycle] ||= {}
|
context.registers[:cycle] ||= {}
|
||||||
|
|
||||||
context.stack do
|
|
||||||
key = context.evaluate(@name)
|
key = context.evaluate(@name)
|
||||||
iteration = context.registers[:cycle][key].to_i
|
iteration = context.registers[:cycle][key].to_i
|
||||||
result = context.evaluate(@variables[iteration])
|
result = context.evaluate(@variables[iteration])
|
||||||
@@ -43,7 +42,6 @@ module Liquid
|
|||||||
context.registers[:cycle][key] = iteration
|
context.registers[:cycle][key] = iteration
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|||||||
@@ -124,14 +124,23 @@ module Liquid
|
|||||||
from = if @from == :continue
|
from = if @from == :continue
|
||||||
offsets[@name].to_i
|
offsets[@name].to_i
|
||||||
else
|
else
|
||||||
context.evaluate(@from).to_i
|
from_value = context.evaluate(@from)
|
||||||
|
if from_value.nil?
|
||||||
|
0
|
||||||
|
else
|
||||||
|
Utils.to_integer(from_value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
collection = context.evaluate(@collection_name)
|
collection = context.evaluate(@collection_name)
|
||||||
collection = collection.to_a if collection.is_a?(Range)
|
collection = collection.to_a if collection.is_a?(Range)
|
||||||
|
|
||||||
limit = context.evaluate(@limit)
|
limit_value = context.evaluate(@limit)
|
||||||
to = limit ? limit.to_i + from : nil
|
to = if limit_value.nil?
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
Utils.to_integer(limit_value) + from
|
||||||
|
end
|
||||||
|
|
||||||
segment = Utils.slice_collection(collection, from, to)
|
segment = Utils.slice_collection(collection, from, to)
|
||||||
segment.reverse! if @reversed
|
segment.reverse! if @reversed
|
||||||
@@ -147,19 +156,19 @@ module Liquid
|
|||||||
|
|
||||||
result = ''
|
result = ''
|
||||||
|
|
||||||
context.stack do
|
context.stack('forloop', @variable_name) do
|
||||||
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
||||||
|
|
||||||
for_stack.push(loop_vars)
|
for_stack.push(loop_vars)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
context['forloop'.freeze] = loop_vars
|
context['forloop'.freeze] = loop_vars
|
||||||
|
level = context.create_level(@variable_name)
|
||||||
|
|
||||||
segment.each do |item|
|
segment.each do |item|
|
||||||
context[@variable_name] = item
|
context.set_level(@variable_name, item, level)
|
||||||
result << @for_block.render(context)
|
result << @for_block.render(context)
|
||||||
loop_vars.send(:increment!)
|
loop_vars.send(:increment!)
|
||||||
|
|
||||||
# Handle any interrupts if they exist.
|
# Handle any interrupts if they exist.
|
||||||
if context.interrupt?
|
if context.interrupt?
|
||||||
interrupt = context.pop_interrupt
|
interrupt = context.pop_interrupt
|
||||||
@@ -167,7 +176,10 @@ module Liquid
|
|||||||
next if interrupt.is_a? ContinueInterrupt
|
next if interrupt.is_a? ContinueInterrupt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
context.unset(@variable_name)
|
||||||
|
context.unset('forloop'.freeze)
|
||||||
ensure
|
ensure
|
||||||
|
|
||||||
for_stack.pop
|
for_stack.pop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ module Liquid
|
|||||||
class If < Block
|
class If < Block
|
||||||
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
||||||
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
||||||
BOOLEAN_OPERATORS = %w(and or)
|
BOOLEAN_OPERATORS = %w(and or).freeze
|
||||||
|
|
||||||
attr_reader :blocks
|
attr_reader :blocks
|
||||||
|
|
||||||
@@ -40,7 +40,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
context.stack do
|
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
if block.evaluate(context)
|
if block.evaluate(context)
|
||||||
return block.attachment.render(context)
|
return block.attachment.render(context)
|
||||||
@@ -48,7 +47,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
''.freeze
|
''.freeze
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Ifchanged < Block
|
class Ifchanged < Block
|
||||||
def render(context)
|
def render(context)
|
||||||
context.stack do
|
|
||||||
output = super
|
output = super
|
||||||
|
|
||||||
if output != context.registers[:ifchanged]
|
if output != context.registers[:ifchanged]
|
||||||
@@ -12,7 +11,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
Template.register_tag('ifchanged'.freeze, Ifchanged)
|
Template.register_tag('ifchanged'.freeze, Ifchanged)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ module Liquid
|
|||||||
begin
|
begin
|
||||||
context.template_name = template_name
|
context.template_name = template_name
|
||||||
context.partial = true
|
context.partial = true
|
||||||
context.stack do
|
context.stack(context_variable_name, *@attributes.keys) do
|
||||||
@attributes.each do |key, value|
|
@attributes.each do |key, value|
|
||||||
context[key] = context.evaluate(value)
|
context[key] = context.evaluate(value)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ module Liquid
|
|||||||
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
||||||
|
|
||||||
result = "<tr class=\"row1\">\n"
|
result = "<tr class=\"row1\">\n"
|
||||||
context.stack do
|
context.stack('tablerowloop', @variable_name) do
|
||||||
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
||||||
context['tablerowloop'.freeze] = tablerowloop
|
context['tablerowloop'.freeze] = tablerowloop
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ module Liquid
|
|||||||
#
|
#
|
||||||
class Unless < If
|
class Unless < If
|
||||||
def render(context)
|
def render(context)
|
||||||
context.stack do
|
|
||||||
# First condition is interpreted backwards ( if not )
|
# First condition is interpreted backwards ( if not )
|
||||||
first_block = @blocks.first
|
first_block = @blocks.first
|
||||||
unless first_block.evaluate(context)
|
unless first_block.evaluate(context)
|
||||||
@@ -24,7 +23,6 @@ module Liquid
|
|||||||
''.freeze
|
''.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
Template.register_tag('unless'.freeze, Unless)
|
Template.register_tag('unless'.freeze, Unless)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -95,16 +95,17 @@ module Liquid
|
|||||||
|
|
||||||
def parse_filter_expressions(filter_name, unparsed_args)
|
def parse_filter_expressions(filter_name, unparsed_args)
|
||||||
filter_args = []
|
filter_args = []
|
||||||
keyword_args = {}
|
keyword_args = nil
|
||||||
unparsed_args.each do |a|
|
unparsed_args.each do |a|
|
||||||
if matches = a.match(JustTagAttributes)
|
if matches = a.match(JustTagAttributes)
|
||||||
|
keyword_args ||= {}
|
||||||
keyword_args[matches[1]] = Expression.parse(matches[2])
|
keyword_args[matches[1]] = Expression.parse(matches[2])
|
||||||
else
|
else
|
||||||
filter_args << Expression.parse(a)
|
filter_args << Expression.parse(a)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
result = [filter_name, filter_args]
|
result = [filter_name, filter_args]
|
||||||
result << keyword_args unless keyword_args.empty?
|
result << keyword_args if keyword_args
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class VariableLookup
|
class VariableLookup
|
||||||
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
||||||
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze]
|
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
|
||||||
|
|
||||||
attr_reader :name, :lookups
|
attr_reader :name, :lookups
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "4.0.1"
|
VERSION = "4.0.3".freeze
|
||||||
end
|
end
|
||||||
|
|||||||
26
performance/memory_profile.rb
Normal file
26
performance/memory_profile.rb
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'benchmark/ips'
|
||||||
|
require 'memory_profiler'
|
||||||
|
require_relative 'theme_runner'
|
||||||
|
|
||||||
|
def profile(phase, &block)
|
||||||
|
puts
|
||||||
|
puts "#{phase}:"
|
||||||
|
puts
|
||||||
|
|
||||||
|
report = MemoryProfiler.report(&block)
|
||||||
|
|
||||||
|
report.pretty_print(
|
||||||
|
color_output: true,
|
||||||
|
scale_bytes: true,
|
||||||
|
detailed_report: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
||||||
|
|
||||||
|
profiler = ThemeRunner.new
|
||||||
|
|
||||||
|
profile("Parsing") { profiler.compile }
|
||||||
|
profile("Rendering") { profiler.render }
|
||||||
@@ -15,7 +15,7 @@ class CommentForm < Liquid::Block
|
|||||||
def render(context)
|
def render(context)
|
||||||
article = context[@variable_name]
|
article = context[@variable_name]
|
||||||
|
|
||||||
context.stack do
|
context.stack('form') do
|
||||||
context['form'] = {
|
context['form'] = {
|
||||||
'posted_successfully?' => context.registers[:posted_successfully],
|
'posted_successfully?' => context.registers[:posted_successfully],
|
||||||
'errors' => context['comment.errors'],
|
'errors' => context['comment.errors'],
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Paginate < Liquid::Block
|
|||||||
def render(context)
|
def render(context)
|
||||||
@context = context
|
@context = context
|
||||||
|
|
||||||
context.stack do
|
context.stack('paginate') do
|
||||||
current_page = context['current_page'].to_i
|
current_page = context['current_page'].to_i
|
||||||
|
|
||||||
pagination = {
|
pagination = {
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class ContextDrop < Liquid::Drop
|
class ContextDrop < Liquid::Drop
|
||||||
def scopes
|
|
||||||
@context.scopes.size
|
|
||||||
end
|
|
||||||
|
|
||||||
def scopes_as_array
|
|
||||||
(1..@context.scopes.size).to_a
|
|
||||||
end
|
|
||||||
|
|
||||||
def loop_pos
|
def loop_pos
|
||||||
@context['forloop.index']
|
@context['forloop.index']
|
||||||
end
|
end
|
||||||
@@ -194,31 +186,6 @@ class DropsTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_scope
|
|
||||||
assert_equal '1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new)
|
|
||||||
assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
|
||||||
assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
|
||||||
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])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_scope_with_assigns
|
|
||||||
assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new)
|
|
||||||
assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
|
||||||
assert_equal 'test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new)
|
|
||||||
assert_equal 'test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_scope_from_tags
|
|
||||||
assert_equal '1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
|
||||||
assert_equal '12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
|
||||||
assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_access_context_from_drop
|
def test_access_context_from_drop
|
||||||
assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3])
|
assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -158,6 +158,10 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal '1', @filters.url_decode(1)
|
assert_equal '1', @filters.url_decode(1)
|
||||||
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
|
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
|
||||||
assert_nil @filters.url_decode(nil)
|
assert_nil @filters.url_decode(nil)
|
||||||
|
exception = assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.url_decode('%ff')
|
||||||
|
end
|
||||||
|
assert_equal 'Liquid error: invalid byte sequence in UTF-8', exception.message
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_truncatewords
|
def test_truncatewords
|
||||||
@@ -177,6 +181,9 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
|
assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
|
||||||
assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
|
assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
|
||||||
assert_equal '', @filters.strip_html(nil)
|
assert_equal '', @filters.strip_html(nil)
|
||||||
|
|
||||||
|
# Quirk of the existing implementation
|
||||||
|
assert_equal 'foo;', @filters.strip_html("<<<script </script>script>foo;</script>")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_join
|
def test_join
|
||||||
@@ -268,10 +275,34 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal [], @filters.sort([], "a")
|
assert_equal [], @filters.sort([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_sort_invalid_property
|
||||||
|
foo = [
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.sort(foo, "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_sort_natural_empty_array
|
def test_sort_natural_empty_array
|
||||||
assert_equal [], @filters.sort_natural([], "a")
|
assert_equal [], @filters.sort_natural([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_sort_natural_invalid_property
|
||||||
|
foo = [
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.sort_natural(foo, "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_legacy_sort_hash
|
def test_legacy_sort_hash
|
||||||
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
||||||
end
|
end
|
||||||
@@ -295,10 +326,34 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal [], @filters.uniq([], "a")
|
assert_equal [], @filters.uniq([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_uniq_invalid_property
|
||||||
|
foo = [
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.uniq(foo, "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_compact_empty_array
|
def test_compact_empty_array
|
||||||
assert_equal [], @filters.compact([], "a")
|
assert_equal [], @filters.compact([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_compact_invalid_property
|
||||||
|
foo = [
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.compact(foo, "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_reverse
|
def test_reverse
|
||||||
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
||||||
end
|
end
|
||||||
@@ -350,11 +405,11 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
def test_map_over_drops_returning_procs
|
def test_map_over_drops_returning_procs
|
||||||
drops = [
|
drops = [
|
||||||
{
|
{
|
||||||
"proc" => ->{ "foo" },
|
"proc" => ->{ "foo" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"proc" => ->{ "bar" },
|
"proc" => ->{ "bar" }
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
templ = '{{ drops | map: "proc" }}'
|
templ = '{{ drops | map: "proc" }}'
|
||||||
assert_template_result "foobar", templ, "drops" => drops
|
assert_template_result "foobar", templ, "drops" => drops
|
||||||
@@ -364,6 +419,29 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_map_returns_empty_on_2d_input_array
|
||||||
|
foo = [
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.map(foo, "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_map_returns_empty_with_no_property
|
||||||
|
foo = [
|
||||||
|
[1],
|
||||||
|
[2],
|
||||||
|
[3]
|
||||||
|
]
|
||||||
|
assert_raises Liquid::ArgumentError do
|
||||||
|
@filters.map(foo, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_sort_works_on_enumerables
|
def test_sort_works_on_enumerables
|
||||||
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
||||||
end
|
end
|
||||||
@@ -394,9 +472,9 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
|
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
|
||||||
|
|
||||||
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
||||||
assert_equal "#{Date.today.year}", @filters.date('now', '%Y')
|
assert_equal Date.today.year.to_s, @filters.date('now', '%Y')
|
||||||
assert_equal "#{Date.today.year}", @filters.date('today', '%Y')
|
assert_equal Date.today.year.to_s, @filters.date('today', '%Y')
|
||||||
assert_equal "#{Date.today.year}", @filters.date('Today', '%Y')
|
assert_equal Date.today.year.to_s, @filters.date('Today', '%Y')
|
||||||
|
|
||||||
assert_nil @filters.date(nil, "%B")
|
assert_nil @filters.date(nil, "%B")
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,34 @@ HERE
|
|||||||
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
|
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_limiting_with_invalid_limit
|
||||||
|
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||||
|
template = <<-MKUP
|
||||||
|
{% for i in array limit: true offset: 1 %}
|
||||||
|
{{ i }}
|
||||||
|
{% endfor %}
|
||||||
|
MKUP
|
||||||
|
|
||||||
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
|
Template.parse(template).render!(assigns)
|
||||||
|
end
|
||||||
|
assert_equal("Liquid error: invalid integer", exception.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_limiting_with_invalid_offset
|
||||||
|
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||||
|
template = <<-MKUP
|
||||||
|
{% for i in array limit: 1 offset: true %}
|
||||||
|
{{ i }}
|
||||||
|
{% endfor %}
|
||||||
|
MKUP
|
||||||
|
|
||||||
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
|
Template.parse(template).render!(assigns)
|
||||||
|
end
|
||||||
|
assert_equal("Liquid error: invalid integer", exception.message)
|
||||||
|
end
|
||||||
|
|
||||||
def test_dynamic_variable_limiting
|
def test_dynamic_variable_limiting
|
||||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||||
assigns['limit'] = 2
|
assigns['limit'] = 2
|
||||||
@@ -340,6 +368,23 @@ HERE
|
|||||||
assert_template_result(expected, template, assigns)
|
assert_template_result(expected, template, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_overwriting_internal_variable
|
||||||
|
template = <<-HEREDOC
|
||||||
|
{% assign forloop = 'first' %}
|
||||||
|
|
||||||
|
{% for item in items %}
|
||||||
|
{{ forloop }}
|
||||||
|
{% assign forloop = 'second' %}
|
||||||
|
{{ forloop }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ forloop }}
|
||||||
|
HEREDOC
|
||||||
|
|
||||||
|
result = Liquid::Template.parse(template).render('items' => '1')
|
||||||
|
assert_equal 'Liquid::ForloopDrop Liquid::ForloopDrop second', result.split.map(&:strip).join(' ')
|
||||||
|
end
|
||||||
|
|
||||||
class LoaderDrop < Liquid::Drop
|
class LoaderDrop < Liquid::Drop
|
||||||
attr_accessor :each_called, :load_slice_called
|
attr_accessor :each_called, :load_slice_called
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class IfElseTagTest < Minitest::Test
|
|||||||
[false, true, true] => true,
|
[false, true, true] => true,
|
||||||
[false, true, false] => false,
|
[false, true, false] => false,
|
||||||
[false, false, true] => false,
|
[false, false, true] => false,
|
||||||
[false, false, false] => false,
|
[false, false, false] => false
|
||||||
}
|
}
|
||||||
|
|
||||||
tests.each do |vals, expected|
|
tests.each do |vals, expected|
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ class TestFileSystem
|
|||||||
when 'assignments'
|
when 'assignments'
|
||||||
"{% assign foo = 'bar' %}"
|
"{% assign foo = 'bar' %}"
|
||||||
|
|
||||||
|
when 'break'
|
||||||
|
"{% break %}"
|
||||||
|
|
||||||
else
|
else
|
||||||
template_path
|
template_path
|
||||||
end
|
end
|
||||||
@@ -242,4 +245,9 @@ class IncludeTagTest < Minitest::Test
|
|||||||
|
|
||||||
assert_equal [], template.errors
|
assert_equal [], template.errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_break_through_include
|
||||||
|
assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}"
|
||||||
|
assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}"
|
||||||
|
end
|
||||||
end # IncludeTagTest
|
end # IncludeTagTest
|
||||||
|
|||||||
@@ -139,6 +139,16 @@ class TemplateTest < Minitest::Test
|
|||||||
refute_nil t.resource_limits.assign_score
|
refute_nil t.resource_limits.assign_score
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_resource_limits_assign_score_counts_bytes_not_characters
|
||||||
|
t = Template.parse("{% assign foo = 'すごい' %}")
|
||||||
|
t.render
|
||||||
|
assert_equal 9, t.resource_limits.assign_score
|
||||||
|
|
||||||
|
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
|
||||||
|
t.render
|
||||||
|
assert_equal 9, t.resource_limits.assign_score
|
||||||
|
end
|
||||||
|
|
||||||
def test_resource_limits_assign_score_nested
|
def test_resource_limits_assign_score_nested
|
||||||
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
||||||
|
|
||||||
@@ -187,6 +197,14 @@ class TemplateTest < Minitest::Test
|
|||||||
assert_equal "ababab", t.render
|
assert_equal "ababab", t.render
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_length_uses_number_of_bytes_not_characters
|
||||||
|
t = Template.parse("{% if true %}すごい{% endif %}")
|
||||||
|
t.resource_limits.render_length_limit = 10
|
||||||
|
assert_equal "Liquid error: Memory limits exceeded", t.render
|
||||||
|
t.resource_limits.render_length_limit = 18
|
||||||
|
assert_equal "すごい", t.render
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_resource_limits_unaffected_by_render_with_context
|
def test_default_resource_limits_unaffected_by_render_with_context
|
||||||
context = Context.new
|
context = Context.new
|
||||||
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
||||||
|
|||||||
0
test/test_helper.rb
Normal file → Executable file
0
test/test_helper.rb
Normal file → Executable file
@@ -24,9 +24,9 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_evaluates_true 1, '<=', 1
|
assert_evaluates_true 1, '<=', 1
|
||||||
# negative numbers
|
# negative numbers
|
||||||
assert_evaluates_true 1, '>', -1
|
assert_evaluates_true 1, '>', -1
|
||||||
assert_evaluates_true (-1), '<', 1
|
assert_evaluates_true -1, '<', 1
|
||||||
assert_evaluates_true 1.0, '>', -1.0
|
assert_evaluates_true 1.0, '>', -1.0
|
||||||
assert_evaluates_true (-1.0), '<', 1.0
|
assert_evaluates_true -1.0, '<', 1.0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_default_operators_evalute_false
|
def test_default_operators_evalute_false
|
||||||
|
|||||||
@@ -102,21 +102,6 @@ class ContextUnitTest < Minitest::Test
|
|||||||
assert_nil @context['does_not_exist']
|
assert_nil @context['does_not_exist']
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_scoping
|
|
||||||
@context.push
|
|
||||||
@context.pop
|
|
||||||
|
|
||||||
assert_raises(Liquid::ContextError) do
|
|
||||||
@context.pop
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_raises(Liquid::ContextError) do
|
|
||||||
@context.push
|
|
||||||
@context.pop
|
|
||||||
@context.pop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_length_query
|
def test_length_query
|
||||||
@context['numbers'] = [1, 2, 3, 4]
|
@context['numbers'] = [1, 2, 3, 4]
|
||||||
|
|
||||||
@@ -170,18 +155,12 @@ class ContextUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_add_item_in_outer_scope
|
def test_add_item_in_outer_scope
|
||||||
@context['test'] = 'test'
|
@context['test'] = 'test'
|
||||||
@context.push
|
|
||||||
assert_equal 'test', @context['test']
|
@context.stack('test') do
|
||||||
@context.pop
|
|
||||||
assert_equal 'test', @context['test']
|
assert_equal 'test', @context['test']
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_item_in_inner_scope
|
|
||||||
@context.push
|
|
||||||
@context['test'] = 'test'
|
|
||||||
assert_equal 'test', @context['test']
|
assert_equal 'test', @context['test']
|
||||||
@context.pop
|
|
||||||
assert_nil @context['test']
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_hierachical_data
|
def test_hierachical_data
|
||||||
|
|||||||
Reference in New Issue
Block a user