graphql_includable 0.2.6 → 0.2.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c8f20574657c12b74d55fe9a06509381ff8f424
4
- data.tar.gz: b4c9667c907560c8a3af23746272d76b81302930
3
+ metadata.gz: e0eb7cacc7d8e3cd78f249e9fe3c11a320b1632b
4
+ data.tar.gz: 972f6e0a2415a2826e34c819b14272462941e81e
5
5
  SHA512:
6
- metadata.gz: edc374917c8ecd75456226683d042a1bd8e48ddc3fbc2f030b95f94d1a40d8af631155249faa4247c6547d72bc7f9f13b972c903dec6233e42260a7320dd37a4
7
- data.tar.gz: 0374af5803f0ed1895f3304d2b86183185349b005caaab38de7020c910ef081de49ad499cc592c33d47f8a4be9bf7b061d7dffb05bcad5695b35d635a24b65fa
6
+ metadata.gz: e550a9ec0fc0abfc72eb76a8948b8e26185fdd98b8898e76b10b3139e2a44e34a9013c81c8e5ddcac87b95a2b6d02d29452ad5852ba3775e37c69b4203742be8
7
+ data.tar.gz: 61ebedb72c0dd0380248eb8765cb0bc9f4ad6d9417bb92285eeefa9a90a81af528b7e65e7de63c534f8030db926b81ea562a49fa5984cec02d76289a8048a1fa
@@ -6,9 +6,8 @@ module GraphQLIncludable
6
6
 
7
7
  module ClassMethods
8
8
  def includes_from_graphql(ctx)
9
- node = Resolver.find_node_by_return_type(ctx.irep_node, model_name)
9
+ node = Resolver.find_node_by_return_type(ctx.irep_node, name)
10
10
  generated_includes = Resolver.includes_for_node(node)
11
- byebug if generated_includes == {}
12
11
  includes(generated_includes)
13
12
  rescue => e
14
13
  self
@@ -1,72 +1,72 @@
1
1
  module GraphQLIncludable
2
2
  class Edge < GraphQL::Relay::Edge
3
- def edge
4
- return @edge if @edge
5
- join_chain = joins_along_edge
6
- edge_class_name = join_chain.shift
7
- edge_class = str_to_class(edge_class_name)
3
+ def edge_record
4
+ return @edge_record if @edge_record.present?
8
5
 
9
- root_association_key = class_to_str(parent.class)
10
- unless edge_class.reflections.keys.include?(root_association_key)
11
- is_polymorphic = true
12
- root_association_key = edge_class.reflections.select { |k, r| r.polymorphic? }.keys.first
13
- end
14
- root_node = { root_association_key.to_sym => [parent] }
15
- terminal_node = { class_to_str(node.class) => node }
16
- join_chain.reverse.each do |rel_name|
17
- terminal_node = { rel_name.to_s.pluralize => terminal_node }
18
- end
6
+ all_association_names = associations_between_node_and_parent
7
+ first_association_name = all_association_names.shift
8
+ other_association_names = all_association_names.map { |s| s.to_s.singularize }
9
+
10
+ edge_class = self.class.str_to_class(first_association_name)
11
+ is_polymorphic = edge_class.reflections.none? { |k, r| r.polymorphic? }
19
12
 
20
- search_hash = root_node.merge(terminal_node)
21
- edge_includes = join_chain.map { |s| s.to_s.singularize }
22
- edge_class = edge_class.includes(*edge_includes) unless edge_includes.empty?
23
- edge_class = edge_class.joins(root_association_key.to_sym) unless is_polymorphic
24
- @edge ||= edge_class.find_by(search_hash)
13
+ selector = edge_class
14
+ selector = selector.merge(edge_class.includes(*other_association_names)) if other_association_names.present?
15
+ selector = selector.merge(edge_class.joins(root_association_key.to_sym)) unless is_polymorphic
16
+ selector = selector.find_by(where_hash_for_edge(edge_class, all_association_names))
17
+
18
+ @edge_record = selector
25
19
  end
26
20
 
27
21
  def method_missing(method_name, *args, &block)
28
- if edge.respond_to?(method_name)
29
- edge.send(method_name, *args, &block)
30
- else
31
- super
32
- end
22
+ return super unless edge_record.respond_to?(method_name)
23
+ edge_record.send(method_name, *args, &block)
33
24
  end
34
25
 
35
26
  def respond_to_missing(method_name, include_private = false)
36
- edge.respond_to?(method_name) || super
27
+ edge_record.respond_to?(method_name) || super
37
28
  end
38
29
 
39
30
  private
31
+ def associations_between_node_and_parent
32
+ return @associations if @associations.present?
33
+
34
+ associations = []
35
+ association_name = node.class.name.pluralize.downcase.to_sym
36
+ association = parent.class.reflect_on_association(association_name)
37
+ while association.is_a?(ActiveRecord::Reflection::ThroughReflection)
38
+ associations.unshift(association.options[:through])
39
+ association = parent.class.reflect_on_association(association.options[:through])
40
+ end
40
41
 
41
- def str_to_class(str)
42
- str.to_s.singularize.camelize.constantize
43
- rescue
42
+ @associations = associations
44
43
  end
45
44
 
46
- def class_to_str(klass)
47
- klass.name.downcase
45
+ def where_hash_for_edge(edge_class, association_names)
46
+ root_association_key = self.class.class_to_str(parent.class)
47
+ unless edge_class.reflections.keys.include?(root_association_key)
48
+ root_association_key = edge_class.reflections.select { |k, r| r.polymorphic? }.keys.first
49
+ end
50
+ root_include = { root_association_key.to_sym => [parent] }
51
+
52
+ terminal_include = { self.class.class_to_str(node.class) => node }
53
+ association_names.reverse.each do |rel_name|
54
+ terminal_include = { rel_name.to_s.pluralize => terminal_include }
55
+ end
56
+
57
+ root_include.merge(terminal_include)
48
58
  end
49
59
 
50
- def joins_along_edge
51
- edge_association_name = node.class.name.pluralize.downcase.to_sym
52
- edge_association = parent.class.reflect_on_association(edge_association_name)
53
- edge_joins = []
54
- while edge_association.is_a? ActiveRecord::Reflection::ThroughReflection
55
- edge_joins.unshift edge_association.options[:through]
56
- edge_association = parent.class.reflect_on_association(edge_association.options[:through])
60
+ class << self
61
+ private
62
+ def self.str_to_class(str)
63
+ str.to_s.singularize.camelize.constantize
64
+ rescue
65
+ end
66
+
67
+ def self.class_to_str(klass)
68
+ klass.name.downcase
57
69
  end
58
- edge_joins
59
- # join_chain = []
60
- # starting_class = parent.class
61
- # node_relationship_name = class_to_str(node.class)
62
- # while starting_class
63
- # reflection = starting_class.reflect_on_association(node_relationship_name)
64
- # association_name = reflection&.options&.try(:[], :through)
65
- # join_chain << association_name if association_name
66
- # starting_class = str_to_class(association_name)
67
- # node_relationship_name = node_relationship_name.singularize
68
- # end
69
- # join_chain
70
70
  end
71
71
  end
72
72
  end
@@ -1,105 +1,113 @@
1
1
  module GraphQLIncludable
2
2
  class Resolver
3
- # Returns the first node in the tree which returns a specific type
4
- def self.find_node_by_return_type(node, desired_return_type)
5
- return_type = node.return_type.unwrap
6
- return node if return_type == desired_return_type
7
- if node.respond_to?(:scoped_children)
8
- node.scoped_children[return_type].find do |_child_name, child_node|
9
- find_node_by_return_type(child_node, desired_return_type)
3
+ class << self
4
+ # Returns the first node in the tree which returns a specific type
5
+ def find_node_by_return_type(node, desired_return_type)
6
+ return_type = node.return_type.unwrap.to_s
7
+ return node if return_type == desired_return_type
8
+ if node.respond_to?(:scoped_children)
9
+ matching_node = nil
10
+ node.scoped_children.values.each do |selections|
11
+ matching_node = selections.values.find do |child_node|
12
+ find_node_by_return_type(child_node, desired_return_type)
13
+ end
14
+ break if matching_node
15
+ end
16
+ matching_node
10
17
  end
11
18
  end
12
- end
13
-
14
- # Translate a node's selections into `includes` values
15
- # Combine and format children values
16
- # Noop on nodes that don't return AR (so no associations to include)
17
- def self.includes_for_node(node)
18
- return_model = node_return_model(node)
19
- return [] if return_model.blank?
20
19
 
21
- children = node.scoped_children[node.return_type.unwrap]
22
- nested_includes = {}
20
+ # Translate a node's selections into `includes` values
21
+ def includes_for_node(node)
22
+ return_model = node_return_model(node)
23
+ return [] if return_model.blank?
24
+ children = node.scoped_children[node.return_type.unwrap]
25
+ child_includes = children.map { |_key, child| includes_for_child(child, return_model) }
26
+ combine_child_includes(child_includes)
27
+ end
23
28
 
24
- children.each_value do |child_node|
25
- child_includes = includes_for_child(child_node, return_model)
29
+ def includes_for_child(node, parent_model)
30
+ attribute_name = node_predicted_association_name(node)
31
+ delegated_through = includes_delegated_through(parent_model, attribute_name)
32
+ delegated_model = model_name_to_class(delegated_through.last) if delegated_through.present?
33
+ association = (delegated_model || parent_model).reflect_on_association(attribute_name)
34
+ interceding_includes = []
35
+ # association = get_model_association(parent_model, attribute_name, interceding_includes)
26
36
 
27
- if child_includes.is_a?(Hash)
28
- nested_includes.merge!(child_includes)
29
- elsif child_includes.is_a?(Array)
30
- includes += child_includes
37
+ if association
38
+ child_includes = includes_for_node(node)
39
+ array_to_nested_hash(interceding_includes + [attribute_name, child_includes].reject(&:blank?))
40
+ # if node_is_relay_connection?(node)
41
+ # join_name = association.options[:through]
42
+ # edge_includes_chain = [association.name]
43
+ # edge_includes_chain << child_includes.pop[association.name.to_s.singularize.to_sym] if child_includes.last&.is_a?(Hash)
44
+ # edge_includes = array_to_nested_hash(edge_includes_chain)
45
+ # end
31
46
  else
32
- includes << child_includes
47
+ # TODO: specified includes?
48
+ [array_to_nested_hash(interceding_includes)].reject(&:blank?)
33
49
  end
34
50
  end
35
51
 
36
- includes << nested_includes if nested_includes.present?
37
- includes.uniq
38
- end
39
-
40
- def self.includes_for_child(node, parent_model)
41
- attribute_name = node_predicted_association_name(node)
42
- delegated_through = includes_delegated_through(parent_model, attribute_name)
43
- delegated_model = model_name_to_class(delegated_through.last) if delegated_through.present?
44
- (delegated_model || parent_model).reflect_on_association(association_name)
45
- association = get_model_association(parent_model, attribute_name, interceding_includes)
52
+ def combine_child_includes(child_includes)
53
+ includes = []
54
+ nested_includes = {}
55
+ child_includes.each do |child|
56
+ if child.is_a?(Hash)
57
+ nested_includes.merge!(child)
58
+ elsif child.is_a?(Array)
59
+ includes += child
60
+ else
61
+ includes << child
62
+ end
63
+ end
64
+ includes << nested_includes if nested_includes.present?
65
+ includes.uniq
66
+ end
46
67
 
47
- if association
48
- child_includes = includes_for_node(node)
49
- array_to_nested_hash(interceding_includes + [attribute_name, child_includes].reject(&:blank?))
50
- # if node_is_relay_connection?(node)
51
- # join_name = association.options[:through]
52
- # edge_includes_chain = [association.name]
53
- # edge_includes_chain << child_includes.pop[association.name.to_s.singularize.to_sym] if child_includes.last&.is_a?(Hash)
54
- # edge_includes = array_to_nested_hash(edge_includes_chain)
55
- # end
56
- else
57
- [array_to_nested_hash(interceding_includes), specified_includes].reject(&:blank?)
68
+ def model_name_to_class(model_name)
69
+ begin
70
+ model_name.to_s.camelize.constantize
71
+ rescue NameError
72
+ model_name.to_s.singularize.camelize.constantize
73
+ end
74
+ rescue
58
75
  end
59
- end
60
76
 
61
- def self.model_name_to_class(model_name)
62
- begin
63
- model_name.to_s.camelize.constantize
77
+ # Translate a node's return type to an ActiveRecord model
78
+ def node_return_model(node)
79
+ model = Object.const_get(node.return_type.unwrap.name.gsub(/(^SquareFoot|Edge$|Connection$)/, ''))
80
+ model if model < ActiveRecord::Base
64
81
  rescue NameError
65
- model_name.to_s.singularize.camelize.constantize
66
82
  end
67
- rescue
68
- end
69
83
 
70
- # Translate a node's return type to an ActiveRecord model
71
- def self.node_return_model(node)
72
- model = Object.const_get(node.return_type.unwrap.name.gsub(/(^SquareFoot|Edge$|Connection$)/, ''))
73
- model if model < ActiveRecord::Base
74
- rescue NameError
75
- end
84
+ def node_predicted_association_name(node)
85
+ definition = node.definitions.first
86
+ definition.metadata[:includes] || (definition.property || definition.name).to_sym
87
+ end
76
88
 
77
- def self.node_predicted_association_name(node)
78
- definition = node.definitions.first
79
- definition.metadata[:includes] || (definition.property || definition.name).to_sym
80
- end
89
+ # If method_name is delegated from base_model, return an array of
90
+ # associations through which those methods can be delegated
91
+ def includes_delegated_through(base_model, method_name)
92
+ chain = []
93
+ method = method_name.to_sym
94
+ model_name = base_model.instance_variable_get(:@delegate_cache).try(:[], method)
95
+ while model_name
96
+ chain << model_name
97
+ model = model_name_to_class(model_name)
98
+ model_name = model.instance_variable_get(:@delegate_cache).try(:[], method)
99
+ end
100
+ chain
101
+ end
81
102
 
82
- # If method_name is delegated from base_model, return an array of
83
- # associations through which those methods can be delegated
84
- def self.includes_delegated_through(base_model, method_name)
85
- chain = []
86
- method = method_name.to_sym
87
- model_name = base_model.instance_variable_get(:@delegate_cache).try(:[], method)
88
- while model_name
89
- chain << model_name
90
- model = model_name_to_class(model_name)
91
- model_name = model.instance_variable_get(:@delegate_cache).try(:[], method)
103
+ # Right-reduce an array into a nested hash
104
+ def array_to_nested_hash(arr)
105
+ arr.reverse.inject { |acc, item| { item => acc } } || {}
92
106
  end
93
- chain
94
- end
95
107
 
96
- # Right-reduce an array into a nested hash
97
- def self.array_to_nested_hash(arr)
98
- arr.reverse.inject { |acc, item| { item => acc } } || {}
108
+ # def node_is_relay_connection?(node)
109
+ # node.return_type.unwrap.name =~ /Connection$/
110
+ # end
99
111
  end
100
-
101
- # def self.node_is_relay_connection?(node)
102
- # node.return_type.unwrap.name =~ /Connection$/
103
- # end
104
112
  end
105
113
  end
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.2.6
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Rouse
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-05-08 00:00:00.000000000 Z
12
+ date: 2018-06-19 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: