graphql 0.17.2 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -0
  3. data/lib/graphql/analysis/query_depth.rb +1 -1
  4. data/lib/graphql/base_type.rb +25 -1
  5. data/lib/graphql/define.rb +2 -0
  6. data/lib/graphql/define/assign_connection.rb +11 -0
  7. data/lib/graphql/define/assign_global_id_field.rb +11 -0
  8. data/lib/graphql/define/assign_object_field.rb +21 -20
  9. data/lib/graphql/define/defined_object_proxy.rb +2 -2
  10. data/lib/graphql/define/instance_definable.rb +13 -3
  11. data/lib/graphql/field.rb +1 -1
  12. data/lib/graphql/language/generation.rb +57 -6
  13. data/lib/graphql/language/lexer.rb +434 -212
  14. data/lib/graphql/language/lexer.rl +18 -0
  15. data/lib/graphql/language/nodes.rb +75 -0
  16. data/lib/graphql/language/parser.rb +853 -341
  17. data/lib/graphql/language/parser.y +114 -17
  18. data/lib/graphql/query.rb +15 -1
  19. data/lib/graphql/relay.rb +13 -0
  20. data/lib/graphql/relay/array_connection.rb +80 -0
  21. data/lib/graphql/relay/base_connection.rb +138 -0
  22. data/lib/graphql/relay/connection_field.rb +54 -0
  23. data/lib/graphql/relay/connection_type.rb +25 -0
  24. data/lib/graphql/relay/edge.rb +22 -0
  25. data/lib/graphql/relay/edge_type.rb +14 -0
  26. data/lib/graphql/relay/global_id_resolve.rb +15 -0
  27. data/lib/graphql/relay/global_node_identification.rb +124 -0
  28. data/lib/graphql/relay/mutation.rb +146 -0
  29. data/lib/graphql/relay/page_info.rb +13 -0
  30. data/lib/graphql/relay/relation_connection.rb +98 -0
  31. data/lib/graphql/schema.rb +3 -0
  32. data/lib/graphql/schema/printer.rb +12 -2
  33. data/lib/graphql/static_validation/message.rb +9 -5
  34. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  35. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  36. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  37. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +7 -7
  38. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  39. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  40. data/lib/graphql/static_validation/rules/fields_will_merge.rb +6 -6
  41. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +17 -9
  42. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  43. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +1 -1
  44. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  45. data/lib/graphql/static_validation/rules/fragments_are_used.rb +17 -6
  46. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  47. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -2
  48. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -5
  49. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  50. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +12 -11
  51. data/lib/graphql/static_validation/type_stack.rb +33 -2
  52. data/lib/graphql/static_validation/validation_context.rb +5 -0
  53. data/lib/graphql/version.rb +1 -1
  54. data/readme.md +16 -4
  55. data/spec/graphql/analysis/analyze_query_spec.rb +31 -2
  56. data/spec/graphql/analysis/query_complexity_spec.rb +24 -0
  57. data/spec/graphql/argument_spec.rb +1 -1
  58. data/spec/graphql/define/instance_definable_spec.rb +9 -0
  59. data/spec/graphql/field_spec.rb +1 -1
  60. data/spec/graphql/internal_representation/rewrite_spec.rb +3 -3
  61. data/spec/graphql/language/generation_spec.rb +25 -4
  62. data/spec/graphql/language/parser_spec.rb +116 -1
  63. data/spec/graphql/query_spec.rb +10 -0
  64. data/spec/graphql/relay/array_connection_spec.rb +164 -0
  65. data/spec/graphql/relay/connection_type_spec.rb +37 -0
  66. data/spec/graphql/relay/global_node_identification_spec.rb +149 -0
  67. data/spec/graphql/relay/mutation_spec.rb +55 -0
  68. data/spec/graphql/relay/page_info_spec.rb +106 -0
  69. data/spec/graphql/relay/relation_connection_spec.rb +348 -0
  70. data/spec/graphql/schema/printer_spec.rb +8 -0
  71. data/spec/graphql/schema/reduce_types_spec.rb +1 -1
  72. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +12 -6
  73. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +8 -4
  74. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  75. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  76. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +7 -2
  77. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -2
  78. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  79. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +5 -3
  80. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  81. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +5 -2
  82. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +10 -2
  83. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +6 -3
  84. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +8 -4
  85. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  86. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +6 -3
  87. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
  88. data/spec/spec_helper.rb +7 -0
  89. data/spec/support/dairy_app.rb +11 -10
  90. data/spec/support/star_wars_data.rb +65 -58
  91. data/spec/support/star_wars_schema.rb +192 -54
  92. metadata +84 -2
