Commit Graph
79 Commits
Author SHA1 Message Date
Guilherme CarreiroandGitHub 32b50ecafe Bump Liquid to 5.11.0 (#2012)
This commit reverts the Inline Snippets tag (#2001) and bumps
Liquid to 5.11. For now, the inclusion of Inline Snippets
in the latest Liquid release is being treated as a bug.

While #2001 does implement the scope contained in RFC#1916,
we need to take a step back to make sure we’re setting our
sights high enough with this feature, and that we’re truly
supporting theme developers in the ways they need.

If you have any feedback, please leave a comment on RFC#1916.

- Liquid Developer Tools
2025-11-19 18:03:23 +01:00
Guilherme CarreiroandGuilherme Carreiro a23c71e40b Fix variable to keep it backward-compatible in strict mode
* lax_parse    - no changes
  * strict_parse - uses the `lax_parse_filter_expressions` (as it was doing before)
  * rigid_parse  - uses the `rigid_parse_filter_expressions`
2025-10-27 16:33:31 +01:00
Charles-P. ClermontandGuilherme Carreiro b8958f626d Stricter 1:1 refactor of strict_parse for Variable 2025-10-27 16:33:31 +01:00
Charles-P. ClermontandGuilherme Carreiro 94bbf6ca32 Make it possible to safe_parse subsets of expressions
e.g. sometimes you want to only accept strings | lookups.
{% render snippetName %} for example. snippetName is a string right now.
We don't want safe_parse_expression because this would allow snippetName
to be a number, a boolean, etc. But we still want to strict parse this.

So what we'll do is use parse_expression(string, safe: true), this is
an optional opt-in to say "I know what I'm doing". Usually that's
because you're using the output of Parser#something as the input of
parse_expression.

It is true that Parser#expression is subset of Expression.parse, it is
not true of the opposite (e.g. Expression.parse doesn't care about .5
and happily parses that as a global lookup of the variable named "5",
Parser#expression throws for that.)

diff --git a/lib/liquid/condition.rb b/lib/liquid/condition.rb
index e5c321dc..9ab350f0 100644
--- a/lib/liquid/condition.rb
+++ b/lib/liquid/condition.rb
@@ -48,8 +48,8 @@ module Liquid
       @@operators
     end

-    def self.parse_expression(parse_context, markup)
-      @@method_literals[markup] || parse_context.parse_expression(markup)
+    def self.parse_expression(parse_context, markup, safe: false)
+      @@method_literals[markup] || parse_context.parse_expression(markup, safe: safe)
     end

     attr_reader :attachment, :child_condition
diff --git a/lib/liquid/parse_context.rb b/lib/liquid/parse_context.rb
index 1c59fe4a..82cf5768 100644
--- a/lib/liquid/parse_context.rb
+++ b/lib/liquid/parse_context.rb
@@ -51,13 +51,13 @@ module Liquid
     end

     def safe_parse_expression(parser)
-      Expression.safe_parse(parser)
+      Expression.safe_parse(parser, @string_scanner, @expression_cache)
     end

-    def parse_expression(markup)
+    def parse_expression(markup, safe: false)
       # todo(guilherme): remove this once rigid mode is fully using safe_parse_expression
-      # raise Liquid::InternalError, "parse_expression is not supported in rigid mode" if @error_mode == :rigid
-      puts("🚨 parse_expression used in rigid mode") if @error_mode == :rigid
+      # raise Liquid::InternalError, "parse_expression is not supported in rigid mode" if !safe && @error_mode == :rigid
+      puts("🚨 parse_expression used in rigid mode") if !safe && @error_mode == :rigid

       Expression.parse(markup, @string_scanner, @expression_cache)
     end
diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb
index 656d2e47..374ee511 100644
--- a/lib/liquid/tag.rb
+++ b/lib/liquid/tag.rb
@@ -72,8 +72,8 @@ module Liquid
       parse_context.safe_parse_expression(parser)
     end

-    def parse_expression(markup)
-      parse_context.parse_expression(markup)
+    def parse_expression(markup, safe: false)
+      parse_context.parse_expression(markup, safe: safe)
     end
   end
 end
diff --git a/lib/liquid/tags/for.rb b/lib/liquid/tags/for.rb
index c2be5db1..3182983b 100644
--- a/lib/liquid/tags/for.rb
+++ b/lib/liquid/tags/for.rb
@@ -93,7 +93,7 @@ module Liquid
       raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')

       collection_name  = p.expression
-      @collection_name = parse_expression(collection_name)
+      @collection_name = parse_expression(collection_name, safe: true)

       @name     = "#{@variable_name}-#{collection_name}"
       @reversed = p.id?('reversed')
diff --git a/lib/liquid/tags/if.rb b/lib/liquid/tags/if.rb
index 342374f1..e25d6250 100644
--- a/lib/liquid/tags/if.rb
+++ b/lib/liquid/tags/if.rb
@@ -81,8 +81,8 @@ module Liquid
       block.attach(new_body)
     end

-    def parse_expression(markup)
-      Condition.parse_expression(parse_context, markup)
+    def parse_expression(markup, safe: false)
+      Condition.parse_expression(parse_context, markup, safe: safe)
     end

     def lax_parse(markup)
@@ -124,9 +124,9 @@ module Liquid
     end

     def parse_comparison(p)
-      a = parse_expression(p.expression)
+      a = parse_expression(p.expression, safe: true)
       if (op = p.consume?(:comparison))
-        b = parse_expression(p.expression)
+        b = parse_expression(p.expression, safe: true)
         Condition.new(a, op, b)
       else
         Condition.new(a)
diff --git a/lib/liquid/tags/include.rb b/lib/liquid/tags/include.rb
index 6cdbfd6f..b72a235b 100644
--- a/lib/liquid/tags/include.rb
+++ b/lib/liquid/tags/include.rb
@@ -87,10 +87,11 @@ module Liquid
     def rigid_parse(markup)
       p = @parse_context.new_parser(markup)

-      template_name = p.expression
+      @template_name_expr = safe_parse_expression(p)
       with_or_for = p.id?("for") || p.id?("with") || nil
+      @variable_name_expr = nil
       if with_or_for
-        variable_name = p.expression
+        @variable_name_expr = parse_expression(p.consume(:id), safe: true)
       end

       alias_name = nil
@@ -98,8 +99,6 @@ module Liquid
         alias_name = p.consume(:id)
       end

-      @template_name_expr = parse_expression(template_name)
-      @variable_name_expr = variable_name ? parse_expression(variable_name) : nil
       @alias_name = alias_name

       # optional comma
@@ -109,7 +108,7 @@ module Liquid
       while p.look(:id)
         key = p.consume
         p.consume(:colon)
-        @attributes[key] = parse_expression(p.expression)
+        @attributes[key] = safe_parse_expression(p)
         p.consume?(:comma) # optional comma
       end
     end
diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb
index 89c11063..4f716b24 100644
--- a/lib/liquid/tags/render.rb
+++ b/lib/liquid/tags/render.rb
@@ -88,10 +88,11 @@ module Liquid
     def rigid_parse(markup)
       p = @parse_context.new_parser(markup)

-      template_name = rigid_template_name(p)
+      @template_name_expr = parse_expression(rigid_template_name(p), safe: true)
+      @variable_name_expr = nil
       with_or_for = p.id?("for") || p.id?("with") || nil
       if with_or_for
-        variable_name = p.expression
+        @variable_name_expr = safe_parse_expression(p)
       end

       alias_name = nil
@@ -99,8 +100,6 @@ module Liquid
         alias_name = p.consume(:id)
       end

-      @template_name_expr = parse_expression(template_name)
-      @variable_name_expr = variable_name ? parse_expression(variable_name) : nil
       @alias_name = alias_name
       @is_for_loop = (with_or_for == FOR)

@@ -111,7 +110,7 @@ module Liquid
       while p.look(:id)
         key = p.consume
         p.consume(:colon)
-        @attributes[key] = parse_expression(p.expression)
+        @attributes[key] = safe_parse_expression(p)
         p.consume?(:comma) # optional comma
       end
     end
diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb
index 20957065..a3623bc5 100644
--- a/lib/liquid/variable.rb
+++ b/lib/liquid/variable.rb
@@ -65,11 +65,11 @@ module Liquid

       return if p.look(:end_of_string)

-      @name = parse_context.parse_expression(p.expression)
+      @name = parse_context.safe_parse_expression(p)
       while p.consume?(:pipe)
         filtername = p.consume(:id)
         filterargs = p.consume?(:colon) ? parse_filterargs(p) : Const::EMPTY_ARRAY
-        @filters << parse_filter_expressions(filtername, filterargs)
+        @filters << parse_filter_expressions(filtername, filterargs, safe: true)
       end
       p.consume(:end_of_string)
     end
@@ -122,15 +122,15 @@ module Liquid

     private

-    def parse_filter_expressions(filter_name, unparsed_args)
+    def parse_filter_expressions(filter_name, unparsed_args, safe: false)
       filter_args  = []
       keyword_args = nil
       unparsed_args.each do |a|
-        if (matches = a.match(JustTagAttributes))
+        if (matches = a.match(JustTagAttributes)) # we'll need to fix this
           keyword_args           ||= {}
-          keyword_args[matches[1]] = parse_context.parse_expression(matches[2])
+          keyword_args[matches[1]] = parse_context.parse_expression(matches[2], safe: false)
         else
-          filter_args << parse_context.parse_expression(a)
+          filter_args << parse_context.parse_expression(a, safe: safe)
         end
       end
       result = [filter_name, filter_args]
2025-10-27 16:33:31 +01:00
4b65a28722 Implement logic for stringify Hashes to keep compat with 3.4 (#1892)
* Exploring

* Bump to v5.6.5

---------

Co-authored-by: Dominic Petrick <[email protected]>
2025-01-15 16:36:53 -05:00
Michael Go 7c592c1c00 store StringScanner in ParseContext and reuse it through parsing 2025-01-07 14:32:45 -04:00
Ian Ker-SeymerandGitHub 63583ffe5b Write one value at a time for array variables (#1863)
* Write one value at a time for array variables

* Handle recursive array
2024-12-11 10:16:58 -05:00
Michael Go 1f3ea7322b avoid allocating new empty array 2024-11-04 15:35:45 -04:00
Dylan Thacker-SmithandGitHub eb70bb9b87 Refactor Liquid::Variable to respect disabling liquid-c nodes (#1584)
parse_context.parse_expression is overriden in liquid-c and will avoid
parsing to a Liquid::C::Expression when parsing with
`disable_liquid_c_nodes: true`
2022-06-21 15:19:56 -04:00
Dylan Thacker-SmithandGitHub 8f7f8761d1 Use Array#each instead of Array#inject to avoid an object allocation (#1341) 2020-10-29 11:24:19 -04:00
Dylan Thacker-SmithandGitHub 7754d5aef5 Attempt to strict parse variables before lax parsing in lax error mode (#1338) 2020-10-28 10:37:00 -04:00
Dylan Thacker-SmithandGitHub ae6bd9f6b0 Allow an empty variable tag during strict parsing for liquid-c compat (#1320) 2020-10-20 14:11:48 -04:00
Dylan Thacker-Smith 065ccbc4aa Remove support for taint checking 2020-07-23 16:22:46 -04:00
Dylan Thacker-Smith 40c68c9c83 Remove support for taint_mode on ruby versions that don't support it 2019-12-19 11:12:51 -05:00
Alessandro Diogo BrückheimerandMike Angell e83b1e4159 Add ForceEqualSignAlignment to .rubocop.yml (#1190)
* Add ForceEqualSignAlignment to .rubocop.yml

* Revert ForceEqualSignAlignment cop

* Update method alignment

* Undo addition of whitespace to improve readability

* Fix missing alignment
2019-10-21 21:18:48 +10:00
Mike AngellandGitHub 0db9c56f34 Disable rendering of tag based on register (#1162)
* Disable rendering of tag based on register

* Improvements to disable tag

* Resolve disbale tag tests

* Test disable_tags register

* disabled_tags is now always avaiable

* Allow multiple tags to be disabled at once

* Move disabled check to block_body

* Code improvements

* Remove redundant nil check

* Improve disabled tag error output

* Improve disable tag API

* Code improvements

* Switch disabled? to not mutate output

* Fix array handling shortcut in disable_tags
2019-09-26 00:18:30 +10:00
Mike AngellandGitHub b667bcb48b Shopify stye guide fixes (#1160) 2019-09-20 02:08:11 +10:00
0d26f05bb8 Enabled frozen string literals (#1154)
* Enabled frozen string literals

* Update rubocop config

* Prefer string interpolation in simple cases

Co-Authored-By: Dylan Thacker-Smith <[email protected]>
2019-09-18 13:19:45 +10:00
Mike Angell 799da202df Apply simple rubocop fixes 2019-08-31 21:58:33 +10:00
Florian Weingarten 9640e77805 render_to_output_buffer 2019-04-23 17:06:29 -04:00
Florian Weingarten 2a1ca3152d liquid without the garbage 2019-04-22 16:34:31 -04:00
Richard Monette 274f078806 defer hash allocation in parse_filter_expressions
add exploration of GC object allocation

remove performance test

can actually remove one more if branch

use named locals to improve readability
2019-03-20 13:20:31 -04:00
Stephen Paul Weber 7d13d88258 s/Traversal/ParseTreeVisitor 2018-10-18 09:38:33 -04:00
Stephen Paul Weber c11fc656cf Colocate Traversal classes with classes they traverse
This puts all knowledge of the traversal in the same file, and removes
the need for a CASES registry.
2018-10-18 09:37:48 -04:00
Ashwin Maroli e4da4d49d2 assign regex to a constant 2018-03-13 23:36:56 +05:30
Loren Hale 0b11b573d9 add global_filter
add a global filter using a proc
only add one proc and not an array
add tests to make sure the global_filter is applied after native filters
2015-07-12 16:46:43 +08:00
Dylan Thacker-Smith c535af021a Rename options instance variable in Variable and Tag. 2015-07-08 19:59:44 -04:00
Dylan Thacker-Smith c45310170b Use parse_context or options instead of @options. 2015-07-08 19:21:59 -04:00
Dylan Thacker-Smith cebf75b8d7 Implement line numbers without the Liquid::Token class. 2015-07-08 19:21:59 -04:00
Dylan Thacker-Smith c4c398174b Use early returns rather than large if in Variable#taint_check 2015-07-07 15:56:03 -04:00
Dylan Thacker-Smith 80b6ac3bc7 Add taint warnings to the context rather than the template. 2015-07-07 15:53:02 -04:00
Florian Weingarten 82269e2509 fix a few more rubocop offenses 2015-06-04 13:09:58 -04:00
Florian Weingarten 3372ca8136 Rubocop 2015-05-14 14:37:18 +00:00
Justin Li dd5ee81089 Disallow number and dash identifier prefixes 2014-10-29 12:08:00 -04:00
Justin Li 5616ddf00e Remove obsolete comment 2014-10-27 13:44:14 -04:00
Justin Li fcb23a4cd2 Disallow filters with no variable in strict mode 2014-10-27 13:34:27 -04:00
Justin Li 5d68e8803f Ensure nil works as a variable name 2014-10-21 14:03:10 -04:00
Justin Li dedd1d3dc0 Fix case where a variable name is falsy 2014-10-21 12:09:26 -04:00
Florian Weingarten 7196a2d58e Avoid parallel assignments 2014-10-18 13:58:32 +00:00
Dylan Thacker-Smith f387508666 Parse expressions in Liquid::Variable#parse. 2014-10-08 21:06:59 -04:00
Evan Huus e836024dd9 Check and handle when a tainted variable is used 2014-09-16 17:23:26 +00:00
Dylan Thacker-Smith b2a74883e9 Remove Liquid::FilterNotFoundError since it is never raised. 2014-09-15 17:42:07 -04:00
Florian Weingarten c83e1c7b6d prefix for Liquid::Error instances 2014-09-05 14:12:30 +00:00
Tristan HumeandFlorian Weingarten 3a0ee6ae91 Remove parser switching duplication 2014-09-05 14:12:29 +00:00
Florian Weingarten c2663258be Merge pull request #364 from collectiveidea/instrument-rendering-with-hooks
Profiling the rendering of a liquid template
2014-08-13 23:04:29 +02:00
Tristan Hume ffd4f9d959 Simplify secondary filter regex 2014-08-13 09:36:02 -04:00
Tristan Hume 292161865d Simplify filter parse regex 2014-08-13 09:28:01 -04:00
Jason Roelofs 173a58d36a Profile liquid rendering
Add a simple profiling system to liquid rendering. Each
liquid tag ({{ }} and {% %}) is processed through this profiling,
keeping track of the partial name (in the case of {% include %}), line
number, and the time it took to render the tag. In the case of {%
include %}, the profiler keeps track of the name of the partial and
properly links back tag rendering to the partial and line number for
easy lookup and dive down. With this, it's now possible to track down
exactly how long each tag takes to render.

These hooks get installed and uninstalled on an as-need basis so by
default there is no impact on the overall liquid execution speed.
2014-08-12 15:37:21 -04:00
Jason Hiltz-Laforge adb7d2bbb8 Reduce temporary objects during variable/filter parsing 2014-07-21 21:13:59 +00:00
Florian Weingarten 3dbb35d823 Merge branch 'freeze_all_the_things'
Conflicts:
	History.md
	lib/liquid/tags/assign.rb
	lib/liquid/tags/capture.rb
	lib/liquid/tags/decrement.rb
	lib/liquid/tags/if.rb
2014-03-24 12:39:34 -04:00