graphql-active_record_batcher 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +21 -15
- data/_test_.db +0 -0
- data/lib/graphql/active_record_batcher.rb +10 -0
- data/lib/graphql/active_record_batcher/association_loader.rb +3 -1
- data/lib/graphql/active_record_batcher/field_instrumenter.rb +36 -10
- data/lib/graphql/active_record_batcher/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee2c32fbd0c4746914bd2991fc4f48120c0ebde7
|
4
|
+
data.tar.gz: eb39eeef4dcef1422f84aff5771ee7c28b3277a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 668f040a391588456525f11d27b6dcfe478fd94cef5bc30950e27076bbb369dbd6f5ae611fe5b0ecf956c63d78c873a9b353e71c348d40906a2fea5be31ed00a
|
7
|
+
data.tar.gz: 4245d86955ae7d93fe41cc5ff70f434b191e9a7c7b0311173b0a96b12fb0a1de83e103cfaad96f7dcf96e5b558659c51a6b31abccdd6c358d334854cb4b7f5ec
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -24,6 +24,17 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
+
### Set up preloading for a Schema
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
Schema = GraphQL::Schema.define do
|
31
|
+
# Enable preloading on a per-schema basis
|
32
|
+
use_preloading
|
33
|
+
query Query
|
34
|
+
mutation Mutation
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
27
38
|
### Preloading Associations
|
28
39
|
|
29
40
|
Using GraphQL without preloading associations results in under performant API and
|
@@ -64,8 +75,8 @@ StarWarsMovie = GraphQL::ObjectType.define do
|
|
64
75
|
# the parent object
|
65
76
|
model Movie
|
66
77
|
|
67
|
-
field :characters, !types[!
|
68
|
-
preloads
|
78
|
+
field :characters, !types[!Character] do
|
79
|
+
preloads :characters
|
69
80
|
resolve ->(movie, _, _) { movie.characters }
|
70
81
|
end
|
71
82
|
end
|
@@ -79,20 +90,15 @@ Associations will now be preloaded and only 3 queries are used this time:
|
|
79
90
|
3: SELECT "characters".* FROM "characters" WHERE "characters"."movie_d" IN (1, 2)
|
80
91
|
```
|
81
92
|
|
82
|
-
|
93
|
+
#### Preload multiple associations
|
83
94
|
|
84
|
-
|
85
|
-
|
86
|
-
query Query
|
87
|
-
mutation Mutation
|
88
|
-
|
89
|
-
# GraphQL Batch setup. Handle Promise objects.
|
90
|
-
lazy_resolve(Promise, :sync)
|
91
|
-
instrument(:query, GraphQL::Batch::Setup)
|
95
|
+
If a certain call loads more than one association, you can use an
|
96
|
+
array to express the ones to preload.
|
92
97
|
|
93
|
-
|
94
|
-
|
95
|
-
|
98
|
+
```ruby
|
99
|
+
field :characters, !types[!Character] do
|
100
|
+
preloads [:characters, :planets]
|
101
|
+
resolve ->(movie, _, _) { movie.characters }
|
96
102
|
end
|
97
103
|
```
|
98
104
|
|
@@ -100,7 +106,7 @@ end
|
|
100
106
|
|
101
107
|
- [ ] Expose a way to batch finds
|
102
108
|
- [ ] Expose a way or documentation on how to batch the `node` field
|
103
|
-
- [
|
109
|
+
- [x] Accept an array of preloads
|
104
110
|
- [ ] Accept nested preloads
|
105
111
|
|
106
112
|
## License
|
data/_test_.db
CHANGED
Binary file
|
@@ -7,5 +7,15 @@ module GraphQL
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
# Accept a preloads attribute on fields
|
10
11
|
GraphQL::Field.accepts_definitions(preloads: GraphQL::Define.assign_metadata_key(:preloads))
|
12
|
+
|
13
|
+
# For association preloading we need to know which Mode we are dealing with
|
11
14
|
GraphQL::ObjectType.accepts_definitions(model: GraphQL::Define.assign_metadata_key(:model))
|
15
|
+
|
16
|
+
# A definition on schema lets us avoid documenting how to setup instrumenters
|
17
|
+
GraphQL::Schema.accepts_definitions(use_preloading: ->(schema) {
|
18
|
+
schema.lazy_methods.set(Promise, :sync)
|
19
|
+
schema.instrument(:query, GraphQL::Batch::Setup)
|
20
|
+
schema.instrument(:field, GraphQL::ActiveRecordBatcher::FieldInstrumenter.new)
|
21
|
+
})
|
@@ -2,9 +2,9 @@ module GraphQL
|
|
2
2
|
module ActiveRecordBatcher
|
3
3
|
class FieldInstrumenter
|
4
4
|
def instrument(type, field)
|
5
|
-
|
5
|
+
associations = field.metadata[:preloads]
|
6
6
|
|
7
|
-
if
|
7
|
+
if associations
|
8
8
|
# Model is needed to preload an association
|
9
9
|
unless model = type.metadata[:model]
|
10
10
|
message = "No ActiveRecord Model set on type #{type.name}'s metadata."\
|
@@ -13,17 +13,14 @@ module GraphQL
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# Make sure the association exists on the model
|
16
|
-
|
17
|
-
|
18
|
-
loader = GraphQL::ActiveRecordBatcher::AssociationLoader.new(
|
19
|
-
model,
|
20
|
-
association_to_preload
|
21
|
-
)
|
16
|
+
validate(model, associations)
|
22
17
|
|
23
18
|
# "Wrap" the resolve proc with our own, which returns a promise
|
24
19
|
old_resolve_proc = field.resolve_proc
|
25
20
|
new_resolve_proc = ->(obj, args, ctx) do
|
26
|
-
|
21
|
+
build_preload_promise(obj, model, associations).then do
|
22
|
+
old_resolve_proc.call(obj, args, ctx)
|
23
|
+
end
|
27
24
|
end
|
28
25
|
|
29
26
|
field.redefine do
|
@@ -36,7 +33,36 @@ module GraphQL
|
|
36
33
|
|
37
34
|
private
|
38
35
|
|
39
|
-
def
|
36
|
+
def build_preload_promise(object, model, associations)
|
37
|
+
if associations.is_a?(Array)
|
38
|
+
Promise.all(associations.map do |association|
|
39
|
+
loader_for(model, association).load(object)
|
40
|
+
end)
|
41
|
+
else
|
42
|
+
loader_for(model, associations).load(object)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def loader_for(model, association)
|
47
|
+
GraphQL::ActiveRecordBatcher::AssociationLoader.for(
|
48
|
+
model,
|
49
|
+
association
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate(model, associations)
|
54
|
+
case associations
|
55
|
+
when Symbol
|
56
|
+
validate_association(model, associations)
|
57
|
+
when Array
|
58
|
+
associations.each { |association| validate_association(model, association) }
|
59
|
+
else
|
60
|
+
raise ArgumentError, "Cannot preload associations #{associations}."\
|
61
|
+
"Use a Symbol to preload one association or an Array of Symbols to load many."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_association(model, association_to_preload)
|
40
66
|
unless model.reflect_on_association(association_to_preload)
|
41
67
|
raise ArgumentError, "No association `#{association_to_preload}` on model `#{model}`"
|
42
68
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-active_record_batcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-Andre Giroux
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|