Hello community, here is the log from the commit of package rubygem-liquid for openSUSE:Factory checked in at 2019-03-27 16:18:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-liquid (Old) and /work/SRC/openSUSE:Factory/.rubygem-liquid.new.25356 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-liquid" Wed Mar 27 16:18:03 2019 rev:4 rq:685132 version:4.0.3 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-liquid/rubygem-liquid.changes 2019-01-21 10:25:27.913743256 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-liquid.new.25356/rubygem-liquid.changes 2019-03-27 16:18:07.355570786 +0100 @@ -1,0 +2,59 @@ +Thu Mar 14 10:58:54 UTC 2019 - Stephan Kulow <[email protected]> + +- updated to version 4.0.3 + see installed History.md + + ## 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] + +------------------------------------------------------------------- Old: ---- liquid-4.0.1.gem New: ---- liquid-4.0.3.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-liquid.spec ++++++ --- /var/tmp/diff_new_pack.5BymmK/_old 2019-03-27 16:18:08.415570565 +0100 +++ /var/tmp/diff_new_pack.5BymmK/_new 2019-03-27 16:18:08.415570565 +0100 @@ -1,7 +1,7 @@ # # spec file for package rubygem-liquid # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -24,7 +24,7 @@ # Name: rubygem-liquid -Version: 4.0.1 +Version: 4.0.3 Release: 0 %define mod_name liquid %define mod_full_name %{mod_name}-%{version} ++++++ liquid-4.0.1.gem -> liquid-4.0.3.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/History.md new/History.md --- old/History.md 2018-10-09 11:14:52.000000000 +0200 +++ new/History.md 2019-03-12 17:44:15.000000000 +0100 @@ -1,5 +1,58 @@ # 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" ### Changed Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/block_body.rb new/lib/liquid/block_body.rb --- old/lib/liquid/block_body.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/block_body.rb 2019-03-12 17:44:15.000000000 +0100 @@ -89,6 +89,7 @@ break else # Other non-Block tags render_node_to_output(node, output, context) + break if context.interrupt? # might have happened through an include end idx += 1 end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/condition.rb new/lib/liquid/condition.rb --- old/lib/liquid/condition.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/condition.rb 2019-03-12 17:44:15.000000000 +0100 @@ -29,7 +29,7 @@ @@operators end - attr_reader :attachment + attr_reader :attachment, :child_condition attr_accessor :left, :operator, :right def initialize(left = nil, operator = nil, right = nil) @@ -83,7 +83,7 @@ protected - attr_reader :child_relation, :child_condition + attr_reader :child_relation private @@ -128,6 +128,15 @@ end end end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + [ + @node.left, @node.right, + @node.child_condition, @node.attachment + ].compact + end + end end class ElseCondition < Condition diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/expression.rb new/lib/liquid/expression.rb --- old/lib/liquid/expression.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/expression.rb 2019-03-12 17:44:15.000000000 +0100 @@ -19,7 +19,7 @@ 'false'.freeze => false, 'blank'.freeze => MethodLiteral.new(:blank?, '').freeze, 'empty'.freeze => MethodLiteral.new(:empty?, '').freeze - } + }.freeze SINGLE_QUOTED_STRING = /\A'(.*)'\z/m DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/i18n.rb new/lib/liquid/i18n.rb --- old/lib/liquid/i18n.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/i18n.rb 2019-03-12 17:44:15.000000000 +0100 @@ -26,7 +26,7 @@ def interpolate(name, vars) name.gsub(/%\{(\w+)\}/) do # raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym] - "#{vars[$1.to_sym]}" + (vars[$1.to_sym]).to_s end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/lexer.rb new/lib/liquid/lexer.rb --- old/lib/liquid/lexer.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/lexer.rb 2019-03-12 17:44:15.000000000 +0100 @@ -12,7 +12,7 @@ ')'.freeze => :close_round, '?'.freeze => :question, '-'.freeze => :dash - } + }.freeze IDENTIFIER = /[a-zA-Z_][\w-]*\??/ SINGLE_STRING_LITERAL = /'[^\']*'/ DOUBLE_STRING_LITERAL = /"[^\"]*"/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/parse_tree_visitor.rb new/lib/liquid/parse_tree_visitor.rb --- old/lib/liquid/parse_tree_visitor.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/liquid/parse_tree_visitor.rb 2019-03-12 17:44:15.000000000 +0100 @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Liquid + class ParseTreeVisitor + def self.for(node, callbacks = Hash.new(proc {})) + if defined?(node.class::ParseTreeVisitor) + node.class::ParseTreeVisitor + else + self + end.new(node, callbacks) + end + + def initialize(node, callbacks) + @node = node + @callbacks = callbacks + end + + def add_callback_for(*classes, &block) + callback = block + callback = ->(node, _) { yield node } if block.arity.abs == 1 + callback = ->(_, _) { yield } if block.arity.zero? + classes.each { |klass| @callbacks[klass] = callback } + self + end + + def visit(context = nil) + children.map do |node| + item, new_context = @callbacks[node.class].call(node, context) + [ + item, + ParseTreeVisitor.for(node, @callbacks).visit(new_context || context) + ] + end + end + + protected + + def children + @node.respond_to?(:nodelist) ? Array(@node.nodelist) : [] + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/standardfilters.rb new/lib/liquid/standardfilters.rb --- old/lib/liquid/standardfilters.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/standardfilters.rb 2019-03-12 17:44:15.000000000 +0100 @@ -9,8 +9,14 @@ '<'.freeze => '<'.freeze, '"'.freeze => '"'.freeze, "'".freeze => '''.freeze - } + }.freeze 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 def size(input) @@ -46,7 +52,12 @@ end 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 def slice(input, offset, length = nil) @@ -103,7 +114,9 @@ def strip_html(input) 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 # Remove all newlines from the string @@ -120,25 +133,18 @@ # provide optional property with which to sort an array of hashes or drops def sort(input, property = nil) ary = InputIterator.new(input) + + return [] if ary.empty? + if property.nil? ary.sort do |a, b| - if !a.nil? && !b.nil? - a <=> b - else - a.nil? ? 1 : -1 - end + nil_safe_compare(a, b) end - elsif ary.empty? # The next two cases assume a non-empty array. - [] elsif ary.all? { |el| el.respond_to?(:[]) } - ary.sort do |a, b| - a = a[property] - b = b[property] - if !a.nil? && !b.nil? - a <=> b - else - a.nil? ? 1 : -1 - end + begin + ary.sort { |a, b| nil_safe_compare(a[property], b[property]) } + rescue TypeError + raise_property_error(property) end end end @@ -148,25 +154,39 @@ def sort_natural(input, property = nil) ary = InputIterator.new(input) + return [] if ary.empty? + if property.nil? ary.sort do |a, b| - if !a.nil? && !b.nil? - a.to_s.casecmp(b.to_s) - else - a.nil? ? 1 : -1 - end + nil_safe_casecmp(a, b) end - elsif ary.empty? # The next two cases assume a non-empty array. - [] elsif ary.all? { |el| el.respond_to?(:[]) } - ary.sort do |a, b| - a = a[property] - b = b[property] - if !a.nil? && !b.nil? - a.to_s.casecmp(b.to_s) - else - a.nil? ? 1 : -1 - end + begin + ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) } + rescue TypeError + raise_property_error(property) + end + end + end + + # Filter the elements of an array to those with a certain property value. + # By default the target is any truthy value. + def where(input, property, target_value = nil) + ary = InputIterator.new(input) + + if ary.empty? + [] + elsif ary.first.respond_to?(:[]) && target_value.nil? + begin + ary.select { |item| item[property] } + rescue TypeError + raise_property_error(property) + end + elsif ary.first.respond_to?(:[]) + begin + ary.select { |item| item[property] == target_value } + rescue TypeError + raise_property_error(property) end end end @@ -181,7 +201,11 @@ elsif ary.empty? # The next two cases assume a non-empty array. [] elsif ary.first.respond_to?(:[]) - ary.uniq{ |a| a[property] } + begin + ary.uniq { |a| a[property] } + rescue TypeError + raise_property_error(property) + end end end @@ -203,6 +227,8 @@ r.is_a?(Proc) ? r.call : r end end + rescue TypeError + raise_property_error(property) end # Remove nils within an array @@ -215,7 +241,11 @@ elsif ary.empty? # The next two cases assume a non-empty array. [] elsif ary.first.respond_to?(:[]) - ary.reject{ |a| a[property].nil? } + begin + ary.reject { |a| a[property].nil? } + rescue TypeError + raise_property_error(property) + end end end @@ -399,11 +429,31 @@ private + def raise_property_error(property) + raise Liquid::ArgumentError.new("cannot select the property '#{property}'") + end + def apply_operation(input, operand, operation) result = Utils.to_number(input).send(operation, Utils.to_number(operand)) result.is_a?(BigDecimal) ? result.to_f : result 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 include Enumerable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/assign.rb new/lib/liquid/tags/assign.rb --- old/lib/liquid/tags/assign.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/assign.rb 2019-03-12 17:44:15.000000000 +0100 @@ -10,6 +10,8 @@ class Assign < Tag Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om + attr_reader :to, :from + def initialize(tag_name, markup, options) super if markup =~ Syntax @@ -45,6 +47,12 @@ 1 end end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + [@node.from] + end + end end Template.register_tag('assign'.freeze, Assign) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/case.rb new/lib/liquid/tags/case.rb --- old/lib/liquid/tags/case.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/case.rb 2019-03-12 17:44:15.000000000 +0100 @@ -3,6 +3,8 @@ Syntax = /(#{QuotedFragment})/o WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om + attr_reader :blocks, :left + def initialize(tag_name, markup, options) super @blocks = [] @@ -80,6 +82,12 @@ block.attach(BlockBody.new) @blocks << block end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + [@node.left] + @node.blocks + end + end end Template.register_tag('case'.freeze, Case) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/cycle.rb new/lib/liquid/tags/cycle.rb --- old/lib/liquid/tags/cycle.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/cycle.rb 2019-03-12 17:44:15.000000000 +0100 @@ -15,6 +15,8 @@ SimpleSyntax = /\A#{QuotedFragment}+/o NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om + attr_reader :variables + def initialize(tag_name, markup, options) super case markup @@ -51,6 +53,12 @@ $1 ? Expression.parse($1) : nil end.compact end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + Array(@node.variables) + end + end end Template.register_tag('cycle', Cycle) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/for.rb new/lib/liquid/tags/for.rb --- old/lib/liquid/tags/for.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/for.rb 2019-03-12 17:44:15.000000000 +0100 @@ -46,8 +46,7 @@ class For < Block Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o - attr_reader :collection_name - attr_reader :variable_name + attr_reader :collection_name, :variable_name, :limit, :from def initialize(tag_name, markup, options) super @@ -192,6 +191,12 @@ def render_else(context) @else_block ? @else_block.render(context) : ''.freeze end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + (super + [@node.limit, @node.from, @node.collection_name]).compact + end + end end Template.register_tag('for'.freeze, For) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/if.rb new/lib/liquid/tags/if.rb --- old/lib/liquid/tags/if.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/if.rb 2019-03-12 17:44:15.000000000 +0100 @@ -12,7 +12,9 @@ class If < Block Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o - BOOLEAN_OPERATORS = %w(and or) + BOOLEAN_OPERATORS = %w(and or).freeze + + attr_reader :blocks def initialize(tag_name, markup, options) super @@ -20,15 +22,15 @@ push_block('if'.freeze, markup) end + def nodelist + @blocks.map(&:attachment) + end + def parse(tokens) while parse_body(@blocks.last.attachment, tokens) end end - def nodelist - @blocks.map(&:attachment) - end - def unknown_tag(tag, markup, tokens) if ['elsif'.freeze, 'else'.freeze].include?(tag) push_block(tag, markup) @@ -108,6 +110,12 @@ Condition.new(a) end end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + @node.blocks + end + end end Template.register_tag('if'.freeze, If) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/include.rb new/lib/liquid/tags/include.rb --- old/lib/liquid/tags/include.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/include.rb 2019-03-12 17:44:15.000000000 +0100 @@ -16,6 +16,8 @@ class Include < Tag Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o + attr_reader :template_name_expr, :variable_name_expr, :attributes + def initialize(tag_name, markup, options) super @@ -107,6 +109,15 @@ file_system.read_template_file(context.evaluate(@template_name_expr)) end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + [ + @node.template_name_expr, + @node.variable_name_expr + ] + @node.attributes.values + end + end end Template.register_tag('include'.freeze, Include) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/tags/table_row.rb new/lib/liquid/tags/table_row.rb --- old/lib/liquid/tags/table_row.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/tags/table_row.rb 2019-03-12 17:44:15.000000000 +0100 @@ -2,6 +2,8 @@ class TableRow < Block Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o + attr_reader :variable_name, :collection_name, :attributes + def initialize(tag_name, markup, options) super if markup =~ Syntax @@ -48,6 +50,12 @@ result << "</tr>\n" result end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + super + @node.attributes.values + [@node.collection_name] + end + end end Template.register_tag('tablerow'.freeze, TableRow) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/truffle.rb new/lib/liquid/truffle.rb --- old/lib/liquid/truffle.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/liquid/truffle.rb 2019-03-12 17:44:15.000000000 +0100 @@ -0,0 +1,5 @@ +module Liquid + module Truffle + + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/variable.rb new/lib/liquid/variable.rb --- old/lib/liquid/variable.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/variable.rb 2019-03-12 17:44:15.000000000 +0100 @@ -138,5 +138,11 @@ raise error end end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + [@node.name] + @node.filters.flatten + end + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/variable_lookup.rb new/lib/liquid/variable_lookup.rb --- old/lib/liquid/variable_lookup.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/variable_lookup.rb 2019-03-12 17:44:15.000000000 +0100 @@ -1,7 +1,7 @@ module Liquid class VariableLookup 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 @@ -78,5 +78,11 @@ def state [@name, @lookups, @command_flags] end + + class ParseTreeVisitor < Liquid::ParseTreeVisitor + def children + @node.lookups + end + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid/version.rb new/lib/liquid/version.rb --- old/lib/liquid/version.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid/version.rb 2019-03-12 17:44:15.000000000 +0100 @@ -1,4 +1,5 @@ # encoding: utf-8 + module Liquid - VERSION = "4.0.1" + VERSION = "4.0.3".freeze end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/liquid.rb new/lib/liquid.rb --- old/lib/liquid.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/lib/liquid.rb 2019-03-12 17:44:15.000000000 +0100 @@ -45,6 +45,7 @@ end require "liquid/version" +require 'liquid/parse_tree_visitor' require 'liquid/lexer' require 'liquid/parser' require 'liquid/i18n' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2018-10-09 11:14:52.000000000 +0200 +++ new/metadata 2019-03-12 17:44:15.000000000 +0100 @@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: liquid version: !ruby/object:Gem::Version - version: 4.0.1 + version: 4.0.3 platform: ruby authors: - Tobias Lütke autorequire: bindir: bin cert_chain: [] -date: 2018-10-09 00:00:00.000000000 Z +date: 2019-03-12 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rake @@ -67,6 +67,7 @@ - lib/liquid/lexer.rb - lib/liquid/locales/en.yml - lib/liquid/parse_context.rb +- lib/liquid/parse_tree_visitor.rb - lib/liquid/parser.rb - lib/liquid/parser_switching.rb - lib/liquid/profiler.rb @@ -95,6 +96,7 @@ - lib/liquid/tags/unless.rb - lib/liquid/template.rb - lib/liquid/tokenizer.rb +- lib/liquid/truffle.rb - lib/liquid/utils.rb - lib/liquid/variable.rb - lib/liquid/variable_lookup.rb @@ -111,6 +113,7 @@ - test/integration/filter_test.rb - test/integration/hash_ordering_test.rb - test/integration/output_test.rb +- test/integration/parse_tree_visitor_test.rb - test/integration/parsing_quirks_test.rb - test/integration/render_profiling_test.rb - test/integration/security_test.rb @@ -130,6 +133,7 @@ - test/integration/trim_mode_test.rb - test/integration/variable_test.rb - test/test_helper.rb +- test/truffle/truffle_test.rb - test/unit/block_unit_test.rb - test/unit/condition_unit_test.rb - test/unit/context_unit_test.rb @@ -165,8 +169,7 @@ - !ruby/object:Gem::Version version: 1.3.7 requirements: [] -rubyforge_project: -rubygems_version: 2.7.6 +rubygems_version: 3.0.2 signing_key: specification_version: 4 summary: A secure, non-evaling end user template engine with aesthetic markup. @@ -191,6 +194,7 @@ - test/integration/hash_ordering_test.rb - test/integration/variable_test.rb - test/integration/blank_test.rb +- test/integration/parse_tree_visitor_test.rb - test/integration/assign_test.rb - test/integration/trim_mode_test.rb - test/integration/context_test.rb @@ -216,5 +220,6 @@ - test/integration/render_profiling_test.rb - test/integration/parsing_quirks_test.rb - test/integration/filter_test.rb +- test/truffle/truffle_test.rb - test/fixtures/en_locale.yml - test/test_helper.rb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/integration/error_handling_test.rb new/test/integration/error_handling_test.rb --- old/test/integration/error_handling_test.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/test/integration/error_handling_test.rb 2019-03-12 17:44:15.000000000 +0100 @@ -123,7 +123,7 @@ ', error_mode: :warn, line_numbers: true - ) + ) assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'], template.warnings.map(&:message) @@ -140,7 +140,7 @@ ', error_mode: :strict, line_numbers: true - ) + ) end assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message @@ -158,7 +158,7 @@ bla ', line_numbers: true - ) + ) end assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/integration/parse_tree_visitor_test.rb new/test/integration/parse_tree_visitor_test.rb --- old/test/integration/parse_tree_visitor_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/test/integration/parse_tree_visitor_test.rb 2019-03-12 17:44:16.000000000 +0100 @@ -0,0 +1,247 @@ +# frozen_string_literal: true + +require 'test_helper' + +class ParseTreeVisitorTest < Minitest::Test + include Liquid + + def test_variable + assert_equal( + ["test"], + visit(%({{ test }})) + ) + end + + def test_varible_with_filter + assert_equal( + ["test", "infilter"], + visit(%({{ test | split: infilter }})) + ) + end + + def test_dynamic_variable + assert_equal( + ["test", "inlookup"], + visit(%({{ test[inlookup] }})) + ) + end + + def test_if_condition + assert_equal( + ["test"], + visit(%({% if test %}{% endif %})) + ) + end + + def test_complex_if_condition + assert_equal( + ["test"], + visit(%({% if 1 == 1 and 2 == test %}{% endif %})) + ) + end + + def test_if_body + assert_equal( + ["test"], + visit(%({% if 1 == 1 %}{{ test }}{% endif %})) + ) + end + + def test_unless_condition + assert_equal( + ["test"], + visit(%({% unless test %}{% endunless %})) + ) + end + + def test_complex_unless_condition + assert_equal( + ["test"], + visit(%({% unless 1 == 1 and 2 == test %}{% endunless %})) + ) + end + + def test_unless_body + assert_equal( + ["test"], + visit(%({% unless 1 == 1 %}{{ test }}{% endunless %})) + ) + end + + def test_elsif_condition + assert_equal( + ["test"], + visit(%({% if 1 == 1 %}{% elsif test %}{% endif %})) + ) + end + + def test_complex_elsif_condition + assert_equal( + ["test"], + visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %})) + ) + end + + def test_elsif_body + assert_equal( + ["test"], + visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %})) + ) + end + + def test_else_body + assert_equal( + ["test"], + visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %})) + ) + end + + def test_case_left + assert_equal( + ["test"], + visit(%({% case test %}{% endcase %})) + ) + end + + def test_case_condition + assert_equal( + ["test"], + visit(%({% case 1 %}{% when test %}{% endcase %})) + ) + end + + def test_case_when_body + assert_equal( + ["test"], + visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %})) + ) + end + + def test_case_else_body + assert_equal( + ["test"], + visit(%({% case 1 %}{% else %}{{ test }}{% endcase %})) + ) + end + + def test_for_in + assert_equal( + ["test"], + visit(%({% for x in test %}{% endfor %})) + ) + end + + def test_for_limit + assert_equal( + ["test"], + visit(%({% for x in (1..5) limit: test %}{% endfor %})) + ) + end + + def test_for_offset + assert_equal( + ["test"], + visit(%({% for x in (1..5) offset: test %}{% endfor %})) + ) + end + + def test_for_body + assert_equal( + ["test"], + visit(%({% for x in (1..5) %}{{ test }}{% endfor %})) + ) + end + + def test_tablerow_in + assert_equal( + ["test"], + visit(%({% tablerow x in test %}{% endtablerow %})) + ) + end + + def test_tablerow_limit + assert_equal( + ["test"], + visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %})) + ) + end + + def test_tablerow_offset + assert_equal( + ["test"], + visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %})) + ) + end + + def test_tablerow_body + assert_equal( + ["test"], + visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %})) + ) + end + + def test_cycle + assert_equal( + ["test"], + visit(%({% cycle test %})) + ) + end + + def test_assign + assert_equal( + ["test"], + visit(%({% assign x = test %})) + ) + end + + def test_capture + assert_equal( + ["test"], + visit(%({% capture x %}{{ test }}{% endcapture %})) + ) + end + + def test_include + assert_equal( + ["test"], + visit(%({% include test %})) + ) + end + + def test_include_with + assert_equal( + ["test"], + visit(%({% include "hai" with test %})) + ) + end + + def test_include_for + assert_equal( + ["test"], + visit(%({% include "hai" for test %})) + ) + end + + def test_preserve_tree_structure + assert_equal( + [[nil, [ + [nil, [[nil, [["other", []]]]]], + ["test", []], + ["xs", []] + ]]], + traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit + ) + end + + private + + def traversal(template) + ParseTreeVisitor + .for(Template.parse(template).root) + .add_callback_for(VariableLookup, &:name) + end + + def visit(template) + traversal(template).visit.flatten.compact + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/integration/parsing_quirks_test.rb new/test/integration/parsing_quirks_test.rb --- old/test/integration/parsing_quirks_test.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/test/integration/parsing_quirks_test.rb 2019-03-12 17:44:16.000000000 +0100 @@ -99,7 +99,7 @@ # After the messed up quotes a filter without parameters (reverse) should work # but one with parameters (remove) shouldn't be detected. assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}") - assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}") + assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}") end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/integration/standard_filter_test.rb new/test/integration/standard_filter_test.rb --- old/test/integration/standard_filter_test.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/test/integration/standard_filter_test.rb 2019-03-12 17:44:16.000000000 +0100 @@ -158,6 +158,10 @@ assert_equal '1', @filters.url_decode(1) assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3)) 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 def test_truncatewords @@ -177,6 +181,9 @@ assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>") assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test") assert_equal '', @filters.strip_html(nil) + + # Quirk of the existing implementation + assert_equal 'foo;', @filters.strip_html("<<<script </script>script>foo;</script>") end def test_join @@ -268,10 +275,34 @@ assert_equal [], @filters.sort([], "a") 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 assert_equal [], @filters.sort_natural([], "a") 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 assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 }) end @@ -295,10 +326,34 @@ assert_equal [], @filters.uniq([], "a") 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 assert_equal [], @filters.compact([], "a") end + def test_compact_invalid_property + foo = [ + [1], + [2], + [3] + ] + + assert_raises Liquid::ArgumentError do + @filters.compact(foo, "bar") + end + end + def test_reverse assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4]) end @@ -364,6 +419,29 @@ assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new 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 assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new end @@ -394,9 +472,9 @@ 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 "#{Date.today.year}", @filters.date('now', '%Y') - assert_equal "#{Date.today.year}", @filters.date('today', '%Y') - assert_equal "#{Date.today.year}", @filters.date('Today', '%Y') + assert_equal Date.today.year.to_s, @filters.date('now', '%Y') + assert_equal Date.today.year.to_s, @filters.date('today', '%Y') + assert_equal Date.today.year.to_s, @filters.date('Today', '%Y') assert_nil @filters.date(nil, "%B") @@ -614,6 +692,78 @@ assert_template_result('abc', "{{ 'abc' | date: '%D' }}") end + def test_where + input = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "beta", "ok" => false }, + { "handle" => "gamma", "ok" => false }, + { "handle" => "delta", "ok" => true } + ] + + expectation = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "delta", "ok" => true } + ] + + assert_equal expectation, @filters.where(input, "ok", true) + assert_equal expectation, @filters.where(input, "ok") + end + + def test_where_no_key_set + input = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "beta" }, + { "handle" => "gamma" }, + { "handle" => "delta", "ok" => true } + ] + + expectation = [ + { "handle" => "alpha", "ok" => true }, + { "handle" => "delta", "ok" => true } + ] + + assert_equal expectation, @filters.where(input, "ok", true) + assert_equal expectation, @filters.where(input, "ok") + end + + def test_where_non_array_map_input + assert_equal [{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok") + assert_equal [], @filters.where({ "a" => "not ok" }, "a", "ok") + end + + def test_where_indexable_but_non_map_value + assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok", true) } + assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok") } + end + + def test_where_non_boolean_value + input = [ + { "message" => "Bonjour!", "language" => "French" }, + { "message" => "Hello!", "language" => "English" }, + { "message" => "Hallo!", "language" => "German" } + ] + + assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French") + assert_equal [{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German") + assert_equal [{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English") + end + + def test_where_array_of_only_unindexable_values + assert_nil @filters.where([nil], "ok", true) + assert_nil @filters.where([nil], "ok") + end + + def test_where_no_target_value + input = [ + { "foo" => false }, + { "foo" => true }, + { "foo" => "for sure" }, + { "bar" => true } + ] + + assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo") + end + private def with_timezone(tz) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/integration/tags/include_tag_test.rb new/test/integration/tags/include_tag_test.rb --- old/test/integration/tags/include_tag_test.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/test/integration/tags/include_tag_test.rb 2019-03-12 17:44:16.000000000 +0100 @@ -30,6 +30,9 @@ when 'assignments' "{% assign foo = 'bar' %}" + when 'break' + "{% break %}" + else template_path end @@ -242,4 +245,9 @@ assert_equal [], template.errors 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/truffle/truffle_test.rb new/test/truffle/truffle_test.rb --- old/test/truffle/truffle_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/test/truffle/truffle_test.rb 2019-03-12 17:44:16.000000000 +0100 @@ -0,0 +1,9 @@ +require 'test_helper' + +class TruffleTest < Minitest::Test + include Liquid + + def test_truffle_works + + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/test/unit/condition_unit_test.rb new/test/unit/condition_unit_test.rb --- old/test/unit/condition_unit_test.rb 2018-10-09 11:14:52.000000000 +0200 +++ new/test/unit/condition_unit_test.rb 2019-03-12 17:44:16.000000000 +0100 @@ -24,9 +24,9 @@ assert_evaluates_true 1, '<=', 1 # negative numbers 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 end def test_default_operators_evalute_false
