File: static_visitor.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (171 lines) | stat: -rw-r--r-- 6,191 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
110
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# frozen_string_literal: true
module GraphQL
  module Language
    # Like `GraphQL::Language::Visitor` except it doesn't support
    # making changes to the document -- only visiting it as-is.
    class StaticVisitor
      def initialize(document)
        @document = document
      end

      # Visit `document` and all children
      # @return [void]
      def visit
        # `@document` may be any kind of node:
        visit_method = @document.visit_method
        result = public_send(visit_method, @document, nil)
        @result = if result.is_a?(Array)
          result.first
        else
          # The node wasn't modified
          @document
        end
      end

      def on_document_children(document_node)
        document_node.children.each do |child_node|
          visit_method = child_node.visit_method
          public_send(visit_method, child_node, document_node)
        end
      end

      def on_field_children(new_node)
        new_node.arguments.each do |arg_node| # rubocop:disable Development/ContextIsPassedCop
          on_argument(arg_node, new_node)
        end
        visit_directives(new_node)
        visit_selections(new_node)
      end

      def visit_directives(new_node)
        new_node.directives.each do |dir_node|
          on_directive(dir_node, new_node)
        end
      end

      def visit_selections(new_node)
        new_node.selections.each do |selection|
          case selection
          when GraphQL::Language::Nodes::Field
            on_field(selection, new_node)
          when GraphQL::Language::Nodes::InlineFragment
            on_inline_fragment(selection, new_node)
          when GraphQL::Language::Nodes::FragmentSpread
            on_fragment_spread(selection, new_node)
          else
            raise ArgumentError, "Invariant: unexpected field selection #{selection.class} (#{selection.inspect})"
          end
        end
      end

      def on_fragment_definition_children(new_node)
        visit_directives(new_node)
        visit_selections(new_node)
      end

      alias :on_inline_fragment_children :on_fragment_definition_children

      def on_operation_definition_children(new_node)
        new_node.variables.each do |arg_node|
          on_variable_definition(arg_node, new_node)
        end
        visit_directives(new_node)
        visit_selections(new_node)
      end

      def on_argument_children(new_node)
        new_node.children.each do |value_node|
          case value_node
          when Language::Nodes::VariableIdentifier
            on_variable_identifier(value_node, new_node)
          when Language::Nodes::InputObject
            on_input_object(value_node, new_node)
          when Language::Nodes::Enum
            on_enum(value_node, new_node)
          when Language::Nodes::NullValue
            on_null_value(value_node, new_node)
          else
            raise ArgumentError, "Invariant: unexpected argument value node #{value_node.class} (#{value_node.inspect})"
          end
        end
      end

      # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time

      # We don't use `alias` here because it breaks `super`
      def self.make_visit_methods(ast_node_class)
        node_method = ast_node_class.visit_method
        children_of_type = ast_node_class.children_of_type
        child_visit_method = :"#{node_method}_children"

        class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
          # The default implementation for visiting an AST node.
          # It doesn't _do_ anything, but it continues to visiting the node's children.
          # To customize this hook, override one of its make_visit_methods (or the base method?)
          # in your subclasses.
          #
          # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
          # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
          # @return [void]
          def #{node_method}(node, parent)
            #{
              if method_defined?(child_visit_method)
                "#{child_visit_method}(node)"
              elsif children_of_type
                children_of_type.map do |child_accessor, child_class|
                  "node.#{child_accessor}.each do |child_node|
                    #{child_class.visit_method}(child_node, node)
                  end"
                end.join("\n")
              else
                ""
              end
            }
          end
        RUBY
      end

      [
        Language::Nodes::Argument,
        Language::Nodes::Directive,
        Language::Nodes::DirectiveDefinition,
        Language::Nodes::DirectiveLocation,
        Language::Nodes::Document,
        Language::Nodes::Enum,
        Language::Nodes::EnumTypeDefinition,
        Language::Nodes::EnumTypeExtension,
        Language::Nodes::EnumValueDefinition,
        Language::Nodes::Field,
        Language::Nodes::FieldDefinition,
        Language::Nodes::FragmentDefinition,
        Language::Nodes::FragmentSpread,
        Language::Nodes::InlineFragment,
        Language::Nodes::InputObject,
        Language::Nodes::InputObjectTypeDefinition,
        Language::Nodes::InputObjectTypeExtension,
        Language::Nodes::InputValueDefinition,
        Language::Nodes::InterfaceTypeDefinition,
        Language::Nodes::InterfaceTypeExtension,
        Language::Nodes::ListType,
        Language::Nodes::NonNullType,
        Language::Nodes::NullValue,
        Language::Nodes::ObjectTypeDefinition,
        Language::Nodes::ObjectTypeExtension,
        Language::Nodes::OperationDefinition,
        Language::Nodes::ScalarTypeDefinition,
        Language::Nodes::ScalarTypeExtension,
        Language::Nodes::SchemaDefinition,
        Language::Nodes::SchemaExtension,
        Language::Nodes::TypeName,
        Language::Nodes::UnionTypeDefinition,
        Language::Nodes::UnionTypeExtension,
        Language::Nodes::VariableDefinition,
        Language::Nodes::VariableIdentifier,
      ].each do |ast_node_class|
        make_visit_methods(ast_node_class)
      end

      # rubocop:disable Development/NoEvalCop
    end
  end
end