Skip to content

Commit

Permalink
Liquid::Traversal
Browse files Browse the repository at this point in the history
This enables traversal over whole document tree.
  • Loading branch information
singpolyma-shopify committed Oct 2, 2018
1 parent 53b8bab commit b325070
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 8 deletions.
4 changes: 2 additions & 2 deletions lib/liquid/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def self.operators
@@operators
end

attr_reader :attachment
attr_reader :attachment, :child_condition
attr_accessor :left, :operator, :right

def initialize(left = nil, operator = nil, right = nil)
Expand Down Expand Up @@ -83,7 +83,7 @@ def inspect

protected

attr_reader :child_relation, :child_condition
attr_reader :child_relation

private

Expand Down
2 changes: 2 additions & 0 deletions lib/liquid/tags/assign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module Liquid
class Assign < Tag
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om

attr_reader :to, :from

def initialize(tag_name, markup, options)
super
if markup =~ Syntax
Expand Down
2 changes: 2 additions & 0 deletions lib/liquid/tags/case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ class Case < Block
Syntax = /(#{QuotedFragment})/o
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om

attr_reader :blocks, :left

def initialize(tag_name, markup, options)
super
@blocks = []
Expand Down
2 changes: 2 additions & 0 deletions lib/liquid/tags/cycle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class Cycle < Tag
SimpleSyntax = /\A#{QuotedFragment}+/o
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om

attr_reader :variables

def initialize(tag_name, markup, options)
super
case markup
Expand Down
4 changes: 2 additions & 2 deletions lib/liquid/tags/for.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ module Liquid
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,
:for_block, :else_block

def initialize(tag_name, markup, options)
super
Expand Down
10 changes: 6 additions & 4 deletions lib/liquid/tags/if.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@ class If < Block
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)

attr_reader :blocks

def initialize(tag_name, markup, options)
super
@blocks = []
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)
Expand Down
2 changes: 2 additions & 0 deletions lib/liquid/tags/include.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module Liquid
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

Expand Down
2 changes: 2 additions & 0 deletions lib/liquid/tags/table_row.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Liquid
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
Expand Down
120 changes: 120 additions & 0 deletions lib/liquid/traversal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# frozen_string_literal: true

module Liquid
class Traversal
def self.for(node, callbacks = Hash.new(proc {}))
kase = CASES.find { |(klass, _)| node.is_a?(klass) }&.last
(kase || self).new(node, callbacks)
end

def initialize(node, callbacks)
@node = node
@callbacks = callbacks
end

def callback_for(*classes, &block)
cb = block
cb = ->(node, _) { block[node] } if block.arity.abs == 1
cb = ->(_, _) { block[] } if block.arity.zero?
classes.each { |klass| @callbacks[klass] = cb }
self
end

def traverse(context = nil)
children.map do |node|
item, new_context = @callbacks[node.class][node, context]
[
item,
Traversal.for(node, @callbacks).traverse(
new_context.nil? ? context : new_context
)
]
end
end

protected

def children
@node.respond_to?(:nodelist) ? Array(@node.nodelist) : []
end

class Assign < Traversal
def children
[@node.from]
end
end

class Case < Traversal
def children
[@node.left] + @node.blocks
end
end

class Condition < Traversal
def children
[
@node.left, @node.right,
@node.child_condition, @node.attachment
].compact
end
end

class Cycle < Traversal
def children
Array(@node.variables)
end
end

class For < Traversal
def children
(super + [@node.limit, @node.from, @node.collection_name]).compact
end
end

class If < Traversal
def children
@node.blocks
end
end

class Include < Traversal
def children
[
@node.template_name_expr,
@node.variable_name_expr
] + @node.attributes.values
end
end

class TableRow < Traversal
def children
super + @node.attributes.values + [@node.collection_name]
end
end

class Variable < Traversal
def children
[@node.name] + @node.filters.flatten
end
end

class VariableLookup < Traversal
def children
@node.lookups
end
end

CASES = {
Liquid::Assign => Assign,
Liquid::Case => Case,
Liquid::Condition => Condition,
Liquid::Cycle => Cycle,
Liquid::For => For,
Liquid::If => If,
Liquid::Include => Include,
Liquid::TableRow => TableRow,
Liquid::Variable => Variable,
Liquid::VariableLookup => VariableLookup
}.freeze
end
end
Loading

0 comments on commit b325070

Please sign in to comment.