Class: Herb::AST::ERBRenderNode

Inherits:
Node
  • Object
show all
Includes:
Colors
Defined in:
lib/herb/ast/nodes.rb,
lib/herb/ast/erb_render_node.rb,
ext/herb/nodes.c

Overview

: type serialized_erb_render_node = { | tag_opening: Herb::Token?, | content: Herb::Token?, | tag_closing: Herb::Token?, | analyzed_ruby: nil, | prism_node: String?, | keywords: Herb::AST::RubyRenderKeywordsNode?, | body: Array, | block_arguments: Array?, | rescue_clause: Herb::AST::ERBRescueNode?, | else_clause: Herb::AST::ERBElseNode?, | ensure_clause: Herb::AST::ERBEnsureNode?, | end_node: Herb::AST::ERBEndNode?, | }

Constant Summary collapse

PARTIAL_EXTENSIONS =
Herb::PARTIAL_EXTENSIONS

Constants included from Colors

Colors::CLEAR_SCREEN, Colors::HIDE_CURSOR, Colors::SHOW_CURSOR

Instance Attribute Summary collapse

Attributes inherited from Node

#errors, #location, #source, #type

Instance Method Summary collapse

Methods included from Colors

bold, bright_magenta, cyan, dimmed, enabled?, fg, fg_bg, green, magenta, red, white, yellow

Methods inherited from Node

#class_name, #inspect_array, #inspect_errors, #node_name, #recursive_errors, #to_json

Constructor Details

#initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, keywords, body, block_arguments, rescue_clause, else_clause, ensure_clause, end_node) ⇒ ERBRenderNode

: (String, Location, Array, Herb::Token, Herb::Token, Herb::Token, nil, String, Herb::AST::RubyRenderKeywordsNode, Array, Array, Herb::AST::ERBRescueNode, Herb::AST::ERBElseNode, Herb::AST::ERBEnsureNode, Herb::AST::ERBEndNode) -> void



3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
# File 'lib/herb/ast/nodes.rb', line 3799

def initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, keywords, body, block_arguments, rescue_clause, else_clause, ensure_clause, end_node)
  super(type, location, errors)
  @tag_opening = tag_opening
  @content = content
  @tag_closing = tag_closing
  @analyzed_ruby = analyzed_ruby
  @prism_node = prism_node
  @keywords = keywords
  @body = body
  @block_arguments = block_arguments
  @rescue_clause = rescue_clause
  @else_clause = else_clause
  @ensure_clause = ensure_clause
  @end_node = end_node
end

Instance Attribute Details

#analyzed_rubyObject (readonly)

: nil



3788
3789
3790
# File 'lib/herb/ast/nodes.rb', line 3788

def analyzed_ruby
  @analyzed_ruby
end

#block_argumentsObject (readonly)

: Array?



3792
3793
3794
# File 'lib/herb/ast/nodes.rb', line 3792

def block_arguments
  @block_arguments
end

#bodyObject (readonly)

: Array



3791
3792
3793
# File 'lib/herb/ast/nodes.rb', line 3791

def body
  @body
end

#contentObject (readonly)

: Herb::Token?



3786
3787
3788
# File 'lib/herb/ast/nodes.rb', line 3786

def content
  @content
end

#else_clauseObject (readonly)

: Herb::AST::ERBElseNode?



3794
3795
3796
# File 'lib/herb/ast/nodes.rb', line 3794

def else_clause
  @else_clause
end

#end_nodeObject (readonly)

: Herb::AST::ERBEndNode?



3796
3797
3798
# File 'lib/herb/ast/nodes.rb', line 3796

def end_node
  @end_node
end

#ensure_clauseObject (readonly)

: Herb::AST::ERBEnsureNode?



3795
3796
3797
# File 'lib/herb/ast/nodes.rb', line 3795

def ensure_clause
  @ensure_clause
end

#keywordsObject (readonly)

: Herb::AST::RubyRenderKeywordsNode?



3790
3791
3792
# File 'lib/herb/ast/nodes.rb', line 3790

def keywords
  @keywords
end

#prism_nodeObject (readonly)

: String?



3789
3790
3791
# File 'lib/herb/ast/nodes.rb', line 3789

def prism_node
  @prism_node
end

#rescue_clauseObject (readonly)

: Herb::AST::ERBRescueNode?



3793
3794
3795
# File 'lib/herb/ast/nodes.rb', line 3793

def rescue_clause
  @rescue_clause
end

#tag_closingObject (readonly)

: Herb::Token?



3787
3788
3789
# File 'lib/herb/ast/nodes.rb', line 3787

def tag_closing
  @tag_closing
end

#tag_openingObject (readonly)

: Herb::Token?



3785
3786
3787
# File 'lib/herb/ast/nodes.rb', line 3785

def tag_opening
  @tag_opening
end

Instance Method Details

#accept(visitor) ⇒ Object

: (Visitor) -> void



3850
3851
3852
# File 'lib/herb/ast/nodes.rb', line 3850

