Class: PlatformosCheck::LiquidNode

Inherits:
Node
  • Object
show all
Defined in:
lib/platformos_check/liquid_node.rb

Overview

A node from the Liquid AST, the result of parsing a liquid file.

Constant Summary collapse

WHITESPACE =
/\s/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, parent, app_file) ⇒ LiquidNode

Returns a new instance of LiquidNode.

Raises:

  • (ArgumentError)


8
9
10
11
12
13
14
15
16
# File 'lib/platformos_check/liquid_node.rb', line 8

def initialize(value, parent, app_file)
  raise ArgumentError, "Expected a Liquid AST Node" if value.is_a?(LiquidNode)

  @value = value
  @parent = parent
  @app_file = app_file
  @tag_markup = nil
  @line_number_offset = 0
end

Instance Attribute Details

#app_fileObject (readonly)

Returns the value of attribute app_file.



6
7
8
# File 'lib/platformos_check/liquid_node.rb', line 6

def app_file
  @app_file
end

#parentObject (readonly)

Returns the value of attribute parent.



6
7
8
# File 'lib/platformos_check/liquid_node.rb', line 6

def parent
  @parent
end

#valueObject (readonly)

Returns the value of attribute value.



6
7
8
# File 'lib/platformos_check/liquid_node.rb', line 6

def value
  @value
end

Instance Method Details

#assigned_or_echoed_variable?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/platformos_check/liquid_node.rb', line 148

def assigned_or_echoed_variable?
  variable? && start_token == ""
end

#block?Boolean

A block of type of node?

Returns:

  • (Boolean)


183
184
185
# File 'lib/platformos_check/liquid_node.rb', line 183

def block?
  block_tag? || block_body? || document?
end

#block_body?Boolean

The body of blocks

Returns:

  • (Boolean)


178
179
180
# File 'lib/platformos_check/liquid_node.rb', line 178

def block_body?
  @value.is_a?(Liquid::BlockBody)
end

#block_end_end_indexObject



258
259
260
261
262
# File 'lib/platformos_check/liquid_node.rb', line 258

def block_end_end_index
  return block_end_start_index unless tag? && block?

  @block_end_end_index ||= block_end_match&.end(0) || block_start_end_index
end

#block_end_markupObject



248
249
250
# File 'lib/platformos_check/liquid_node.rb', line 248

def block_end_markup
  source[block_end_start_index...block_end_end_index]
end

#block_end_start_indexObject



252
253
254
255
256
# File 'lib/platformos_check/liquid_node.rb', line 252

def block_end_start_index
  return block_start_end_index unless tag? && block?

  @block_end_start_index ||= block_end_match&.begin(0) || block_start_end_index
end

#block_start_end_indexObject



244
245
246
# File 'lib/platformos_check/liquid_node.rb', line 244

def block_start_end_index
  @block_start_end_index ||= position.end_index + end_token.size
end

#block_start_markupObject



230
231
232
# File 'lib/platformos_check/liquid_node.rb', line 230

def block_start_markup
  source[block_start_start_index...block_start_end_index]
end

#block_start_start_indexObject



234
235
236
237
238
239
240
241
242
# File 'lib/platformos_check/liquid_node.rb', line 234

def block_start_start_index
  @block_start_start_index ||= if inside_liquid_tag?
                                 backtrack_on_whitespace(source, start_index, /[ \t]/)
                               elsif tag?
                                 backtrack_on_whitespace(source, start_index) - start_token.length
                               else
                                 position.start_index - start_token.length
                               end
end

#block_tag?Boolean

A tag %…endtag % node?

Returns:

  • (Boolean)


173
174
175
# File 'lib/platformos_check/liquid_node.rb', line 173

def block_tag?
  @value.is_a?(Liquid::Block)
end

#childrenObject

Array of children nodes.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/platformos_check/liquid_node.rb', line 19

