graphql_includable 0.5.0 → 1.0.0.beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,92 +1,51 @@
1
+ GraphQL::Field.accepts_definitions(
2
+ ##
3
+ # Define Active Record includes for a field
4
+ includes: GraphQL::Define.assign_metadata_key(:includes)
5
+ )
6
+
1
7
  module GraphQLIncludable
2
- class IncludesManager
3
- def initialize(parent_attribute)
4
- @parent_attribute = parent_attribute
5
- @included_children = {}
8
+ class Resolver
9
+ def initialize(ctx)
10
+ @root_ctx = ctx
6
11
  end
7
12
 
8
- def add_child_include(association)
9
- return @included_children[association.name] if @included_children.key?(association.name)
10
-
11
- manager = IncludesManager.new(association.name)
12
- @included_children[association.name] = manager
13
- manager
14
- end
13
+ def includes_for_node(node, includes)
14
+ return includes_for_top_level_connection(node, includes) if node.definition.connection?
15
15
 
16
- def empty?
17
- @included_children.empty?
16
+ children = node.scoped_children[node.return_type.unwrap]
17
+ children.each_value do |child_node|
18
+ definition_override = node_definition_override(node, child_node)
19
+ includes_for_child(child_node, includes, definition_override)
20
+ end
18
21
  end
19
22
 
20
- def includes
21
- child_includes = {}
22
- child_includes_arr = []
23
- @included_children.each do |key, value|
24
- if value.empty?
25
- child_includes_arr << key
26
- else
27
- includes = value.includes
28
- if includes.is_a?(Array)
29
- child_includes_arr += includes
30
- else
31
- child_includes.merge!(includes)
32
- end
33
- end
34
- end
23
+ private
35
24
 
36
- if child_includes_arr.present?
37
- child_includes_arr << child_includes if child_includes.present?
38
- child_includes = child_includes_arr
39
- end
25
+ def includes_for_child(node, includes, definition_override)
26
+ return includes_for_connection(node, includes, definition_override) if node.definition.connection?
40
27
 
41
- return child_includes if @parent_attribute.nil?
42
- { @parent_attribute => child_includes }
43
- end
44
- end
28
+ builder = build_includes(node, definition_override)
29
+ return unless builder.present?
30
+ includes.merge_includes(builder.includes) unless builder.includes.empty?
45
31
 
46
- class Resolver
47
- # Returns the first node in the tree which returns a specific type
48
- def self.find_node_by_return_type(node, desired_return_type)
49
- return_type = node.return_type.unwrap.to_s
50
- return node if return_type == desired_return_type
51
- return nil unless node.respond_to?(:scoped_children)
52
-
53
- matching_node = nil
54
- node.scoped_children.values.each do |selections|
55
- matching_node = selections.values.find do |child_node|
56
- find_node_by_return_type(child_node, desired_return_type)
57
- end
58
- break if matching_node
59
- end
60
- matching_node
61
- end
32
+ return unless builder.includes?
62
33
 
63
- # Translate a node's selections into `includes` values
64
- # Combine and format children values
65
- # Noop on nodes that don't return AR (so no associations to include)
66
- def self.includes_for_node(node, includes_manager)
67
- return_model = node_return_model(node)
68
- return [] if return_model.blank?
34
+ # Determine which [nested] child Includes manager to send to the children
35
+ child_includes = includes.dig(builder.included_path)
69
36
 
70
37
  children = node.scoped_children[node.return_type.unwrap]
71
-
72
38
  children.each_value do |child_node|
73
- includes_for_child(child_node, return_model, includes_manager)
39
+ definition_override = node_definition_override(node, child_node)
40
+ includes_for_child(child_node, child_includes, definition_override)
74
41
  end
75
42
  end
76
43
 
77
44
  # rubocop:disable Metrics/AbcSize
78
45
  # rubocop:disable Metrics/MethodLength