def accept(visitor)
  visitor.visit_erb_render_node(self)
end

#candidate_paths(name = nil, view_root = nil, source_directory = nil) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/herb/ast/erb_render_node.rb', line 45

def candidate_paths(name = nil, view_root = nil, source_directory = nil)
  name ||= partial_path || template_name

  return [] unless name

  view_root = Pathname.new(view_root) unless view_root.nil? || view_root.is_a?(Pathname)

  directory = File.dirname(name) if name.include?("/")
  base = name.include?("/") ? File.basename(name) : name
  source_directory = Pathname.new(source_directory) if source_directory && !source_directory.is_a?(Pathname)

  PARTIAL_EXTENSIONS.flat_map do |extension|
    paths = [] #: Array[Pathname]

    if directory
      paths << view_root.join(directory, "_#{base}#{extension}") if view_root
    else
      paths << source_directory.join("_#{base}#{extension}") if source_directory
      paths << view_root.join("_#{base}#{extension}") if view_root
    end

    paths
  end
end

#child_nodesObject

: () -> Array



3855
3856
3857
# File 'lib/herb/ast/nodes.rb', line 3855

def child_nodes
  [keywords, *(body || []), *(block_arguments || []), rescue_clause, else_clause, ensure_clause, end_node]
end

#compact_child_nodesObject

: () -> Array



3860
3861
3862
# File 'lib/herb/ast/nodes.rb', line 3860

def compact_child_nodes
  child_nodes.compact
end

#deserialized_prism_nodeObject

: () -> Prism::node?



3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
# File 'lib/herb/ast/nodes.rb', line 3816

def deserialized_prism_node
  prism_node = @prism_node
  return nil unless prism_node
  return nil unless source

  begin
    require "prism"
  rescue LoadError
    warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
    return nil
  end

  Prism.load(source, prism_node).value
end

#dynamic?Boolean

Returns:

  • (Boolean)


14
15
16
# File 'lib/herb/ast/erb_render_node.rb', line 14

def dynamic?
  !static_partial? && (keywords&.object || keywords&.renderable)
end

#find_non_partial_matches(name = nil, view_root = nil, source_directory = nil) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/herb/ast/erb_render_node.rb', line 111

def find_non_partial_matches(name = nil, view_root = nil, source_directory = nil)
  name ||= partial_path || template_name

  return [] unless name

  matches = [] #: Array[String]

  PARTIAL_EXTENSIONS.each do |extension|
    if name.include?("/")
      next unless view_root

      view_root = Pathname.new(view_root) unless view_root.is_a?(Pathname)
      directory = File.dirname(name)
      base = File.basename(name)
      non_partial_path = view_root.join(directory, "#{base}#{extension}")

      if non_partial_path.exist?
        matches << "#{name}#{extension} exists as a template, not a partial. Rename to _#{base}#{extension} to use it with render"
      end
    else
      if source_directory
        source_directory = Pathname.new(source_directory) unless source_directory.is_a?(Pathname)
        non_partial_path = source_directory.join("#{name}#{extension}")

        if non_partial_path.exist?
          matches << "#{name}#{extension} exists as a template, not a partial. Rename to _#{name}#{extension} to use it with render"
        end
      end

      if view_root
        view_root = Pathname.new(view_root) unless view_root.is_a?(Pathname)
        non_partial_path = view_root.join("#{name}#{extension}")

        if non_partial_path.exist?
          matches << "#{name}#{extension} exists as a template, not a partial. Rename to _#{name}#{extension} to use it with render"
        end
      end
    end
  end

  matches.uniq
end

#inspectObject

: () -> String



3865
3866
3867
# File 'lib/herb/ast/nodes.rb', line 3865

def inspect
  tree_inspect.rstrip.gsub(/\s+$/, "")
end

#layout_nameObject



26
27
28
# File 'lib/herb/ast/erb_render_node.rb', line 26

def layout_name
  keywords&.layout&.value
end

#local_namesObject



30
31
32
# File 'lib/herb/ast/erb_render_node.rb', line 30

def local_names
  keywords&.locals&.map { |local| local.name&.value }&.compact || []
end

#partial_pathObject



18
19
20
# File 'lib/herb/ast/erb_render_node.rb', line 18

def partial_path
  keywords&.partial&.value
end

#resolve(view_root: nil, source_directory: nil) ⇒ Object



34
35
36
37
38
39
40
41
42
43
# File 'lib/herb/ast/erb_render_node.rb', line 34

def resolve(view_root: nil, source_directory: nil)
  name = partial_path || template_name

  return nil unless name

  view_root = Pathname.new(view_root) unless view_root.nil? || view_root.is_a?(Pathname)

  candidates = candidate_paths(name, view_root, source_directory)
  candidates.find(&:exist?)
end

#similar_partials(view_root: nil, source_directory: nil, limit: 3) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/herb/ast/erb_render_node.rb', line 70