def children
  @children ||= begin
    nodes =
      if comment?
        []
      elsif defined?(@value.class::ParseTreeVisitor)
        @value.class::ParseTreeVisitor.new(@value, {}).children
      elsif @value.respond_to?(:nodelist)
        Array(@value.nodelist)
      else
        []
      end
    # Work around a bug in Liquid::Variable::ParseTreeVisitor that doesn't return
    # the args in a hash as children nodes.
    nodes = nodes.flat_map do |node|
      case node
      when Hash
        node.values
      else
        node
      end
    end
    nodes
      .reject(&:nil?) # We don't want nil nodes, and they can happen
      .map { |node| LiquidNode.new(node, self, @app_file) }
  end
end

#comment?Boolean

A comment % block node?

Returns:

  • (Boolean)


157
158
159
# File 'lib/platformos_check/liquid_node.rb', line 157

def comment?
  @value.is_a?(Liquid::Comment)
end

#document?Boolean Also known as: root?

Top level node of every liquid_file.

Returns:

  • (Boolean)


167
168
169
# File 'lib/platformos_check/liquid_node.rb', line 167

def document?
  @value.is_a?(Liquid::Document)
end

#end_columnObject



130
131
132
# File 'lib/platformos_check/liquid_node.rb', line 130

def end_column
  position.end_column
end

#end_indexObject



122
123
124
# File 'lib/platformos_check/liquid_node.rb', line 122

def end_index
  position.end_index
end

#end_rowObject



126
127
128
# File 'lib/platformos_check/liquid_node.rb', line 126

def end_row
  position.end_row
end

#end_tokenObject



368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/platformos_check/liquid_node.rb', line 368

def end_token
  if inside_liquid_tag? && source[end_index] == "\n"
    "\n"
  elsif inside_liquid_tag?
    ""
  elsif variable? && source[end_index...end_index + 3] == "-}}"
    "-}}"
  elsif variable? && source[end_index...end_index + 2] == "}}"
    "}}"
  elsif tag? && whitespace_trimmed_end?
    "-%}"
  elsif tag?
    "%}"
  else # this could happen because we're in an assign statement (variable)
    ""
  end
end

#filtersObject

Raises:

  • (TypeError)


201
202
203
204
205
# File 'lib/platformos_check/liquid_node.rb', line 201

def filters
  raise TypeError, "Attempting to lookup filters of #{type_name}. Only variables have filters." unless variable?

  @value.filters
end

#function?Boolean

Returns:

  • (Boolean)


191
192
193
# File 'lib/platformos_check/liquid_node.rb', line 191

def function?
  @value.is_a?(PlatformosCheck::Tags::Function)
end

#inline_comment?Boolean

# comment %

Returns:

  • (Boolean)


162
163
164
# File 'lib/platformos_check/liquid_node.rb', line 162

def inline_comment?
  @value.is_a?(Liquid::InlineComment)
end

#inner_jsonObject



85
86
87
88
89
90
91
92
# File 'lib/platformos_check/liquid_node.rb', line 85

def inner_json
  return nil unless parse_json?

  @inner_json ||= JSON.parse(inner_markup)
rescue JSON::ParserError
  # Handled by ValidSchema
  @inner_json = nil
end

#inner_markupObject



79
80
81
82
83
# File 'lib/platformos_check/liquid_node.rb', line 79

def inner_markup
  return '' unless block?

  @inner_markup ||= source[block_start_end_index...block_end_start_index]
end

#inner_markup_end_columnObject



308
309
310
# File 'lib/platformos_check/liquid_node.rb', line 308

def inner_markup_end_column
  inner_markup_position.end_column
end

#inner_markup_end_indexObject



292
293
294
# File 'lib/platformos_check/liquid_node.rb', line 292

def inner_markup_end_index
  inner_markup_position.end_index
end

#inner_markup_end_rowObject



304
305
306
# File 'lib/platformos_check/liquid_node.rb', line 304

def inner_markup_end_row
  inner_markup_position.end_row
end

#inner_markup_start_columnObject



300
301
302
# File 'lib/platformos_check/liquid_node.rb', line 300

def inner_markup_start_column
  inner_markup_position.start_column
end

#inner_markup_start_indexObject



288
289
290
# File 'lib/platformos_check/liquid_node.rb', line 288

def inner_markup_start_index
  inner_markup_position.start_index
end

#inner_markup_start_rowObject



296
297
298
# File 'lib/platformos_check/liquid_node.rb', line 296

def inner_markup_start_row
  inner_markup_position.start_row
end

#inside_liquid_tag?Boolean

Is this node inside a ‘liquid … %` block?

Returns:

  • (Boolean)


315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/platformos_check/liquid_node.rb', line 315

def inside_liquid_tag?
  # What we're doing here is starting at the start of the tag and
  # backtrack on all the whitespace until we land on something. If
  # that something is {% or %-, then we can safely assume that
  # we're inside a full tag and not a liquid tag.
  @inside_liquid_tag ||= if tag? && start_index && source
                           i = 1
                           i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
                           first_two_backtracked_characters = source[(start_index - i - 1)..(start_index - i)]
                           first_two_backtracked_characters != "{%" && first_two_backtracked_characters != "%-"
                         else
                           false
                         end
end

#line_numberObject

Most nodes have a line number, but it’s not guaranteed.



101
102
103
104
105
106
107
108
# File 'lib/platformos_check/liquid_node.rb', line 101

def line_number
  if tag? && @value.respond_to?(:line_number)
    markup # initialize the line_number_offset
    @value.line_number - @line_number_offset
  elsif @value.respond_to?(:line_number)
    @value.line_number
  end
end

#literal?Boolean

Literals are hard-coded values in the liquid file.

Returns:

  • (Boolean)


135
136
137
# File 'lib/platformos_check/liquid_node.rb', line 135

def literal?
  @value.is_a?(String) || @value.is_a?(Integer)
end

#markupObject

The original source code of the node. Doesn’t contain wrapping braces.



48
49
50
51
52
53
54
55
56
# File 'lib/platformos_check/liquid_node.rb', line 48

def markup
  if tag?
    tag_markup
  elsif literal?
    value.to_s
  elsif @value.instance_variable_defined?(:@markup)
    @value.instance_variable_get(:@markup)
  end
end

#markup=(markup) ⇒ Object



94
95
96
97
98
# File 'lib/platformos_check/liquid_node.rb', line 94

def markup=(markup)
  return unless @value.instance_variable_defined?(:@markup)

  @value.instance_variable_set(:@markup, markup)
end

#outer_markupObject

The original source code of the node. Does contain wrapping braces.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/platformos_check/liquid_node.rb', line 59

def outer_markup
  if literal?
    markup
  elsif variable_lookup?
    ''
  elsif variable?
    start_token + markup + end_token
  elsif tag? && block?
    start_index = block_start_start_index
    end_index = block_start_end_index
    end_index += inner_markup.size
    end_index = find_block_delimiter(end_index)&.end(0)
    source[start_index...end_index]
  elsif tag?
    source[block_start_start_index...block_start_end_index]
  else
    inner_markup
  end
end

#outer_markup_end_columnObject



284
285
286
# File 'lib/platformos_check/liquid_node.rb', line 284

def outer_markup_end_column
  outer_markup_position.end_column
end

#outer_markup_end_indexObject



268
269
270
# File 'lib/platformos_check/liquid_node.rb', line 268

def outer_markup_end_index
  outer_markup_position.end_index
end

#outer_markup_end_rowObject



280
281
282
# File 'lib/platformos_check/liquid_node.rb', line 280

def outer_markup_end_row
  outer_markup_position.end_row
end

#outer_markup_start_columnObject



276
277
278
# File 'lib/platformos_check/liquid_node.rb', line 276

def outer_markup_start_column
  outer_markup_position.start_column
end

#outer_markup_start_indexObject



264
265
266
# File 'lib/platformos_check/liquid_node.rb', line 264

def outer_markup_start_index
  outer_markup_position.start_index
end

#outer_markup_start_rowObject



272
273
274
# File 'lib/platformos_check/liquid_node.rb', line 272

def outer_markup_start_row
  outer_markup_position.start_row
end

#parse_json?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/platformos_check/liquid_node.rb', line 187

def parse_json?
  @value.is_a?(PlatformosCheck::Tags::ParseJson)
end

#sourceObject



207
208
209
# File 'lib/platformos_check/liquid_node.rb', line 207

def source
  app_file&.source
end

#start_columnObject



118
119
120
# File 'lib/platformos_check/liquid_node.rb', line 118

def start_column
  position.start_column
end

#start_indexObject



110
111
112
# File 'lib/platformos_check/liquid_node.rb', line 110

def start_index
  position.start_index
end

#start_rowObject



114
115
116
# File 'lib/platformos_check/liquid_node.rb', line 114

def start_row
  position.start_row
end

#start_tokenObject



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/platformos_check/liquid_node.rb', line 352

def start_token
  if inside_liquid_tag?
    ""
  elsif variable? && source[start_index - 3..start_index - 1] == "{{-"
    "{{-"
  elsif variable? && source[start_index - 2..start_index - 1] == "{{"
    "{{"
  elsif tag? && whitespace_trimmed_start?
    "{%-"
  elsif tag?
    "{%"
  else
    ""
  end
end

#tag?Boolean

A tag % node?

Returns:

  • (Boolean)


140
141
142
# File 'lib/platformos_check/liquid_node.rb', line 140

def tag?
  @value.is_a?(Liquid::Tag)
end

#to_hObject

For debugging purposes, this might be easier for the eyes.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/platformos_check/liquid_node.rb', line 212

def to_h
  if literal?
    return @value
  elsif variable_lookup?
    return {
      type_name:,
      name: value.name.to_s,
      lookups: children.map(&:to_h)
    }
  end

  {
    type_name:,
    markup: outer_markup,
    children: children.map(&:to_h)
  }
end

#type_nameObject

The ‘:under_score_name` of this type of node. Used to dispatch to the `on_<type_name>` and `after_<type_name>` check methods.