79
- def self.includes_from_connection(node, _parent_model, associations_from_parent_model, includes_manager)
80
- # TODO: Possibly basic support for connections with only nodes
81
- return unless node.return_type.fields['edges'].edge_class <= GraphQLIncludable::Relay::EdgeWithNode
82
-
83
- edges_association = associations_from_parent_model[:edges]
84
- nodes_association = associations_from_parent_model[:nodes]
85
-
86
- edge_node_attribute = node.definition.metadata[:edge_to_node_property]
87
- edge_model = edges_association.klass
88
- edge_to_node_association = edge_model.reflect_on_association(edge_node_attribute)
89
- node_model = edge_to_node_association.klass
46
+ def includes_for_connection(node, includes, definition_override)
47
+ builder = build_connection_includes(node, definition_override)
48
+ return unless builder&.includes?
90
49
 
91
50
  connection_children = node.scoped_children[node.return_type.unwrap]
92
51
  connection_children.each_value do |connection_node|
@@ -105,109 +64,138 @@ module GraphQLIncludable
105
64
  # }
106
65
 
107
66
  if connection_node.name == 'edges'
108
- edges_includes_manager = includes_manager.add_child_include(edges_association)
67
+ edges_includes_builder = builder.edges_builder.builder
68
+ includes.merge_includes(edges_includes_builder.includes)
69
+ edges_includes = edges_includes_builder.path_leaf_includes
109
70
 
110
71
  edge_children = connection_node.scoped_children[connection_node.return_type.unwrap]
111
72
  edge_children.each_value do |edge_child_node|
112
73
  if edge_child_node.name == 'node'
113
- node_includes_manager = edges_includes_manager.add_child_include(edge_to_node_association)
74
+ node_includes_builder = builder.edges_builder.node_builder
75
+ edges_includes.merge_includes(node_includes_builder.includes)
76
+ edge_node_includes = node_includes_builder.path_leaf_includes
114
77
 
115
78
  node_children = edge_child_node.scoped_children[edge_child_node.return_type.unwrap]
116
79
  node_children.each_value do |node_child_node|
117
- includes_for_child(node_child_node, node_model, node_includes_manager)
80
+ definition_override = node_definition_override(edge_child_node, node_child_node)
81
+ includes_for_child(node_child_node, edge_node_includes, definition_override)
118
82
  end
119
83
  else
120
- includes_for_child(edge_child_node, edge_model, edges_includes_manager)
84
+ definition_override = node_definition_override(connection_node, edge_child_node)
85
+ includes_for_child(edge_child_node, edges_includes, definition_override)
121
86
  end
122
87
  end
123
88
  elsif connection_node.name == 'nodes'
124
- nodes_includes_manager = includes_manager.add_child_include(nodes_association)
89
+ nodes_includes_builder = builder.nodes_builder
90
+ includes.merge_includes(nodes_includes_builder.includes)
91
+ nodes_includes = nodes_includes_builder.path_leaf_includes
92
+
125
93
  node_children = connection_node.scoped_children[connection_node.return_type.unwrap]
126
94
  node_children.each_value do |node_child_node|
127
- includes_for_child(node_child_node, node_model, nodes_includes_manager)
95
+ definition_override = node_definition_override(connection_node, node_child_node)
96
+ includes_for_child(node_child_node, nodes_includes, definition_override)
128
97
  end
129
98
  elsif connection_node.name == 'totalCount'
130
- # Handled using `.size` - if includes() grabbed edges/nodes it will .length else, a COUNT query saving memory.
99
+ # Handled using `.size`
131
100
  end
132
101
  end
133
102
  end
134
- # rubocop:enable Metrics/MethodLength
135
- # rubocop:enable Metrics/AbcSize
136
-
137
- def self.includes_for_child(node, parent_model, includes_manager)
138
- associations = possible_associations(node, parent_model)
139
- return unless associations.present?
140
103
 
141
- if node_is_relay_connection?(node)
142
- includes_from_connection(node, parent_model, associations, includes_manager)
104
+ # Special case:
105
+ # When includes_for_node is called within a connection resolver, there is no need to use that field's nodes/edges
106
+ # includes, only edge_to_node includes
107
+ def includes_for_top_level_connection(node, includes)
108
+ connection_children = node.scoped_children[node.return_type.unwrap]
109
+ top_level_being_resolved = @root_ctx.namespace(:gql_includable)[:resolving]
110
+
111
+ if top_level_being_resolved == :edges
112
+ builder = build_connection_includes(node, nil)
113
+ return unless builder&.edges_builder&.node_builder&.includes?
114
+
115
+ edges_node = connection_children['edges']
116
+ edges_includes = includes
117
+
118
+ edge_children = edges_node.scoped_children[edges_node.return_type.unwrap]
119
+ edge_children.each_value do |edge_child_node|
120
+ if edge_child_node.name == 'node'
121
+ node_includes_builder = builder.edges_builder.node_builder
122
+ edges_includes.merge_includes(node_includes_builder.includes)
123
+ edge_node_includes = node_includes_builder.path_leaf_includes
124
+
125
+ node_children = edge_child_node.scoped_children[edge_child_node.return_type.unwrap]
126
+ node_children.each_value do |node_child_node|
127
+ definition_override = node_definition_override(edge_child_node, node_child_node)
128
+ includes_for_child(node_child_node, edge_node_includes, definition_override)
129
+ end
130
+ else
131
+ definition_override = node_definition_override(edges_node, edge_child_node)
132
+ includes_for_child(edge_child_node, edges_includes, definition_override)
133
+ end
134
+ end
143
135
  else
144
- association = associations[:default] # should only be one
145
- child_includes_manager = includes_manager.add_child_include(association)
146
- includes_for_node(node, child_includes_manager)
136
+ nodes_node = connection_children['nodes']
137
+ return unless nodes_node.present?
138
+ nodes_includes = includes
139
+
140
+ node_children = nodes_node.scoped_children[nodes_node.return_type.unwrap]
141
+ node_children.each_value do |node_child_node|
142
+ definition_override = node_definition_override(nodes_node, node_child_node)
143
+ includes_for_child(node_child_node, nodes_includes, definition_override)
144
+ end
147
145
  end
148
146
  end
147
+ # rubocop:enable Metrics/MethodLength
148
+ # rubocop:enable Metrics/AbcSize
149
149
 
150
- # rubocop:disable Lint/HandleExceptions
151
- def self.model_name_to_class(model_name)
152
- begin
153
- model_name.to_s.camelize.constantize
154
- rescue NameError
155
- model_name.to_s.singularize.camelize.constantize
156
- end
157
- rescue
158
- end
150
+ def build_includes(node, definition_override)
151
+ definition = definition_override || node.definition
152
+ includes_meta = definition.metadata[:includes]
153
+ return nil if includes_meta.blank?
159
154
 
160
- # Translate a node's return type to an ActiveRecord model
161
- def self.node_return_model(node)
162
- model = Object.const_get(node.return_type.unwrap.name.gsub(/(^SquareFoot|Edge$|Connection$)/, ''))
163
- model if model < ActiveRecord::Base
164
- rescue NameError
165
- end
166
- # rubocop:enable Lint/HandleExceptions
155
+ builder = GraphQLIncludable::IncludesBuilder.new
167
156
 
168
- def self.node_is_relay_connection?(node)
169
- node.return_type.unwrap.name =~ /Connection$/
170
- end
157
+ if includes_meta.is_a?(Proc)
158
+ if includes_meta.arity == 2
159
+ args_for_field = @root_ctx.query.arguments_for(node, node.definition)
160
+ builder.instance_exec(args_for_field, @root_ctx, &includes_meta)
161
+ else
162
+ builder.instance_exec(&includes_meta)
163
+ end
164
+ else
165
+ builder.path(includes_meta)
166
+ end
171
167
 
172
- def self.possible_associations(node, parent_model)
173
- attribute_names = node_possible_association_names(node)
174
- attribute_names.transform_values { |attribute_name| figure_out_association(attribute_name, parent_model) }.compact
168
+ builder
175
169
  end
176
170
 
177
- def self.node_possible_association_names(node)
178
- definition = node.definitions.first
179
- user_includes = definition.metadata[:includes]
171
+ def build_connection_includes(node, definition_override)
172
+ definition = definition_override || node.definition
173
+ includes_meta = definition.metadata[:includes]
174
+ return nil if includes_meta.blank?
180
175
 
181
- association_names = {}
182
- if node_is_relay_connection?(node)
183
- association_names[:edges] = user_includes[:edges] if user_includes.key?(:edges)
184
- association_names[:nodes] = user_includes[:nodes] if user_includes.key?(:nodes)
185
- return association_names if association_names.present? # This will have resolve procs for nodes and edges
176
+ builder = GraphQLIncludable::ConnectionIncludesBuilder.new
177
+ if includes_meta.arity == 2
178
+ args_for_field = @root_ctx.query.arguments_for(node, node.definition)
179
+ builder.instance_exec(args_for_field, @root_ctx, &includes_meta)
180
+ else
181
+ builder.instance_exec(&includes_meta)
186
182
  end
187
-
188
- association_names[:default] = user_includes || (definition.property || definition.name).to_sym
189
- association_names
183
+ builder
190
184
  end
191
185
 
192
- def self.figure_out_association(attribute_name, parent_model)
193
- delegated_through = includes_delegated_through(parent_model, attribute_name)
194
- delegated_model = model_name_to_class(delegated_through.last) if delegated_through.present?
195
- association = (delegated_model || parent_model).reflect_on_association(attribute_name)
196
- association
197
- end
198
-
199
- # If method_name is delegated from base_model, return an array of
200
- # associations through which those methods can be delegated
201
- def self.includes_delegated_through(base_model, method_name)
202
- chain = []
203
- method = method_name.to_sym
204
- model_name = base_model.instance_variable_get(:@delegate_cache).try(:[], method)
205
- while model_name
206
- chain << model_name
207
- model = model_name_to_class(model_name)
208
- model_name = model.instance_variable_get(:@delegate_cache).try(:[], method)
209
- end
210
- chain
186
+ def node_definition_override(parent_node, child_node)
187
+ node_return_type = parent_node.return_type.unwrap
188
+ child_node_parent_type = child_node.parent.return_type.unwrap
189
+
190
+ return nil unless child_node_parent_type != node_return_type
191
+ child_node_definition_override = nil
192
+ # Handle GraphQL interface with overridden fields
193
+ # GraphQL makes child_node.return_type the interface instance
194
+ # and therefore takes the metadata from the interface rather than the
195
+ # implementing object's overridden field instance
196
+ is_interface = node_return_type.interfaces.include?(child_node_parent_type)
197
+ child_node_definition_override = node_return_type.fields[child_node.name] if is_interface
198
+ child_node_definition_override
211
199
  end
212
200
  end
213
201
  end
@@ -1,33 +1,41 @@
1
1
  require 'graphql'
2
- require 'graphql_includable/resolver'
3
- require 'graphql_includable/concern'
4
- require 'graphql_includable/edge'
5
- require 'graphql_includable/relay/edge_with_node'
6
- require 'graphql_includable/relay/edge_with_node_connection'
7
- require 'graphql_includable/relay/edge_with_node_connection_type'
8
- require 'graphql_includable/relay/instrumentation/connection'
9
- require 'graphql_includable/new/gql_includable'
2
+ require_relative 'graphql_includable/includes_builder'
3
+ require_relative 'graphql_includable/includes'
4
+ require_relative 'graphql_includable/resolver'
5
+ require_relative 'graphql_includable/relay/edge_with_node_connection_type'
6
+ require_relative 'graphql_includable/relay/instrumentation'
10
7
 
11
- GraphQL::Field.accepts_definitions(
12
- includes: GraphQL::Define.assign_metadata_key(:includes),
13
- edge_to_node_property: GraphQL::Define.assign_metadata_key(:edge_to_node_property),
14
- resolve_edges: GraphQL::Define.assign_metadata_key(:resolve_edges),
15
- resolve_nodes: GraphQL::Define.assign_metadata_key(:resolve_nodes),
16
- # Internal use
17
- _includable_connection_marker: GraphQL::Define.assign_metadata_key(:_includable_connection_marker)
18
- )
8
+ module GraphQLIncludable
9
+ def self.includes(ctx, starting_at: nil)
10
+ ActiveSupport::Notifications.instrument('graphql_includable.includes') do |instrument|
11
+ instrument[:operation_name] = ctx.query&.operation_name
12
+ instrument[:field_name] = ctx.irep_node.name
13
+ instrument[:starting_at] = starting_at
19
14
 
20
- module GraphQL
21
- class BaseType
22
- def define_includable_connection(**kwargs, &block)
23
- warn '[DEPRECATION] `define_includable_connection` is deprecated. Use `define_connection_with_fetched_edge`.'
24
- define_connection(
25
- edge_class: GraphQLIncludable::Edge,
26
- **kwargs,
27
- &block
28
- )
15
+ includes = Includes.new(nil)
16
+ resolver = Resolver.new(ctx)
17
+
18
+ node = ctx.irep_node
19
+ node = node_for_path(node, starting_at) if starting_at.present?
20
+
21
+ generated_includes = if node.present?
22
+ resolver.includes_for_node(node, includes)
23
+ includes.active_record_includes
24
+ end
25
+
26
+ instrument[:includes] = generated_includes
27
+ generated_includes
29
28
  end
29
+ end
30
30
 
31
+ def self.node_for_path(node, path_into_query)
32
+ children = node.scoped_children[node.return_type.unwrap]&.with_indifferent_access || {}
33
+ children.dig(path_into_query)
34
+ end
35
+ end
36
+
37
+ module GraphQL
38
+ class BaseType
31
39
  def define_connection_with_fetched_edge(**kwargs, &block)
32
40
  GraphQLIncludable::Relay::EdgeWithNodeConnectionType.create_type(
33
41
  self,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_includable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 1.0.0.beta.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Rouse
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-09-24 00:00:00.000000000 Z
13
+ date: 2019-10-04 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description:
16
16
  email:
@@ -22,20 +22,12 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
  files:
24
24
  - lib/graphql_includable.rb
25
- - lib/graphql_includable/concern.rb
26
- - lib/graphql_includable/edge.rb
27
- - lib/graphql_includable/new/gql_includable.rb
28
- - lib/graphql_includable/new/includes.rb
29
- - lib/graphql_includable/new/includes_builder.rb
30
- - lib/graphql_includable/new/relay/edge_with_node.rb
31
- - lib/graphql_includable/new/relay/edge_with_node_connection.rb
32
- - lib/graphql_includable/new/relay/edge_with_node_connection_type.rb
33
- - lib/graphql_includable/new/relay/instrumentation.rb
34
- - lib/graphql_includable/new/resolver.rb
25
+ - lib/graphql_includable/includes.rb
26
+ - lib/graphql_includable/includes_builder.rb
35
27
  - lib/graphql_includable/relay/edge_with_node.rb
36
28
  - lib/graphql_includable/relay/edge_with_node_connection.rb
37
29
  - lib/graphql_includable/relay/edge_with_node_connection_type.rb
38
- - lib/graphql_includable/relay/instrumentation/connection.rb
30
+ - lib/graphql_includable/relay/instrumentation.rb
39
31
  - lib/graphql_includable/resolver.rb
40
32
  homepage: https://github.com/thesquarefoot/graphql_includable
41
33
  licenses:
@@ -52,9 +44,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
52
44
  version: '0'
53
45
  required_rubygems_version: !ruby/object:Gem::Requirement
54
46
  requirements:
55
- - - ">="
47
+ - - ">"
56
48
  - !ruby/object:Gem::Version
57
- version: '0'
49
+ version: 1.3.1
58
50
  requirements: []
59
51
  rubyforge_project:
60
52
  rubygems_version: 2.6.14.1
@@ -1,39 +0,0 @@
1
- require 'active_support/concern'
2
-
3
- module GraphQLIncludable
4
- module Concern
5
- extend ActiveSupport::Concern
6
-
7
- module ClassMethods
8
- def includes_from_graphql(ctx)
9
- warn '[DEPRECATION] `includes_from_graphql` is deprecated. See migration guide in README.'
10
- ActiveSupport::Notifications.instrument('graphql_includable.includes_from_graphql') do |instrument|
11
- instrument[:operation_name] = ctx.query&.operation_name
12
- instrument[:field_name] = ctx.irep_node.name
13
-
14
- node = Resolver.find_node_by_return_type(ctx.irep_node, name)
15
- manager = IncludesManager.new(nil)
16
- Resolver.includes_for_node(node, manager)
17
-
18
- generated_includes = manager.includes
19
- instrument[:includes] = generated_includes
20
- includes(generated_includes)
21
- end
22
- rescue => e
23
- Rails.logger.error(e)
24
- self
25
- end
26
-
27
- def delegate_cache
28
- @delegate_cache ||= {}
29
- end
30
-
31
- def delegate(*methods, args)
32
- methods.each do |method|
33
- delegate_cache[method] = args[:to]
34
- end
35
- super(*methods, args) if defined?(super)
36
- end
37
- end
38
- end
39
- end
@@ -1,108 +0,0 @@
1
- # rubocop:disable Style/ConditionalAssignment
2
- # rubocop:disable Lint/HandleExceptions
3
- # rubocop:disable Metrics/AbcSize
4
- # rubocop:disable Style/IfInsideElse
5
- # rubocop:disable Metrics/MethodLength
6
- module GraphQLIncludable
7
- class Edge < GraphQL::Relay::Edge
8
- def edge
9
- return @edge if @edge
10
-
11
- join_chain = joins_along_edge
12
- edge_class_name = join_chain.shift
13
- edge_class = str_to_class(edge_class_name)
14
-
15
- parent = if self.parent.instance_of?(GraphQLIncludable::New::Relay::LazyEvaluatedNode)
16
- parent_class = self.parent._value.class
17
- self.parent._value
18
- else
19
- parent_class = self.parent.class
20
- self.parent
21
- end
22
-
23
- root_association_key = class_to_str(parent_class)
24
- unless edge_class.reflections.keys.include?(root_association_key)
25
- is_polymorphic = true
26
- root_association_key = edge_class.reflections.select { |_k, r| r.polymorphic? }.keys.first
27
- end
28
-
29
- if parent_class.delegate_cache&.key?(edge_class_name)
30
- root_association_search_value = parent.send(parent_class.delegate_cache[edge_class_name])
31
- else
32
- root_association_search_value = parent
33
- end
34
-
35
- root_node = { root_association_key.to_sym => [root_association_search_value] }
36
- terminal_node = { class_to_str(node.class) => node }
37
- join_chain.reverse.each do |rel_name|
38
- terminal_node = { rel_name.to_s.pluralize => terminal_node }
39
- end
40
-
41
- search_hash = root_node.merge(terminal_node)
42
- edge_includes = join_chain.map { |s| s.to_s.singularize }
43
- edge_class = edge_class.includes(*edge_includes) unless edge_includes.empty?
44
- edge_class = edge_class.joins(root_association_key.to_sym) unless is_polymorphic
45
- @edge ||= edge_class.find_by(search_hash)
46
- end
47
-
48
- def method_missing(method_name, *args, &block)
49
- if edge.respond_to?(method_name)
50
- edge.send(method_name, *args, &block)
51
- else
52
- super
53
- end
54
- end
55
-
56
- def respond_to_missing?(method_name, include_private = false)
57
- edge.respond_to?(method_name) || super
58
- end
59
-
60
- private
61
-
62
- def str_to_class(str)
63
- str.to_s.singularize.camelize.constantize
64
- rescue
65
- end
66
-
67
- def class_to_str(klass)
68
- klass.name.downcase
69
- end
70
-
71
- def joins_along_edge
72
- # node.edges
73
- # edge.node
74
- # edge.parent
75
- # parent.edges
76
- # node.parents
77
- # parent.nodes
78
- edge_association_name = node.class.name.pluralize.downcase.to_sym
79
- if parent.instance_of?(GraphQLIncludable::New::Relay::LazyEvaluatedNode)
80
- parent_class = parent._value.class
81
- else
82
- if parent.class.delegate_cache&.key?(edge_association_name)
83
- parent_class = str_to_class(parent.class.delegate_cache[edge_association_name])
84
- else
85
- parent_class = parent.class
86
- end
87
- end
88
- edge_association = parent_class.reflect_on_association(edge_association_name)
89
- edge_joins = []
90
- while edge_association.is_a? ActiveRecord::Reflection::ThroughReflection
91
- edge_joins.unshift edge_association.options[:through]
92
- edge_association = parent_class.reflect_on_association(edge_association.options[:through])
93
- end
94
- edge_joins
95
- # join_chain = []
96
- # starting_class = parent.class
97
- # node_relationship_name = class_to_str(node.class)
98
- # while starting_class
99
- # reflection = starting_class.reflect_on_association(node_relationship_name)
100
- # association_name = reflection&.options&.try(:[], :through)
101
- # join_chain << association_name if association_name
102
- # starting_class = str_to_class(association_name)
103
- # node_relationship_name = node_relationship_name.singularize
104
- # end
105
- # join_chain
106
- end
107
- end
108
- end
@@ -1,49 +0,0 @@
1
- require 'graphql'
2
- require_relative 'includes_builder'
3
- require_relative 'includes'
4
- require_relative 'resolver'
5
- require_relative 'relay/edge_with_node_connection_type'
6
- require_relative 'relay/instrumentation'
7
-
8
- module GraphQLIncludable
9
- module New
10
- def self.includes(ctx, starting_at: nil)
11
- ActiveSupport::Notifications.instrument('graphql_includable.includes') do |instrument|
12
- instrument[:operation_name] = ctx.query&.operation_name
13
- instrument[:field_name] = ctx.irep_node.name
14
- instrument[:starting_at] = starting_at
15
-
16
- includes = Includes.new(nil)
17
- resolver = Resolver.new(ctx)
18
-
19
- node = ctx.irep_node
20
- node = node_for_path(node, starting_at) if starting_at.present?
21
-
22
- generated_includes = if node.present?
23
- resolver.includes_for_node(node, includes)
24
- includes.active_record_includes
25
- end
26
-
27
- instrument[:includes] = generated_includes
28
- generated_includes
29
- end
30
- end
31
-
32
- def self.node_for_path(node, path_into_query)
33
- children = node.scoped_children[node.return_type.unwrap]&.with_indifferent_access || {}
34
- children.dig(path_into_query)
35
- end
36
- end
37
- end
38
-
39
- module GraphQL
40
- class BaseType
41
- def new_define_connection_with_fetched_edge(**kwargs, &block)
42
- GraphQLIncludable::New::Relay::EdgeWithNodeConnectionType.create_type(
43
- self,
44
- **kwargs,
45
- &block
46
- )
47
- end
48
- end
49
- end