def similar_partials(view_root: nil, source_directory: nil, limit: 3)
  name = partial_path || template_name

  return [] unless name

  suggestions = [] #: Array[String]

  if view_root
    view_root = Pathname.new(view_root) unless view_root.is_a?(Pathname)

    if view_root.directory?
      all_partials = Dir[File.join(view_root, "**", Herb::PARTIAL_GLOB_PATTERN)].map do |file|
        relative = Pathname.new(file).relative_path_from(view_root).to_s
        relative.sub(%r{(^|/)_}, '\1').sub(/\..*\z/, "")
      end

      spell_checker = DidYouMean::SpellChecker.new(dictionary: all_partials)
      suggestions = spell_checker.correct(name).first(limit)
    end
  elsif source_directory
    source_directory = Pathname.new(source_directory) unless source_directory.is_a?(Pathname)

    if source_directory.directory?
      local_partials = Dir[File.join(source_directory, Herb::PARTIAL_GLOB_PATTERN)].map do |file|
        File.basename(file).sub(/\A_/, "").sub(/\..*\z/, "")
      end

      unless local_partials.empty?
        spell_checker = DidYouMean::SpellChecker.new(dictionary: local_partials)
        suggestions = spell_checker.correct(name).first(limit)
      end
    end
  end

  if suggestions.empty?
    suggestions.concat(find_non_partial_matches(name, view_root, source_directory))
  end

  suggestions
end

#static_partial?Boolean

Returns:

  • (Boolean)


10
11
12
# File 'lib/herb/ast/erb_render_node.rb', line 10

def static_partial?
  keywords&.partial && !keywords&.partial&.value&.empty?
end

#template_nameObject



22
23
24
# File 'lib/herb/ast/erb_render_node.rb', line 22

def template_name
  keywords&.template_path&.value
end

#to_hashObject

: () -> serialized_erb_render_node



3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
# File 'lib/herb/ast/nodes.rb', line 3832

def to_hash
  super.merge({
    tag_opening: tag_opening,
    content: content,
    tag_closing: tag_closing,
    analyzed_ruby: analyzed_ruby,
    prism_node: prism_node,
    keywords: keywords,
    body: body,
    block_arguments: block_arguments,
    rescue_clause: rescue_clause,
    else_clause: else_clause,
    ensure_clause: ensure_clause,
    end_node: end_node,
  }) #: Herb::serialized_erb_render_node
end

#tree_inspect(indent: 0, depth: 0, depth_limit: 10) ⇒ Object

: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String



3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
# File 'lib/herb/ast/nodes.rb', line 3870

def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
  output = +""

  output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
  output += "\n"

  if depth >= depth_limit
    output += dimmed("└── [depth limit reached ...]\n\n")

    return output.gsub(/^/, "    " * indent)
  end

  output += inspect_errors(prefix: "")

  output += white("├── tag_opening: ")
  output += tag_opening ? tag_opening.tree_inspect : magenta("")
  output += "\n"
  output += white("├── content: ")
  output += content ? content.tree_inspect : magenta("")
  output += "\n"
  output += white("├── tag_closing: ")
  output += tag_closing ? tag_closing.tree_inspect : magenta("")
  output += "\n"
  if prism_node && source
    output += white("├── prism_node: ")
    output += Herb::PrismInspect.inspect_prism_serialized(prism_node, source, "")
    output += "\n"
  end
  output += white("├── keywords: ")
  if keywords
    output += "\n"
    output += "│   └── "
    output += keywords.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, "    " * (indent + 1)).lstrip.gsub(/^/, "").delete_prefix("")
  else
    output += magenta("∅\n")
  end
  output += white("├── body: ")
  output += inspect_array(body, prefix: "", indent: indent, depth: depth + 1, depth_limit: depth_limit)
  output += white("├── block_arguments: ")
  output += inspect_array(block_arguments, prefix: "", indent: indent, depth: depth + 1, depth_limit: depth_limit)
  output += white("├── rescue_clause: ")
  if rescue_clause
    output += "\n"
    output += "│   └── "
    output += rescue_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, "    " * (indent + 1)).lstrip.gsub(/^/, "").delete_prefix("")
  else
    output += magenta("∅\n")
  end
  output += white("├── else_clause: ")
  if else_clause
    output += "\n"
    output += "│   └── "
    output += else_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, "    " * (indent + 1)).lstrip.gsub(/^/, "").delete_prefix("")
  else
    output += magenta("∅\n")
  end
  output += white("├── ensure_clause: ")
  if ensure_clause
    output += "\n"
    output += "│   └── "
    output += ensure_clause.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, "    " * (indent + 1)).lstrip.gsub(/^/, "").delete_prefix("")
  else
    output += magenta("∅\n")
  end
  output += white("└── end_node: ")
  if end_node
    output += "\n"
    output += "    └── "
    output += end_node.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, "    " * (indent + 1)).lstrip.gsub(/^/, "    ").delete_prefix("    ")
  else
    output += magenta("∅\n")
  end
  output += "\n"

  output.gsub(/^/, "    " * indent)
end