197
198
199
# File 'lib/platformos_check/liquid_node.rb', line 197

def type_name
  @type_name ||= StringHelpers.underscore(StringHelpers.demodulize(@value.class.name)).to_sym
end

#variable?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/platformos_check/liquid_node.rb', line 144

def variable?
  @value.is_a?(Liquid::Variable)
end

#variable_lookup?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/platformos_check/liquid_node.rb', line 152

def variable_lookup?
  @value.is_a?(Liquid::VariableLookup)
end

#whitespace_trimmed_end?Boolean

Is this node inside a tag or variable ends starts by removing whitespace. i.e. -%} or -}}

Returns:

  • (Boolean)


342
343
344
345
346
347
348
349
350
# File 'lib/platformos_check/liquid_node.rb', line 342

def whitespace_trimmed_end?
  @whitespace_trimmed_end ||= if end_index && source && !inside_liquid_tag?
                                i = 0
                                i += 1 while source[end_index + i] =~ WHITESPACE && i < source.size
                                source[end_index + i] == "-"
                              else
                                false
                              end
end

#whitespace_trimmed_start?Boolean

Is this node inside a tag or variable that starts by removing whitespace. i.e. {%- or {{-

Returns:

  • (Boolean)


331
332
333
334
335
336
337
338
339
# File 'lib/platformos_check/liquid_node.rb', line 331

def whitespace_trimmed_start?
  @whitespace_trimmed_start ||= if start_index && source && !inside_liquid_tag?
                                  i = 1
                                  i += 1 while source[start_index - i] =~ WHITESPACE && i < start_index
                                  source[start_index - i] == "-"
                                else
                                  false
                                end
end