Class: GraphQL::Relay::BaseConnection

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/relay/base_connection.rb

Overview

Subclasses must implement: - #cursor_from_node, which returns an opaque cursor for the given item - #sliced_nodes, which slices by before & after - #paged_nodes, which applies first & last limits

In a subclass, you have access to - #nodes, the collection which the connection will wrap - #first, #after, #last, #before (arguments passed to the field) - #max_page_size (the specified maximum page size that can be returned from a connection)

Direct Known Subclasses

ArrayConnection, RelationConnection

Constant Summary collapse

CURSOR_SEPARATOR =

Just to encode data in the cursor, use something that won’t conflict

"---"
CONNECTION_IMPLEMENTATIONS =

Map of collection class names -> connection_classes eg {"Array" => ArrayConnection}

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nodes, arguments, field: nil, max_page_size: nil, parent: nil, context: nil) ⇒ BaseConnection

Make a connection, wrapping nodes

Parameters:

  • nodes (Object)

    The collection of nodes

  • arguments (GraphQL::Query::Arguments)

    Query arguments

  • field (GraphQL::Field) (defaults to: nil)

    The underlying field

  • max_page_size (Int) (defaults to: nil)

    The maximum number of results to return

  • parent (Object) (defaults to: nil)

    The object which this collection belongs to

  • context (GraphQL::Query::Context) (defaults to: nil)

    The context from the field being resolved



61
62
63
64
65
66
67
68
69
# File 'lib/graphql/relay/base_connection.rb', line 61

def initialize(nodes, arguments, field: nil, max_page_size: nil, parent: nil, context: nil)
  @context = context
  @nodes = nodes
  @arguments = arguments
  @field = field
  @parent = parent
  @encoder = context ? @context.schema.cursor_encoder : GraphQL::Schema::Base64Encoder
  @max_page_size = max_page_size.nil? && context ? @context.schema.default_max_page_size : max_page_size
end

Instance Attribute Details

#argumentsObject (readonly)

Returns the value of attribute arguments.



52
53
54
# File 'lib/graphql/relay/base_connection.rb', line 52

def arguments
  @arguments
end

#contextObject (readonly)

Returns the value of attribute context.



52
53
54
# File 'lib/graphql/relay/base_connection.rb', line 52

def context
  @context
end

#fieldObject (readonly)

Returns the value of attribute field.



52
53
54
# File 'lib/graphql/relay/base_connection.rb', line 52

def field
  @field
end

#max_page_sizeObject (readonly)

Returns the value of attribute max_page_size.



52
53
54
# File 'lib/graphql/relay/base_connection.rb', line 52

def max_page_size
  @max_page_size
end

#nodesObject (readonly)

Returns the value of attribute nodes.



52
53
54
# File 'lib/graphql/relay/base_connection.rb', line 52

def nodes
  @nodes
end

#parentObject (readonly)

Returns the value of attribute parent.



52
53
54
# File 'lib/graphql/relay/base_connection.rb', line 52

def parent
  @parent
end

Class Method Details

.connection_for_nodes(nodes) ⇒ subclass of BaseConnection

Find a connection implementation suitable for exposing nodes

Parameters:

  • nodes (Object)

    A collection of nodes (eg, Array, AR::Relation)

Returns:

  • (subclass of BaseConnection)

    a connection Class for wrapping nodes



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/graphql/relay/base_connection.rb', line 27

def connection_for_nodes(nodes)
  # If it's a new-style connection object, it's already ready to go
  if nodes.is_a?(GraphQL::Pagination::Connection)
    return nodes
  end
  # Check for class _names_ because classes can be redefined in Rails development
  nodes.class.ancestors.each do |ancestor|
    conn_impl = CONNECTION_IMPLEMENTATIONS[ancestor.name]
    if conn_impl
      return conn_impl
    end
  end
  # Should have found a connection during the loop:
  raise("No connection implementation to wrap #{nodes.class} (#{nodes})")
end

.register_connection_implementation(nodes_class, connection_class) ⇒ Object

Add connection_class as the connection wrapper for nodes_class eg, RelationConnection is the implementation for AR::Relation

Parameters:

  • nodes_class (Class)

    A class representing a collection (eg, Array, AR::Relation)

  • connection_class (Class)

    A class implementing Connection methods



47
48
49
# File 'lib/graphql/relay/base_connection.rb', line 47

def register_connection_implementation(nodes_class, connection_class)
  CONNECTION_IMPLEMENTATIONS[nodes_class.name] = connection_class
end

Instance Method Details

#afterString?

The value passed as after:, if there was one

Returns:

  • (String, nil)


95
96
97
# File 'lib/graphql/relay/base_connection.rb', line 95

def after
  arguments[:after]
end

#beforeString?

The value passed as before:, if there was one

Returns:

  • (String, nil)


107
108
109
# File 'lib/graphql/relay/base_connection.rb', line 107

def before
  arguments[:before]
end

#cursor_from_node(object) ⇒ Object

An opaque operation which returns a connection-specific cursor.



151
152
153
# File 'lib/graphql/relay/base_connection.rb', line 151

def cursor_from_node(object)
  raise GraphQL::RequiredImplementationMissingError, "must return a cursor for this object/connection pair"
end

#decode(data) ⇒ Object



75
76
77
78
79
# File 'lib/graphql/relay/base_connection.rb', line 75

def decode(data)
  @encoder.decode(data, nonce: true)
rescue ArgumentError
  raise GraphQL::ExecutionError, "Invalid cursor: #{data.inspect}"
end

#edge_nodesObject

These are the nodes to render for this connection, probably wrapped by Edge



113
114
115
# File 'lib/graphql/relay/base_connection.rb', line 113

def edge_nodes
  @edge_nodes ||= paged_nodes
end

#encode(data) ⇒ Object



71
72
73
# File 'lib/graphql/relay/base_connection.rb', line 71

def encode(data)
  @encoder.encode(data, nonce: true)
end

#end_cursorObject

Used by pageInfo



142
143
144
145
146
147
148
# File 'lib/graphql/relay/base_connection.rb', line 142

def end_cursor
  if end_node = (respond_to?(:paged_nodes_array, true) ? paged_nodes_array : paged_nodes).last
    return cursor_from_node(end_node)
  else
    return nil
  end
end

#firstInteger?

The value passed as first:, if there was one. Negative numbers become 0.

Returns:

  • (Integer, nil)


83
84
85
86
87
88
89
90
91
# File 'lib/graphql/relay/base_connection.rb', line 83

def first
  @first ||= begin
    capped = limit_pagination_argument(arguments[:first], max_page_size)
    if capped.nil? && last.nil?
      capped = max_page_size
    end
    capped
  end
end

#has_next_pageObject

Used by pageInfo



123
124
125
# File 'lib/graphql/relay/base_connection.rb', line 123

def has_next_page
  !!(first && sliced_nodes.count > first)
end

#has_previous_pageObject

Used by pageInfo



128
129
130
# File 'lib/graphql/relay/base_connection.rb', line 128

def has_previous_page
  !!(last && sliced_nodes.count > last)
end

#inspectObject



155
156
157
# File 'lib/graphql/relay/base_connection.rb', line 155

def inspect
  "#<GraphQL::Relay::Connection @parent=#{@parent.inspect} @arguments=#{@arguments.to_h.inspect}>"
end

#lastInteger?

The value passed as last:, if there was one. Negative numbers become 0.

Returns:

  • (Integer, nil)


101
102
103
# File 'lib/graphql/relay/base_connection.rb', line 101

def last
  @last ||= limit_pagination_argument(arguments[:last], max_page_size)
end

#page_infoObject

Support the pageInfo field



118
119
120
# File 'lib/graphql/relay/base_connection.rb', line 118

def page_info
  self
end

#start_cursorObject

Used by pageInfo



133
134
135
136
137
138
139
# File 'lib/graphql/relay/base_connection.rb', line 133

def start_cursor
  if start_node = (respond_to?(:paged_nodes_array, true) ? paged_nodes_array : paged_nodes).first
    return cursor_from_node(start_node)
  else
    return nil
  end
end