@@ -0,0 +1,54 @@
1
+ module GraphQL
2
+ module Relay
3
+ # Provided a GraphQL field which returns a collection of items,
4
+ # `ConnectionField.create` modifies that field to expose those items
5
+ # as a collection.
6
+ #
7
+ # The original resolve proc is used to fetch items,
8
+ # then a connection implementation is fetched with {BaseConnection.connection_for_items}.
9
+ class ConnectionField
10
+ ARGUMENT_DEFINITIONS = [
11
+ ["first", GraphQL::INT_TYPE, "Returns the first _n_ elements from the list."],
12
+ ["after", GraphQL::STRING_TYPE, "Returns the elements in the list that come after the specified global ID."],
13
+ ["last", GraphQL::INT_TYPE, "Returns the last _n_ elements from the list."],
14
+ ["before", GraphQL::STRING_TYPE, "Returns the elements in the list that come before the specified global ID."],
15
+ ]
16
+
17
+ DEFAULT_ARGUMENTS = ARGUMENT_DEFINITIONS.reduce({}) do |memo, arg_defn|
18
+ argument = GraphQL::Argument.new
19
+ name, type, description = arg_defn
20
+ argument.name = name
21
+ argument.type = type
22
+ argument.description = description
23
+ memo[argument.name.to_s] = argument
24
+ memo
25
+ end
26
+
27
+ # Turn A GraphQL::Field into a connection by:
28
+ # - Merging in the default arguments
29
+ # - Transforming its resolve function to return a connection object
30
+ # @param [GraphQL::Field] A field which returns items to be wrapped as a connection
31
+ # @param max_page_size [Integer] The maximum number of items which may be requested (if a larger page is requested, it is limited to this number)
32
+ # @return [GraphQL::Field] A field which serves a connections
33
+ def self.create(underlying_field, max_page_size: nil)
34
+ underlying_field.arguments = DEFAULT_ARGUMENTS.merge(underlying_field.arguments)
35
+ original_resolve = underlying_field.resolve_proc
36
+ underlying_field.resolve = get_connection_resolve(underlying_field.name, original_resolve, max_page_size: max_page_size)
37
+ underlying_field
38
+ end
39
+
40
+ private
41
+
42
+ # Wrap the original resolve proc
43
+ # so you capture its value, then wrap it in a
44
+ # connection implementation
45
+ def self.get_connection_resolve(field_name, underlying_resolve, max_page_size: nil)
46
+ -> (obj, args, ctx) {
47
+ items = underlying_resolve.call(obj, args, ctx)
48
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_items(items)
49
+ connection_class.new(items, args, max_page_size: max_page_size, parent: obj)
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ module GraphQL
2
+ module Relay
3
+ module ConnectionType
4
+ # Create a connection which exposes edges of this type
5
+ def self.create_type(wrapped_type, edge_type: nil, edge_class: nil, &block)
6
+ edge_type ||= wrapped_type.edge_type
7
+ edge_class ||= GraphQL::Relay::Edge
8
+ connection_type_name = "#{wrapped_type.name}Connection"
9
+
10
+ connection_type = ObjectType.define do
11
+ name(connection_type_name)
12
+ field :edges, types[edge_type] do
13
+ resolve -> (obj, args, ctx) {
14
+ obj.edge_nodes.map { |item| edge_class.new(item, obj) }
15
+ }
16
+ end
17
+ field :pageInfo, !PageInfo, property: :page_info
18
+ block && instance_eval(&block)
19
+ end
20
+
21
+ connection_type
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ module GraphQL
2
+ module Relay
3
+ # Mostly an internal concern.
4
+ #
5
+ # Wraps an object as a `node`, and exposes a connection-specific `cursor`.
6
+ class Edge < GraphQL::ObjectType
7
+ attr_reader :node, :connection
8
+ def initialize(node, connection)
9
+ @node = node
10
+ @connection = connection
11
+ end
12
+
13
+ def cursor
14
+ @cursor ||= connection.cursor_from_node(node)
15
+ end
16
+
17
+ def parent
18
+ @parent ||= connection.parent
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ module GraphQL
2
+ module Relay
3
+ module EdgeType
4
+ def self.create_type(wrapped_type, name: nil, &block)
5
+ GraphQL::ObjectType.define do
6
+ name("#{wrapped_type.name}Edge")
7
+ field :node, wrapped_type
8
+ field :cursor, !types.String
9
+ block && instance_eval(&block)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module GraphQL
2
+ module Relay
3
+ class GlobalIdResolve
4
+ def initialize(type_name:, property:)
5
+ @property = property
6
+ @type_name = type_name
7
+ end
8
+
9
+ def call(obj, args, ctx)
10
+ id_value = obj.public_send(@property)
11
+ ctx.query.schema.node_identification.to_global_id(@type_name, id_value)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,124 @@
1
+ require 'singleton'
2
+ module GraphQL
3
+ module Relay
4
+ # This object provides helpers for working with global IDs.
5
+ # It's assumed you'll only have 1!
6
+ #
7
+ # GlobalIdField depends on that, since it calls class methods
8
+ # which delegate to the singleton instance.
9
+ #
10
+ class GlobalNodeIdentification
11
+ include GraphQL::Define::InstanceDefinable
12
+ accepts_definitions(:object_from_id, :type_from_object, :to_global_id, :from_global_id, :description)
13
+ lazy_defined_attr_accessor :description
14
+
15
+ class << self
16
+ attr_accessor :id_separator
17
+ end
18
+
19
+ self.id_separator = "-"
20
+
21
+ def initialize
22
+ @to_global_id_proc = DEFAULT_TO_GLOBAL_ID
23
+ @from_global_id_proc = DEFAULT_FROM_GLOBAL_ID
24
+ end
25
+
26
+ # Returns `NodeInterface`, which all Relay types must implement
27
+ def interface
28
+ @interface ||= begin
29
+ ensure_defined
30
+ ident = self
31
+ GraphQL::InterfaceType.define do
32
+ name "Node"
33
+ field :id, !types.ID
34
+ resolve_type -> (obj, schema) {
35
+ ident.type_from_object(obj)
36
+ }
37
+ end
38
+ end
39
+ end
40
+
41
+ # Returns a field for finding objects from a global ID, which Relay needs
42
+ def field
43
+ ensure_defined
44
+ ident = self
45
+ GraphQL::Field.define do
46
+ type(ident.interface)
47
+ argument :id, !types.ID
48
+ resolve -> (obj, args, ctx) {
49
+ ctx.query.schema.node_identification.object_from_id(args[:id], ctx)
50
+ }
51
+ description ident.description
52
+ end
53
+ end
54
+
55
+ DEFAULT_TO_GLOBAL_ID = -> (type_name, id) {
56
+ id_str = id.to_s
57
+ if type_name.include?(self.id_separator) || id_str.include?(self.id_separator)
58
+ raise "to_global_id(#{type_name}, #{id}) contains reserved characters `#{self.id_separator}`"
59
+ end
60
+ Base64.strict_encode64([type_name, id_str].join(self.id_separator))
61
+ }
62
+
63
+ DEFAULT_FROM_GLOBAL_ID = -> (global_id) {
64
+ Base64.decode64(global_id).split(self.id_separator)
65
+ }
66
+
67
+ # Create a global ID for type-name & ID
68
+ # (This is an opaque transform)
69
+ def to_global_id(type_name, id)
70
+ ensure_defined
71
+ @to_global_id_proc.call(type_name, id)
72
+ end
73
+
74
+ def to_global_id=(proc)
75
+ ensure_defined
76
+ @to_global_id_proc = proc
77
+ end
78
+
79
+ # Get type-name & ID from global ID
80
+ # (This reverts the opaque transform)
81
+ def from_global_id(global_id)
82
+ ensure_defined
83
+ @from_global_id_proc.call(global_id)
84
+ end
85
+
86
+ def from_global_id=(proc)
87
+ ensure_defined
88
+ @from_global_id_proc = proc
89
+ end
90
+
91
+ # Use the provided config to
92
+ # get a type for a given object
93
+ def type_from_object(object)
94
+ ensure_defined
95
+ type_result = @type_from_object_proc.call(object)
96
+ if type_result.nil?
97
+ nil
98
+ elsif !type_result.is_a?(GraphQL::BaseType)
99
+ type_str = "#{type_result} (#{type_result.class.name})"
100
+ raise "type_from_object(#{object}) returned #{type_str}, but it should return a GraphQL type"
101
+ else
102
+ type_result
103
+ end
104
+ end
105
+
106
+ def type_from_object=(proc)
107
+ ensure_defined
108
+ @type_from_object_proc = proc
109
+ end
110
+
111
+ # Use the provided config to
112
+ # get an object from a UUID
113
+ def object_from_id(id, ctx)
114
+ ensure_defined
115
+ @object_from_id_proc.call(id, ctx)
116
+ end
117
+
118
+ def object_from_id=(proc)
119
+ ensure_defined
120
+ @object_from_id_proc = proc
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,146 @@
1
+ module GraphQL
2
+ module Relay
3
+ # Define a Relay mutation:
4
+ # - give it a name (used for derived inputs & outputs)
5
+ # - declare its inputs
6
+ # - declare its outputs
7
+ # - declare the mutation procedure
8
+ #
9
+ # `resolve` should return a hash with a key for each of the `return_field`s
10
+ #
11
+ # Inputs may also contain a `clientMutationId`
12
+ #
13
+ # @example Updating the name of an item
14
+ # UpdateNameMutation = GraphQL::Relay::Mutation.define do
15
+ # name "UpdateName"
16
+ #
17
+ # input_field :name, !types.String
18
+ # input_field :itemId, !types.ID
19
+ #
20
+ # return_field :item, ItemType
21
+ #
22
+ # resolve -> (inputs, ctx) {
23
+ # item = Item.find_by_id(inputs[:id])
24
+ # item.update(name: inputs[:name])
25
+ # {item: item}
26
+ # }
27
+ # end
28
+ #
29
+ # MutationType = GraphQL::ObjectType.define do
30
+ # # The mutation object exposes a field:
31
+ # field :updateName, field: UpdateNameMutation.field
32
+ # end
33
+ #
34
+ # # Then query it:
35
+ # query_string = %|
36
+ # mutation updateName {
37
+ # updateName(input: {itemId: 1, name: "new name", clientMutationId: "1234"}) {
38
+ # item { name }
39
+ # clientMutationId
40
+ # }|
41
+ #
42
+ # GraphQL::Query.new(MySchema, query_string).result
43
+ # # {"data" => {
44
+ # # "updateName" => {
45
+ # # "item" => { "name" => "new name"},
46
+ # # "clientMutationId" => "1234"
47
+ # # }
48
+ # # }}
49
+ #
50
+ class Mutation
51
+ include GraphQL::Define::InstanceDefinable
52
+ accepts_definitions(
53
+ :name, :description, :resolve,
54
+ input_field: GraphQL::Define::AssignArgument,
55
+ return_field: GraphQL::Define::AssignObjectField,
56
+ )
57
+ lazy_defined_attr_accessor :name, :description
58
+ lazy_defined_attr_accessor :fields, :arguments
59
+
60
+ # For backwards compat, but do we need this separate API?
61
+ alias :return_fields :fields
62
+ alias :input_fields :arguments
63
+
64
+ def initialize
65
+ @fields = {}
66
+ @arguments = {}
67
+ end
68
+
69
+ def resolve=(proc)
70
+ ensure_defined
71
+ @resolve_proc = proc
72
+ end
73
+
74
+ def field
75
+ @field ||= begin
76
+ ensure_defined
77
+ field_return_type = self.return_type
78
+ field_input_type = self.input_type
79
+ field_resolve_proc = -> (obj, args, ctx){
80
+ results_hash = @resolve_proc.call(args[:input], ctx)
81
+ Result.new(arguments: args, result: results_hash)
82
+ }
83
+ GraphQL::Field.define do
84
+ type(field_return_type)
85
+ argument :input, !field_input_type
86
+ resolve(field_resolve_proc)
87
+ end
88
+ end
89
+ end
90
+
91
+ def return_type
92
+ @return_type ||= begin
93
+ ensure_defined
94
+ mutation_name = name
95
+ type_name = "#{mutation_name}Payload"
96
+ type_fields = return_fields
97
+ GraphQL::ObjectType.define do
98
+ name(type_name)
99
+ description("Autogenerated return type of #{mutation_name}")
100
+ field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
101
+ type_fields.each do |name, field_obj|
102
+ field name, field: field_obj
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ def input_type
109
+ @input_type ||= begin
110
+ ensure_defined
111
+ mutation_name = name
112
+ type_name = "#{mutation_name}Input"
113
+ type_fields = input_fields
114
+ GraphQL::InputObjectType.define do
115
+ name(type_name)
116
+ description("Autogenerated input type of #{mutation_name}")
117
+ input_field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
118
+ type_fields.each do |name, field_obj|
119
+ input_field name, field_obj.type, field_obj.description, default_value: field_obj.default_value
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ class Result
126
+ attr_reader :arguments, :result
127
+ def initialize(arguments:, result:)
128
+ @arguments = arguments
129
+ @result = result
130
+ end
131
+
132
+ def clientMutationId
133
+ arguments[:input][:clientMutationId]
134
+ end
135
+
136
+ def method_missing(name, *args, &block)
137
+ if result.key?(name)
138
+ result[name]
139
+ else
140
+ super
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,13 @@
1
+ module GraphQL
2
+ module Relay
3
+ # Wrap a Connection and expose its page info
4
+ PageInfo = GraphQL::ObjectType.define do
5
+ name("PageInfo")
6
+ description("Metadata about a connection")
7
+ field :hasNextPage, !types.Boolean, "Indicates if there are more pages to fetch", property: :has_next_page
8
+ field :hasPreviousPage, !types.Boolean, "Indicates if there are any pages prior to the current page", property: :has_previous_page
9
+ field :startCursor, types.String, "When paginating backwards, the cursor to continue", property: :start_cursor
10
+ field :endCursor, types.String, "When paginating forwards, the cursor to continue", property: :end_cursor
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,98 @@
1
+ module GraphQL
2
+ module Relay
3
+ # A connection implementation to expose SQL collection objects.
4
+ # It works for:
5
+ # - `ActiveRecord::Relation`
6
+ # - `Sequel::Dataset`
7
+ class RelationConnection < BaseConnection
8
+ def cursor_from_node(item)
9
+ offset = starting_offset + paged_nodes_array.index(item) + 1
10
+ Base64.strict_encode64(offset.to_s)
11
+ end
12
+
13
+ def has_next_page
14
+ !!(first && sliced_nodes.limit(limit + 1).count > limit)
15
+ end
16
+
17
+ def has_previous_page
18
+ !!(last && starting_offset > 0)
19
+ end
20
+
21
+ private
22
+
23
+ # apply first / last limit results
24
+ def paged_nodes
25
+ @paged_nodes ||= begin
26
+ items = sliced_nodes
27
+ items.limit(limit)
28
+ end
29
+ end
30
+
31
+ # Apply cursors to edges
32
+ def sliced_nodes
33
+ @sliced_nodes ||= begin
34
+ items = object
35
+ items.offset(starting_offset)
36
+ end
37
+ end
38
+
39
+ def offset_from_cursor(cursor)
40
+ Base64.decode64(cursor).to_i
41
+ end
42
+
43
+ def starting_offset
44
+ @starting_offset ||= begin
45
+ if before
46
+ [previous_offset, 0].max
47
+ else
48
+ previous_offset
49
+ end
50
+ end
51
+ end
52
+
53
+ # Offset from the previous selection, if there was one
54
+ # Otherwise, zero
55
+ def previous_offset
56
+ @previous_offset ||= if after
57
+ offset_from_cursor(after)
58
+ elsif before
59
+ prev_page_size = [max_page_size, last].compact.min || 0
60
+ offset_from_cursor(before) - prev_page_size - 1
61
+ else
62
+ 0
63
+ end
64
+ end
65
+
66
+ # Limit to apply to this query:
67
+ # - find a value from the query
68
+ # - don't exceed max_page_size
69
+ # - otherwise, don't limit
70
+ def limit
71
+ @limit ||= begin
72
+ limit_from_arguments = if first
73
+ first
74
+ else
75
+ if previous_offset < 0
76
+ previous_offset + (last ? last : 0)
77
+ else
78
+ last
79
+ end
80
+ end
81
+ [limit_from_arguments, max_page_size].compact.min
82
+ end
83
+ end
84
+
85
+ def paged_nodes_array
86
+ @paged_nodes_array ||= paged_nodes.to_a
87
+ end
88
+ end
89
+
90
+
91
+ if defined?(ActiveRecord::Relation)
92
+ BaseConnection.register_connection_implementation(ActiveRecord::Relation, RelationConnection)
93
+ end
94
+ if defined?(Sequel::Dataset)
95
+ BaseConnection.register_connection_implementation(Sequel::Dataset, RelationConnection)
96
+ end
97
+ end
98
+ end