graphql-active_record_batcher 0.1.0 → 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/.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
|