graphql-batch-edge 0.4.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f1cfb067d3ce1cf15b038bf8557342b45cdfa047
4
+ data.tar.gz: 2bf333fa584937a3e7b73ee5fd4e6921f167fe8b
5
+ SHA512:
6
+ metadata.gz: f293f1fb48b1f1480655db9b0bf6f3a7851c3250f627d3c0ddc7b48e2bfcfed6d0378fedfa94a8fb65f6cfcb19066a4927a976dbe1b03ded76d6d2b52b5ccef7
7
+ data.tar.gz: d987a5823ace6f8d79bb8c17e257bc86f88c2da8369a623519d742297d8ab60edcb62251f6ca2bf4ad854aa3f30cab68ab2c9812c4437dd6afff25a379ef2a32
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile*.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3
4
+ - 2.6
5
+ env:
6
+ - TESTING_INTERPRETER=true
7
+ - TESTING_INTERPRETER=false
@@ -0,0 +1,28 @@
1
+ # Contributing
2
+
3
+ Types of contributions we welcome:
4
+
5
+ * Reporting issues with existing features
6
+ * Bug fixes
7
+ * Performance improvements
8
+ * Documentation and/or clearer interfaces
9
+
10
+ ## Proposing Features
11
+
12
+ The main use case for this project is around batching queries
13
+ for GraphQL requests, but is open to changes that make it more
14
+ generic. This includes supporting concurrent or parallel executors.
15
+
16
+ When in doubt about whether we will be interested in including a
17
+ new feature in this project, please open an issue to propose the
18
+ feature so we can confirm the feature should be in scope for the
19
+ project before it is implemented.
20
+
21
+ ## How To Contribute
22
+
23
+ 1. Fork the [repository in github](https://github.com/Shopify/graphql-batch)
24
+ 2. Create your feature branch (`git checkout -b fix-feature`)
25
+ 3. Commit your changes (`git commit -am 'fix: Summarize change'`)
26
+ 3. Make sure all tests pass (`bundle exec rake`)
27
+ 4. Push to the branch (`git push origin fix-feature`)
28
+ 5. [Create new pull request](https://github.com/Shopify/graphql-batch/pulls)
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ if ENV["TESTING_INTERPRETER"] == "true"
6
+ gem "graphql", "1.9.0.pre4"
7
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Shopify Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,183 @@
1
+ # GraphQL::Batch
2
+
3
+ [![Build Status](https://travis-ci.org/Shopify/graphql-batch.svg?branch=master)](https://travis-ci.org/Shopify/graphql-batch)
4
+ [![Gem Version](https://badge.fury.io/rb/graphql-batch.svg)](https://rubygems.org/gems/graphql-batch)
5
+
6
+ Provides an executor for the [`graphql` gem](https://github.com/rmosolgo/graphql-ruby) which allows queries to be batched.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'graphql-batch'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install graphql-batch
23
+
24
+ ## Usage
25
+
26
+ ### Basic Usage
27
+
28
+ #### Schema Configuration
29
+
30
+ Require the library
31
+
32
+ ```ruby
33
+ require 'graphql/batch'
34
+ ```
35
+
36
+ Define a custom loader, which is initialized with arguments that are used for grouping and a perform method for performing the batch load.
37
+
38
+ ```ruby
39
+ class RecordLoader < GraphQL::Batch::Loader
40
+ def initialize(model)
41
+ @model = model
42
+ end
43
+
44
+ def perform(ids)
45
+ @model.where(id: ids).each { |record| fulfill(record.id, record) }
46
+ ids.each { |id| fulfill(id, nil) unless fulfilled?(id) }
47
+ end
48
+ end
49
+ ```
50
+
51
+ Use `GraphQL::Batch` as a plugin in your schema _after_ specifying the mutation
52
+ so that `GraphQL::Batch` can extend the mutation fields to clear the cache after
53
+ they are resolved (for graphql >= `1.5.0`).
54
+
55
+ ```ruby
56
+ class MySchema < GraphQL::Schema
57
+ query MyQueryType
58
+ mutation MyMutationType
59
+
60
+ use GraphQL::Batch
61
+ end
62
+ ```
63
+
64
+ For pre `1.5.0` versions:
65
+
66
+ ```ruby
67
+ MySchema = GraphQL::Schema.define do
68
+ query MyQueryType
69
+
70
+ GraphQL::Batch.use(self)
71
+ end
72
+ ```
73
+
74
+ #### Field Usage
75
+
76
+ The loader class can be used from the resolver for a graphql field by calling `.for` with the grouping arguments to get a loader instance, then call `.load` on that instance with the key to load.
77
+
78
+ ```ruby
79
+ field :product, Types::Product, null: true do
80
+ argument :id, ID, required: true
81
+ end
82
+
83
+ def product(id:)
84
+ RecordLoader.for(Product).load(id)
85
+ end
86
+ ```
87
+
88
+ The loader also supports batch loading an array of records instead of just a single record, via `load_many`. For example:
89
+
90
+ ```ruby
91
+ field :products, [Types::Product, null: true], null: false do
92
+ argument :ids, [ID], required: true
93
+ end
94
+
95
+ def products(ids:)
96
+ RecordLoader.for(Product).load_many(ids)
97
+ end
98
+ ```
99
+
100
+ Although this library doesn't have a dependency on active record,
101
+ the [examples directory](examples) has record and association loaders
102
+ for active record which handles edge cases like type casting ids
103
+ and overriding GraphQL::Batch::Loader#cache_key to load associations
104
+ on records with the same id.
105
+
106
+ ### Promises
107
+
108
+ GraphQL::Batch::Loader#load returns a Promise using the [promise.rb gem](https://rubygems.org/gems/promise.rb) to provide a promise based API, so you can transform the query results using `.then`
109
+
110
+ ```ruby
111
+ def product_title(id:)
112
+ RecordLoader.for(Product).load(id).then do |product|
113
+ product.title
114
+ end
115
+ end
116
+ ```
117
+
118
+ You may also need to do another query that depends on the first one to get the result, in which case the query block can return another query.
119
+
120
+ ```ruby
121
+ def product_image(id:)
122
+ RecordLoader.for(Product).load(id).then do |product|
123
+ RecordLoader.for(Image).load(product.image_id)
124
+ end
125
+ end
126
+ ```
127
+
128
+ If the second query doesn't depend on the first one, then you can use Promise.all, which allows each query in the group to be batched with other queries.
129
+
130
+ ```ruby
131
+ def all_collections
132
+ Promise.all([
133
+ CountLoader.for(Shop, :smart_collections).load(context.shop_id),
134
+ CountLoader.for(Shop, :custom_collections).load(context.shop_id),
135
+ ]).then do |results|
136
+ results.reduce(&:+)
137
+ end
138
+ end
139
+ ```
140
+
141
+ `.then` can optionally take two lambda arguments, the first of which is equivalent to passing a block to `.then`, and the second one handles exceptions. This can be used to provide a fallback
142
+
143
+ ```ruby
144
+ def product(id:)
145
+ # Try the cache first ...
146
+ CacheLoader.for(Product).load(args["id"]).then(nil, lambda do |exc|
147
+ # But if there's a connection error, go to the underlying database
148
+ raise exc unless exc.is_a?(Redis::BaseConnectionError)
149
+ logger.warn err.message
150
+ RecordLoader.for(Product).load(args["id"])
151
+ end)
152
+ end
153
+ ```
154
+
155
+ ## Unit Testing
156
+
157
+ Your loaders can be tested outside of a GraphQL query by doing the
158
+ batch loads in a block passed to GraphQL::Batch.batch. That method
159
+ will set up thread-local state to store the loaders, batch load any
160
+ promise returned from the block then clear the thread-local state
161
+ to avoid leaking state between tests.
162
+
163
+ ```ruby
164
+ def test_single_query
165
+ product = products(:snowboard)
166
+ title = GraphQL::Batch.batch do
167
+ RecordLoader.for(Product).load(product.id).then(&:title)
168
+ end
169
+ assert_equal product.title, title
170
+ end
171
+ ```
172
+
173
+ ## Development
174
+
175
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
176
+
177
+ ## Contributing
178
+
179
+ See our [contributing guidelines](CONTRIBUTING.md) for more information.
180
+
181
+ ## License
182
+
183
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "graphql/batch"
5
+
6
+ require "irb"
7
+ IRB.start
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
@@ -0,0 +1,48 @@
1
+ class AssociationLoader < GraphQL::Batch::Loader
2
+ def self.validate(model, association_name)
3
+ new(model, association_name)
4
+ nil
5
+ end
6
+
7
+ def initialize(model, association_name)
8
+ @model = model
9
+ @association_name = association_name
10
+ validate
11
+ end
12
+
13
+ def load(record)
14
+ raise TypeError, "#{@model} loader can't load association for #{record.class}" unless record.is_a?(@model)
15
+ return Promise.resolve(read_association(record)) if association_loaded?(record)
16
+ super
17
+ end
18
+
19
+ # We want to load the associations on all records, even if they have the same id
20
+ def cache_key(record)
21
+ record.object_id
22
+ end
23
+
24
+ def perform(records)
25
+ preload_association(records)
26
+ records.each { |record| fulfill(record, read_association(record)) }
27
+ end
28
+
29
+ private
30
+
31
+ def validate
32
+ unless @model.reflect_on_association(@association_name)
33
+ raise ArgumentError, "No association #{@association_name} on #{@model}"
34
+ end
35
+ end
36
+
37
+ def preload_association(records)
38
+ ::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
39
+ end
40
+
41
+ def read_association(record)
42
+ record.public_send(@association_name)
43
+ end
44
+
45
+ def association_loaded?(record)
46
+ record.association(@association_name).loaded?
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ class RecordLoader < GraphQL::Batch::Loader
2
+ def initialize(model, column: model.primary_key, where: nil)
3
+ @model = model
4
+ @column = column.to_s
5
+ @column_type = model.type_for_attribute(@column)
6
+ @where = where
7
+ end
8
+
9
+ def load(key)
10
+ super(@column_type.cast(key))
11
+ end
12
+
13
+ def perform(keys)
14
+ query(keys).each { |record| fulfill(record.public_send(@column), record) }
15
+ keys.each { |key| fulfill(key, nil) unless fulfilled?(key) }
16
+ end
17
+
18
+ private
19
+
20
+ def query(keys)
21
+ scope = @model
22
+ scope = scope.where(@where) if @where
23
+ scope.where(@column => keys)
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'graphql/batch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "graphql-batch-edge"
8
+ spec.version = GraphQL::Batch::VERSION
9
+ spec.authors = ["Dylan Thacker-Smith"]
10
+ spec.email = ["gems@shopify.com"]
11
+
12
+ spec.summary = "A query batching executor for the graphql gem (Unofficial Edge Releases)"
13
+ spec.homepage = "https://github.com/Shopify/graphql-batch"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "graphql", ">= 1.3", "< 2"
22
+ spec.add_runtime_dependency "promise.rb", "~> 0.7.2"
23
+
24
+ spec.add_development_dependency "byebug" if RUBY_ENGINE == 'ruby'
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest"
27
+ end
@@ -0,0 +1,49 @@
1
+ require "graphql"
2
+ require "promise.rb"
3
+
4
+ module GraphQL
5
+ module Batch
6
+ BrokenPromiseError = ::Promise::BrokenError
7
+ class NoExecutorError < StandardError; end
8
+
9
+ def self.batch(executor_class: GraphQL::Batch::Executor)
10
+ begin
11
+ GraphQL::Batch::Executor.start_batch(executor_class)
12
+ ::Promise.sync(yield)
13
+ ensure
14
+ GraphQL::Batch::Executor.end_batch
15
+ end
16
+ end
17
+
18
+ def self.use(schema_defn, executor_class: GraphQL::Batch::Executor)
19
+ schema = schema_defn.target
20
+ if GraphQL::VERSION >= "1.6.0"
21
+ instrumentation = GraphQL::Batch::SetupMultiplex.new(schema, executor_class: executor_class)
22
+ schema_defn.instrument(:multiplex, instrumentation)
23
+ if schema.mutation
24
+ if Gem::Version.new(GraphQL::VERSION) >= Gem::Version.new('1.9.0.pre3') &&
25
+ schema.mutation.metadata[:type_class]
26
+ require_relative "batch/mutation_field_extension"
27
+ schema.mutation.fields.each do |name, f|
28
+ field = f.metadata[:type_class]
29
+ field.extension(GraphQL::Batch::MutationFieldExtension)
30
+ end
31
+ else
32
+ schema_defn.instrument(:field, instrumentation)
33
+ end
34
+ end
35
+ else
36
+ instrumentation = GraphQL::Batch::Setup.new(schema, executor_class: executor_class)
37
+ schema_defn.instrument(:query, instrumentation)
38
+ schema_defn.instrument(:field, instrumentation)
39
+ end
40
+ schema_defn.lazy_resolve(::Promise, :sync)
41
+ end
42
+ end
43
+ end
44
+
45
+ require_relative "batch/version"
46
+ require_relative "batch/loader"
47
+ require_relative "batch/executor"
48
+ require_relative "batch/setup"
49
+ require_relative "batch/setup_multiplex"
@@ -0,0 +1,86 @@
1
+ module GraphQL::Batch
2
+ class Executor
3
+ THREAD_KEY = :"#{name}.batched_queries"
4
+ private_constant :THREAD_KEY
5
+
6
+ class << self
7
+ def current
8
+ Thread.current[THREAD_KEY]
9
+ end
10
+
11
+ def current=(executor)
12
+ Thread.current[THREAD_KEY] = executor
13
+ end
14
+
15
+ def start_batch(executor_class)
16
+ executor = Thread.current[THREAD_KEY] ||= executor_class.new
17
+ executor.increment_level
18
+ end
19
+
20
+ def end_batch
21
+ executor = current
22
+ unless executor
23
+ raise NoExecutorError, 'Cannot end a batch without an Executor.'
24
+ end
25
+ return unless executor.decrement_level < 1
26
+ self.current = nil
27
+ end
28
+ end
29
+
30
+ # Set to true when performing a batch query, otherwise, it is false.
31
+ #
32
+ # Can be used to detect unbatched queries in an ActiveSupport::Notifications.subscribe block.
33
+ attr_reader :loading
34
+
35
+ def initialize
36
+ @loaders = {}
37
+ @loading = false
38
+ @nesting_level = 0
39
+ end
40
+
41
+ def loader(key)
42
+ @loaders[key] ||= yield.tap do |loader|
43
+ loader.executor = self
44
+ loader.loader_key = key
45
+ end
46
+ end
47
+
48
+ def resolve(loader)
49
+ was_loading = @loading
50
+ @loading = true
51
+ loader.resolve
52
+ ensure
53
+ @loading = was_loading
54
+ end
55
+
56
+ def tick
57
+ resolve(@loaders.shift.last)
58
+ end
59
+
60
+ def wait_all
61
+ tick until @loaders.empty?
62
+ end
63
+
64
+ def clear
65
+ @loaders.clear
66
+ end
67
+
68
+ def increment_level
69
+ @nesting_level += 1
70
+ end
71
+
72
+ def decrement_level
73
+ @nesting_level -= 1
74
+ end
75
+
76
+ def around_promise_callbacks
77
+ # We need to set #loading to false so that any queries that happen in the promise
78
+ # callback aren't interpreted as being performed in GraphQL::Batch::Loader#perform
79
+ was_loading = @loading
80
+ @loading = false
81
+ yield
82
+ ensure
83
+ @loading = was_loading
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,133 @@
1
+ module GraphQL::Batch
2
+ class Loader
3
+ def self.for(*group_args)
4
+ loader_key = loader_key_for(*group_args)
5
+ executor = Executor.current
6
+
7
+ unless executor
8
+ raise GraphQL::Batch::NoExecutorError, 'Cannot create loader without'\
9
+ ' an Executor. Wrap the call to `for` with `GraphQL::Batch.batch`'\
10
+ ' or use `GraphQL::Batch::Setup` as a query instrumenter if'\
11
+ ' using with `graphql-ruby`'
12
+ end
13
+
14
+ executor.loader(loader_key) { new(*group_args) }
15
+ end
16
+
17
+ def self.loader_key_for(*group_args)
18
+ [self].concat(group_args)
19
+ end
20
+
21
+ def self.load(key)
22
+ self.for.load(key)
23
+ end
24
+
25
+ def self.load_many(keys)
26
+ self.for.load_many(keys)
27
+ end
28
+
29
+ attr_accessor :loader_key, :executor
30
+
31
+ def load(key)
32
+ cache[cache_key(key)] ||= begin
33
+ queue << key
34
+ ::Promise.new.tap { |promise| promise.source = self }
35
+ end
36
+ end
37
+
38
+ def load_many(keys)
39
+ ::Promise.all(keys.map { |key| load(key) })
40
+ end
41
+
42
+ def resolve #:nodoc:
43
+ return if resolved?
44
+ load_keys = queue
45
+ @queue = nil
46
+ perform(load_keys)
47
+ check_for_broken_promises(load_keys)
48
+ rescue => err
49
+ reject_pending_promises(load_keys, err)
50
+ end
51
+
52
+ # For Promise#sync
53
+ def wait #:nodoc:
54
+ if executor
55
+ executor.resolve(self)
56
+ else
57
+ resolve
58
+ end
59
+ end
60
+
61
+ def resolved?
62
+ @queue.nil? || @queue.empty?
63
+ end
64
+
65
+ protected
66
+
67
+ # Fulfill the key with provided value, for use in #perform
68
+ def fulfill(key, value)
69
+ finish_resolve(key) do |promise|
70
+ promise.fulfill(value)
71
+ end
72
+ end
73
+
74
+ def reject(key, reason)
75
+ finish_resolve(key) do |promise|
76
+ promise.reject(reason)
77
+ end
78
+ end
79
+
80
+ # Returns true when the key has already been fulfilled, otherwise returns false
81
+ def fulfilled?(key)
82
+ promise_for(key).fulfilled?
83
+ end
84
+
85
+ # Must override to load the keys and call #fulfill for each key
86
+ def perform(keys)
87
+ raise NotImplementedError
88
+ end
89
+
90
+ # Override to use a different key for the cache than the load key
91
+ def cache_key(load_key)
92
+ load_key
93
+ end
94
+
95
+ private
96
+
97
+ def finish_resolve(key)
98
+ promise = promise_for(key)
99
+ return yield(promise) unless executor
100
+ executor.around_promise_callbacks do
101
+ yield promise
102
+ end
103
+ end
104
+
105
+ def cache
106
+ @cache ||= {}
107
+ end
108
+
109
+ def queue
110
+ @queue ||= []
111
+ end
112
+
113
+ def promise_for(load_key)
114
+ cache.fetch(cache_key(load_key))
115
+ end
116
+
117
+ def reject_pending_promises(load_keys, err)
118
+ load_keys.each do |key|
119
+ next unless promise_for(key).pending?
120
+
121
+ reject(key, err)
122
+ end
123
+ end
124
+
125
+ def check_for_broken_promises(load_keys)
126
+ load_keys.each do |key|
127
+ next unless promise_for(key).pending?
128
+
129
+ reject(key, ::Promise::BrokenError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,12 @@
1
+ module GraphQL::Batch
2
+ class MutationFieldExtension < GraphQL::Schema::FieldExtension
3
+ def resolve(object:, arguments:, **_rest)
4
+ GraphQL::Batch::Executor.current.clear
5
+ begin
6
+ ::Promise.sync(yield(object, arguments))
7
+ ensure
8
+ GraphQL::Batch::Executor.current.clear
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ module GraphQL::Batch
2
+ class Setup
3
+ class << self
4
+ def start_batching(executor_class)
5
+ GraphQL::Batch::Executor.start_batch(executor_class)
6
+ end
7
+
8
+ def end_batching
9
+ GraphQL::Batch::Executor.end_batch
10
+ end
11
+
12
+ def instrument_field(schema, type, field)
13
+ return field unless type == schema.mutation
14
+ old_resolve_proc = field.resolve_proc
15
+ field.redefine do
16
+ resolve ->(obj, args, ctx) {
17
+ GraphQL::Batch::Executor.current.clear
18
+ begin
19
+ ::Promise.sync(old_resolve_proc.call(obj, args, ctx))
20
+ ensure
21
+ GraphQL::Batch::Executor.current.clear
22
+ end
23
+ }
24
+ end
25
+ end
26
+ end
27
+
28
+ def initialize(schema, executor_class:)
29
+ @schema = schema
30
+ @executor_class = executor_class
31
+ end
32
+
33
+ def before_query(query)
34
+ Setup.start_batching(@executor_class)
35
+ end
36
+
37
+ def after_query(query)
38
+ Setup.end_batching
39
+ end
40
+
41
+ def instrument(type, field)
42
+ Setup.instrument_field(@schema, type, field)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,20 @@
1
+ module GraphQL::Batch
2
+ class SetupMultiplex
3
+ def initialize(schema, executor_class:)
4
+ @schema = schema
5
+ @executor_class = executor_class
6
+ end
7
+
8
+ def before_multiplex(multiplex)
9
+ Setup.start_batching(@executor_class)
10
+ end
11
+
12
+ def after_multiplex(multiplex)
13
+ Setup.end_batching
14
+ end
15
+
16
+ def instrument(type, field)
17
+ Setup.instrument_field(@schema, type, field)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module GraphQL
2
+ module Batch
3
+ VERSION = "0.4.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql-batch-edge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Dylan Thacker-Smith
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: graphql
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
33
+ - !ruby/object:Gem::Dependency
34
+ name: promise.rb
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.7.2
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.7.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: byebug
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '10.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '10.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: minitest
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description:
90
+ email:
91
+ - gems@shopify.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - ".gitignore"
97
+ - ".travis.yml"
98
+ - CONTRIBUTING.md
99
+ - Gemfile
100
+ - LICENSE.txt
101
+ - README.md
102
+ - Rakefile
103
+ - bin/console
104
+ - bin/setup
105
+ - examples/association_loader.rb
106
+ - examples/record_loader.rb
107
+ - graphql-batch.gemspec
108
+ - lib/graphql/batch.rb
109
+ - lib/graphql/batch/executor.rb
110
+ - lib/graphql/batch/loader.rb
111
+ - lib/graphql/batch/mutation_field_extension.rb
112
+ - lib/graphql/batch/setup.rb
113
+ - lib/graphql/batch/setup_multiplex.rb
114
+ - lib/graphql/batch/version.rb
115
+ homepage: https://github.com/Shopify/graphql-batch
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.4.6
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: A query batching executor for the graphql gem (Unofficial Edge Releases)
139
+ test_files: []