graphql_includable 0.1.7 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/graphql_includable.rb +10 -146
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d25c20a822decf0d9e5ee56419b85a5794c9ae5f
|
4
|
+
data.tar.gz: a0a6803c0cde3cb012b57b2b6bd51bdad46d9971
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e70cf65b71d6299ed2b118b600fdffc7b330c43c3a357e378c72bcfdc88623b0eed2fb7651a1531c2f9ac2b256919652a98b75487a9d05261fb24b47152dca1
|
7
|
+
data.tar.gz: f87b181a30dbb8f806380bead5fdcb163ed51762ccaaef7a0d4cb6f36b2f5cf5a9f1376bc21661baa1ea4ef4e1d9a7ec5578aeb4321920961ba85f22ac50916a
|
data/lib/graphql_includable.rb
CHANGED
@@ -1,155 +1,19 @@
|
|
1
1
|
require 'graphql'
|
2
|
-
require '
|
2
|
+
require 'graphql_includable/concern'
|
3
|
+
require 'graphql_includable/edge'
|
3
4
|
|
4
5
|
GraphQL::Field.accepts_definitions(
|
5
6
|
includes: GraphQL::Define.assign_metadata_key(:includes)
|
6
7
|
)
|
7
8
|
|
8
|
-
module
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def delegate_cache
|
18
|
-
@delegate_cache ||= {}
|
19
|
-
end
|
20
|
-
|
21
|
-
# hook ActiveSupport's delegate method to track models and where their delegated methods go
|
22
|
-
def delegate(*methods, args)
|
23
|
-
methods.each do |method|
|
24
|
-
delegate_cache[method] = args[:to]
|
25
|
-
end
|
26
|
-
super(*methods, args) if defined?(super)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.generate_includes_from_graphql(ctx, model_name)
|
31
|
-
matching_node = find_child_returning_model_name(ctx.irep_node, model_name)
|
32
|
-
includes_from_graphql_field(matching_node)
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.find_child_returning_model_name(node, model_name)
|
36
|
-
matching_node = nil
|
37
|
-
return_type = node_return_type(node)
|
38
|
-
if return_type.to_s == model_name
|
39
|
-
matching_node = node
|
40
|
-
elsif node.respond_to? :scoped_children
|
41
|
-
node.scoped_children[return_type].each do |_child_name, child_node|
|
42
|
-
matching_node = find_child_returning_model_name(child_node, model_name)
|
43
|
-
break if matching_node
|
44
|
-
end
|
45
|
-
end
|
46
|
-
matching_node
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.includes_from_graphql_field(node)
|
50
|
-
includes = []
|
51
|
-
nested_includes = {}
|
52
|
-
|
53
|
-
return_model = node_return_class(node)
|
54
|
-
return [] unless node && return_model
|
55
|
-
|
56
|
-
node.scoped_children[node_return_type(node)].each do |_child_name, child_node|
|
57
|
-
child_includes = includes_from_graphql_child(child_node, return_model)
|
58
|
-
if child_includes.is_a?(Hash)
|
59
|
-
nested_includes.merge!(child_includes)
|
60
|
-
else
|
61
|
-
child_includes = [child_includes] unless child_includes.is_a?(Array)
|
62
|
-
includes += child_includes
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
includes << nested_includes unless nested_includes.empty?
|
67
|
-
includes
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.includes_from_graphql_child(child_node, return_model)
|
71
|
-
specified_includes = child_node.definitions[0].metadata[:includes]
|
72
|
-
attribute_name = node_predicted_association_name(child_node)
|
73
|
-
includes_chain = delegated_includes_chain(return_model, attribute_name)
|
74
|
-
|
75
|
-
if model_has_association?(return_model, attribute_name, includes_chain)
|
76
|
-
child_includes = includes_from_graphql_field(child_node)
|
77
|
-
includes_chain << (specified_includes || attribute_name)
|
78
|
-
includes_chain << child_includes unless child_includes.empty?
|
79
|
-
array_to_nested_hash(includes_chain)
|
80
|
-
else
|
81
|
-
includes = []
|
82
|
-
includes << array_to_nested_hash(includes_chain) unless includes_chain.empty?
|
83
|
-
includes << specified_includes if specified_includes
|
84
|
-
includes
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.node_return_class(node)
|
89
|
-
# rubocop:disable Lint/HandleExceptions, Style/RedundantBegin
|
90
|
-
begin
|
91
|
-
Object.const_get(node_return_type(node).name)
|
92
|
-
rescue NameError
|
93
|
-
end
|
94
|
-
# rubocop:enable Lint/HandleExceptions, Style/RedundantBegin
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.node_returns_active_record?(node)
|
98
|
-
klass = node_return_class(node)
|
99
|
-
klass && klass < ActiveRecord::Base
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.node_predicted_association_name(node)
|
103
|
-
definition = node.definitions[0]
|
104
|
-
specified_includes = definition.metadata[:includes]
|
105
|
-
if specified_includes.is_a?(Symbol)
|
106
|
-
specified_includes
|
107
|
-
else
|
108
|
-
(definition.property || definition.name).to_sym
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# get unwrapped return type from a field, stripping ListType / NonNullType wrappers
|
113
|
-
def self.node_return_type(node)
|
114
|
-
type = node.return_type
|
115
|
-
type = type.of_type while type.respond_to? :of_type
|
116
|
-
type
|
117
|
-
end
|
118
|
-
|
119
|
-
def self.model_has_association?(model, association_name, includes_chain)
|
120
|
-
delegated_model = model_name_to_class(includes_chain.last) unless includes_chain.empty?
|
121
|
-
(delegated_model || model).reflect_on_association(association_name)
|
122
|
-
end
|
123
|
-
|
124
|
-
# get a 1d array of the chain of delegated model names,
|
125
|
-
# so if model A delegates method B to model C, which delegates method B to model D,
|
126
|
-
# delegated_includes_chain(A, :B) => [:C, :D]
|
127
|
-
def self.delegated_includes_chain(base_model, method_name)
|
128
|
-
chain = []
|
129
|
-
method = method_name.to_sym
|
130
|
-
model_name = base_model.instance_variable_get('@delegate_cache').try(:[], method)
|
131
|
-
while model_name
|
132
|
-
chain << model_name
|
133
|
-
model = model_name_to_class(model_name)
|
134
|
-
model_name = model.instance_variable_get('@delegate_cache').try(:[], method)
|
135
|
-
end
|
136
|
-
chain
|
137
|
-
end
|
138
|
-
|
139
|
-
# convert a 1d array into a nested hash
|
140
|
-
# e.g. [:foo, :bar, :baz] => { :foo => { :bar => :baz }}
|
141
|
-
def self.array_to_nested_hash(arr)
|
142
|
-
arr.reverse.inject { |acc, item| { item => acc } }
|
143
|
-
end
|
144
|
-
|
145
|
-
# convert a model name into a class variable,
|
146
|
-
# e.g. :search_parameters -> SearchParameters
|
147
|
-
def self.model_name_to_class(model_name)
|
148
|
-
begin
|
149
|
-
model = model_name.to_s.camelize.constantize
|
150
|
-
rescue NameError
|
151
|
-
model = model_name.to_s.singularize.camelize.constantize
|
9
|
+
module GraphQL
|
10
|
+
class BaseType
|
11
|
+
def define_includable_connection(**kwargs, &block)
|
12
|
+
define_connection(
|
13
|
+
edge_class: GraphQLIncludable::Edge,
|
14
|
+
**kwargs,
|
15
|
+
&block
|
16
|
+
)
|
152
17
|
end
|
153
|
-
model
|
154
18
|
end
|
155
19
|
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.
|
4
|
+
version: 0.2.0
|
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:
|
12
|
+
date: 2018-02-09 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email:
|
@@ -40,7 +40,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
40
|
version: '0'
|
41
41
|
requirements: []
|
42
42
|
rubyforge_project:
|
43
|
-
rubygems_version: 2.6.
|
43
|
+
rubygems_version: 2.6.11
|
44
44
|
signing_key:
|
45
45
|
specification_version: 4
|
46
46
|
summary: An ActiveSupport::Concern for GraphQL Ruby to eager-load query data
|