module GraphQL
module Language
module Nodes
# AbstractNode creates classes who:
# - require their keyword arguments, throw ArgumentError if they don't match
# - expose accessors for keyword arguments
class AbstractNode
attr_accessor :line, :col
# @param options [Hash] Must contain all attributes defined by {required_attrs}, may also include `position_source`
def initialize(options={})
if options.key?(:position_source)
position_source = options.delete(:position_source)
@line, @col = position_source.line_and_column
elsif options.key?(:line)
@line = options.delete(:line)
@col = options.delete(:col)
end
initialize_node(options)
end
# This is called with node-specific options
def initialize_node(options={})
raise NotImplementedError
end
# @return [GraphQL::Language::Nodes::AbstractNode] all nodes in the tree below this one
def children
self.class.child_attributes
.map { |attr_name| public_send(attr_name) }
.flatten
end
class << self
def child_attributes(*attr_names)
@child_attributes ||= []
@child_attributes += attr_names
end
end
def position
[line, col]
end
def to_query_string
Generation.generate(self)
end
end
class WrapperType < AbstractNode
attr_accessor :of_type
def initialize_node(of_type: nil)
@of_type = of_type
end
def children
[].freeze
end
end
class NameOnlyNode < AbstractNode
attr_accessor :name
def initialize_node(name: nil)
@name = name
end
def children
[].freeze
end
end
class Argument < AbstractNode
attr_accessor :name, :value
def initialize_node(name: nil, value: nil)
@name = name
@value = value
end
def children
[value].flatten.select { |v| v.is_a?(AbstractNode) }
end
end
class Directive < AbstractNode
attr_accessor :name, :arguments
child_attributes :arguments
def initialize_node(name: nil, arguments: [])
@name = name
@arguments = arguments
end
end
class Document < AbstractNode
attr_accessor :definitions
child_attributes :definitions
def initialize_node(definitions: [])
@definitions = definitions
end
end
class Enum < NameOnlyNode; end
class Field < AbstractNode
attr_accessor :name, :alias, :arguments, :directives, :selections
child_attributes :arguments, :directives, :selections
def initialize_node(name: nil, arguments: [], directives: [], selections: [], **kwargs)
@name = name
# oops, alias is a keyword:
@alias = kwargs.fetch(:alias, nil)
@arguments = arguments
@directives = directives
@selections = selections
end
end
class FragmentDefinition < AbstractNode
attr_accessor :name, :type, :directives, :selections
child_attributes :directives, :selections
def initialize_node(name: nil, type: nil, directives: [], selections: [])
@name = name
@type = type
@directives = directives
@selections = selections
end
end
class FragmentSpread < AbstractNode
attr_accessor :name, :directives
child_attributes :directives
def initialize_node(name: nil, directives: [])
@name = name
@directives = directives
end
end
class InlineFragment < AbstractNode
attr_accessor :type, :directives, :selections
child_attributes :directives, :selections
def initialize_node(type: nil, directives: [], selections: [])
@type = type
@directives = directives
@selections = selections
end
end
class InputObject < AbstractNode
attr_accessor :arguments
child_attributes :arguments
def initialize_node(arguments: [])
@arguments = arguments
end
def to_h(options={})
arguments.inject({}) do |memo, pair|
v = pair.value
memo[pair.name] = v.is_a?(InputObject) ? v.to_h : v
memo
end
end
end
class ListType < WrapperType; end
class NonNullType < WrapperType; end
class OperationDefinition < AbstractNode
attr_accessor :operation_type, :name, :variables, :directives, :selections
child_attributes :variables, :directives, :selections
def initialize_node(operation_type: nil, name: nil, variables: [], directives: [], selections: [])
@operation_type = operation_type
@name = name
@variables = variables
@directives = directives
@selections = selections
end
end
class TypeName < NameOnlyNode; end
class VariableDefinition < AbstractNode
attr_accessor :name, :type, :default_value
def initialize_node(name: nil, type: nil, default_value: nil)
@name = name
@type = type
@default_value = default_value
end
end
class VariableIdentifier < NameOnlyNode; end
class SchemaDefinition < AbstractNode
attr_accessor :query, :mutation, :subscription
def initialize_node(query: nil, mutation: nil, subscription: nil)
@query = query
@mutation = mutation
@subscription = subscription
end
end
class ScalarTypeDefinition < NameOnlyNode; end
class ObjectTypeDefinition < AbstractNode
attr_accessor :name, :interfaces, :fields
child_attributes :fields
def initialize_node(name:, interfaces:, fields:)
@name = name
@interfaces = interfaces || []
@fields = fields
end
end
class InputValueDefinition < AbstractNode
attr_accessor :name, :type, :default_value
def initialize_node(name:, type:, default_value: nil)
@name = name
@type = type
@default_value = default_value
end
end
class FieldDefinition < AbstractNode
attr_accessor :name, :arguments, :type
child_attributes :arguments
def initialize_node(name:, arguments:, type:)
@name = name
@arguments = arguments
@type = type
end
end
class InterfaceTypeDefinition < AbstractNode
attr_accessor :name, :fields
child_attributes :fields
def initialize_node(name:, fields:)
@name = name
@fields = fields
end
end
class UnionTypeDefinition < AbstractNode
attr_accessor :name, :types
def initialize_node(name:, types:)
@name = name
@types = types
end
end
class EnumTypeDefinition < AbstractNode
attr_accessor :name, :values
def initialize_node(name:, values:)
@name = name
@values = values
end
end
class InputObjectTypeDefinition < AbstractNode
attr_accessor :name, :fields
child_attributes :fields
def initialize_node(name:, fields:)
@name = name
@fields = fields
end
end
end
end
end