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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 987cfab30dfdc3481d7feff6ffe29273ed8de21c
4
- data.tar.gz: 067a21a5f99cb3f68e6b1084a685e8839a82e442
3
+ metadata.gz: ee2c32fbd0c4746914bd2991fc4f48120c0ebde7
4
+ data.tar.gz: eb39eeef4dcef1422f84aff5771ee7c28b3277a8
5
5
  SHA512:
6
- metadata.gz: 3f68ca34876e1ef67ce416b428fafb11b648388b68e5c36d4f017163ebe5cd82482822014accc07bd77a981e6c873975b7b20e8c6a8f7560b55b85e3007b16f7
7
- data.tar.gz: b536f7dc3d2ccb195ebc70e66fcf4326185236acebcf057c2dfcef4770a464c7fa66b9ffe7d54b1ec7549f62125141f4c5a0007ce9139133ab55f4b5211e8b58
6
+ metadata.gz: 668f040a391588456525f11d27b6dcfe478fd94cef5bc30950e27076bbb369dbd6f5ae611fe5b0ecf956c63d78c873a9b353e71c348d40906a2fea5be31ed00a
7
+ data.tar.gz: 4245d86955ae7d93fe41cc5ff70f434b191e9a7c7b0311173b0a96b12fb0a1de83e103cfaad96f7dcf96e5b558659c51a6b31abccdd6c358d334854cb4b7f5ec
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ *.db
2
3
  /.yardoc
3
4
  /Gemfile.lock
4
5
  /_yardoc/
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[!Dog] do
68
- preloads(:characters)
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
- ### Schema config
93
+ #### Preload multiple associations
83
94
 
84
- ```ruby
85
- Schema = GraphQL::Schema.define do
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
- # FieldInstrumenter takes care of preloading assocations you've
94
- # marked using the `preloads` attribute
95
- instrument(:field, GraphQL::ActiveRecordBatcher::FieldInstrumenter.new)
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
- - [ ] Accept an array of preloads
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
+ })
@@ -34,7 +34,9 @@ module GraphQL
34
34
  attr_reader :model, :association
35
35
 
36
36
  def association_loaded?(record)
37
- record.association(@association).loaded?
37
+ puts record
38
+ puts @association
39
+ record.association(@association).loaded?
38
40
  end
39
41
  end
40
42
  end
@@ -2,9 +2,9 @@ module GraphQL
2
2
  module ActiveRecordBatcher
3
3
  class FieldInstrumenter
4
4
  def instrument(type, field)
5
- association_to_preload = field.metadata[:preloads]
5
+ associations = field.metadata[:preloads]
6
6
 
7
- if association_to_preload
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
- validate_preload(model, association_to_preload)
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
- loader.load(obj).then { old_resolve_proc.call(obj, args, ctx) }
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 validate_preload(model, association_to_preload)
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
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module ActiveRecordBatcher
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  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.1.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-15 00:00:00.000000000 Z
11
+ date: 2017-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler