graphql-batch 0.4.0 → 0.5.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/.github/workflows/ci.yml +23 -0
- data/.rubocop.yml +11 -0
- data/.rubocop_todo.yml +199 -0
- data/Gemfile +4 -2
- data/README.md +13 -30
- data/Rakefile +10 -1
- data/examples/active_storage_loader.rb +75 -0
- data/examples/association_loader.rb +2 -1
- data/examples/http_loader.rb +66 -0
- data/examples/record_loader.rb +1 -0
- data/examples/window_key_loader.rb +81 -0
- data/graphql-batch.gemspec +5 -6
- data/lib/graphql/batch/loader.rb +45 -15
- data/lib/graphql/batch/setup_multiplex.rb +2 -6
- data/lib/graphql/batch/version.rb +1 -1
- data/lib/graphql/batch.rb +8 -18
- metadata +17 -13
- data/.travis.yml +0 -7
- data/lib/graphql/batch/setup.rb +0 -45
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b257f38787895a689caec957a242bef5fa56e02030b2ac499078a510820b8669
         | 
| 4 | 
            +
              data.tar.gz: 9232d5995209ff370617a4f68f31cf936fd334ccd9eaad1cc449336d353a6535
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d8106c65d1842f98cbe1698e881e54c569d8452b684a6caea28b62a75cfde21a4b28fdbb6a61acb7a884dc6826a6209d05ddcdc6b3b7843ee6188202dfdfb638
         | 
| 7 | 
            +
              data.tar.gz: 370418c233cc8fbf99734ba76eefd1c3d703a0bc40b249ed0dc8e24890a968fe45cf22ff8dc9d1a88c73dee5aa00971c4e3b0a5104048f9b5e3390bfced7a421
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            name: Tests
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            on:
         | 
| 4 | 
            +
              - push
         | 
| 5 | 
            +
              - pull_request
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            jobs:
         | 
| 8 | 
            +
              test:
         | 
| 9 | 
            +
                runs-on: ubuntu-latest
         | 
| 10 | 
            +
                strategy:
         | 
| 11 | 
            +
                  fail-fast: false
         | 
| 12 | 
            +
                  matrix:
         | 
| 13 | 
            +
                    ruby: [2.4, 2.7, '3.0']
         | 
| 14 | 
            +
                    graphql_version: ['~> 1.10.0', '~> 1.13']
         | 
| 15 | 
            +
                steps:
         | 
| 16 | 
            +
                - uses: actions/checkout@v2
         | 
| 17 | 
            +
                - uses: ruby/setup-ruby@v1
         | 
| 18 | 
            +
                  with:
         | 
| 19 | 
            +
                    bundler-cache: true
         | 
| 20 | 
            +
                    ruby-version: ${{ matrix.ruby }}
         | 
| 21 | 
            +
                  env:
         | 
| 22 | 
            +
                    GRAPHQL_VERSION: ${{ matrix.graphql_version }}
         | 
| 23 | 
            +
                - run: bundle exec rake
         | 
    
        data/.rubocop.yml
    ADDED
    
    
    
        data/.rubocop_todo.yml
    ADDED
    
    | @@ -0,0 +1,199 @@ | |
| 1 | 
            +
            # This configuration was generated by
         | 
| 2 | 
            +
            # `rubocop --auto-gen-config`
         | 
| 3 | 
            +
            # on 2020-02-06 13:18:09 -0500 using RuboCop version 0.78.0.
         | 
| 4 | 
            +
            # The point is for the user to remove these configuration records
         | 
| 5 | 
            +
            # one by one as the offenses are removed from the code base.
         | 
| 6 | 
            +
            # Note that changes in the inspected code, or installation of new
         | 
| 7 | 
            +
            # versions of RuboCop, may require this file to be generated again.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            # Offense count: 1
         | 
| 10 | 
            +
            # Cop supports --auto-correct.
         | 
| 11 | 
            +
            # Configuration parameters: EnforcedStyle, IndentationWidth.
         | 
| 12 | 
            +
            # SupportedStyles: outdent, indent
         | 
| 13 | 
            +
            Layout/AccessModifierIndentation:
         | 
| 14 | 
            +
              Exclude:
         | 
| 15 | 
            +
                - 'examples/http_loader.rb'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            # Offense count: 5
         | 
| 18 | 
            +
            # Cop supports --auto-correct.
         | 
| 19 | 
            +
            # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
         | 
| 20 | 
            +
            # URISchemes: http, https
         | 
| 21 | 
            +
            Layout/LineLength:
         | 
| 22 | 
            +
              Max: 182
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            # Offense count: 4
         | 
| 25 | 
            +
            # Cop supports --auto-correct.
         | 
| 26 | 
            +
            # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator.
         | 
| 27 | 
            +
            # SupportedStylesForExponentOperator: space, no_space
         | 
| 28 | 
            +
            Layout/SpaceAroundOperators:
         | 
| 29 | 
            +
              Exclude:
         | 
| 30 | 
            +
                - 'test/graphql_test.rb'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            # Offense count: 6
         | 
| 33 | 
            +
            # Cop supports --auto-correct.
         | 
| 34 | 
            +
            # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
         | 
| 35 | 
            +
            # SupportedStyles: space, no_space
         | 
| 36 | 
            +
            # SupportedStylesForEmptyBraces: space, no_space
         | 
| 37 | 
            +
            Layout/SpaceBeforeBlockBraces:
         | 
| 38 | 
            +
              Exclude:
         | 
| 39 | 
            +
                - 'test/support/db.rb'
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            # Offense count: 5
         | 
| 42 | 
            +
            # Cop supports --auto-correct.
         | 
| 43 | 
            +
            # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
         | 
| 44 | 
            +
            # SupportedStyles: space, no_space, compact
         | 
| 45 | 
            +
            # SupportedStylesForEmptyBraces: space, no_space
         | 
| 46 | 
            +
            Layout/SpaceInsideHashLiteralBraces:
         | 
| 47 | 
            +
              Exclude:
         | 
| 48 | 
            +
                - 'test/graphql_test.rb'
         | 
| 49 | 
            +
                - 'test/multiplex_test.rb'
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            # Offense count: 1
         | 
| 52 | 
            +
            # Cop supports --auto-correct.
         | 
| 53 | 
            +
            # Configuration parameters: EnforcedStyle.
         | 
| 54 | 
            +
            # SupportedStyles: space, no_space
         | 
| 55 | 
            +
            Layout/SpaceInsideParens:
         | 
| 56 | 
            +
              Exclude:
         | 
| 57 | 
            +
                - 'test/loader_test.rb'
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            # Offense count: 3
         | 
| 60 | 
            +
            Lint/MissingSuper:
         | 
| 61 | 
            +
              Exclude:
         | 
| 62 | 
            +
                - 'test/executor_test.rb'
         | 
| 63 | 
            +
                - 'test/loader_test.rb'
         | 
| 64 | 
            +
                - 'test/support/loaders.rb'
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            # Offense count: 5
         | 
| 67 | 
            +
            # Cop supports --auto-correct.
         | 
| 68 | 
            +
            # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
         | 
| 69 | 
            +
            Lint/UnusedBlockArgument:
         | 
| 70 | 
            +
              Exclude:
         | 
| 71 | 
            +
                - 'lib/graphql/batch.rb'
         | 
| 72 | 
            +
                - 'test/support/db.rb'
         | 
| 73 | 
            +
                - 'test/support/loaders.rb'
         | 
| 74 | 
            +
                - 'test/support/schema.rb'
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            # Offense count: 6
         | 
| 77 | 
            +
            # Cop supports --auto-correct.
         | 
| 78 | 
            +
            # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
         | 
| 79 | 
            +
            Lint/UnusedMethodArgument:
         | 
| 80 | 
            +
              Exclude:
         | 
| 81 | 
            +
                - 'lib/graphql/batch/loader.rb'
         | 
| 82 | 
            +
                - 'lib/graphql/batch/setup.rb'
         | 
| 83 | 
            +
                - 'lib/graphql/batch/setup_multiplex.rb'
         | 
| 84 | 
            +
                - 'test/executor_test.rb'
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            # Offense count: 2
         | 
| 87 | 
            +
            Lint/UselessAssignment:
         | 
| 88 | 
            +
              Exclude:
         | 
| 89 | 
            +
                - 'test/loader_test.rb'
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            # Offense count: 1
         | 
| 92 | 
            +
            # Configuration parameters: CountBlocks.
         | 
| 93 | 
            +
            Metrics/BlockNesting:
         | 
| 94 | 
            +
              Max: 4
         | 
| 95 | 
            +
             | 
| 96 | 
            +
            # Offense count: 1
         | 
| 97 | 
            +
            # Cop supports --auto-correct.
         | 
| 98 | 
            +
            # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners.
         | 
| 99 | 
            +
            # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
         | 
| 100 | 
            +
            # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
         | 
| 101 | 
            +
            # FunctionalMethods: let, let!, subject, watch
         | 
| 102 | 
            +
            # IgnoredMethods: lambda, proc, it
         | 
| 103 | 
            +
            Style/BlockDelimiters:
         | 
| 104 | 
            +
              Exclude:
         | 
| 105 | 
            +
                - 'test/support/db.rb'
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            # Offense count: 11
         | 
| 108 | 
            +
            # Cop supports --auto-correct.
         | 
| 109 | 
            +
            # Configuration parameters: AutoCorrect, EnforcedStyle.
         | 
| 110 | 
            +
            # SupportedStyles: nested, compact
         | 
| 111 | 
            +
            Style/ClassAndModuleChildren:
         | 
| 112 | 
            +
              Exclude:
         | 
| 113 | 
            +
                - 'lib/graphql/batch/executor.rb'
         | 
| 114 | 
            +
                - 'lib/graphql/batch/loader.rb'
         | 
| 115 | 
            +
                - 'lib/graphql/batch/mutation_field_extension.rb'
         | 
| 116 | 
            +
                - 'lib/graphql/batch/setup.rb'
         | 
| 117 | 
            +
                - 'lib/graphql/batch/setup_multiplex.rb'
         | 
| 118 | 
            +
                - 'test/batch_test.rb'
         | 
| 119 | 
            +
                - 'test/custom_executor_test.rb'
         | 
| 120 | 
            +
                - 'test/executor_test.rb'
         | 
| 121 | 
            +
                - 'test/graphql_test.rb'
         | 
| 122 | 
            +
                - 'test/loader_test.rb'
         | 
| 123 | 
            +
                - 'test/multiplex_test.rb'
         | 
| 124 | 
            +
             | 
| 125 | 
            +
            # Offense count: 1
         | 
| 126 | 
            +
            # Cop supports --auto-correct.
         | 
| 127 | 
            +
            # Configuration parameters: EnforcedStyle, AllowInnerBackticks.
         | 
| 128 | 
            +
            # SupportedStyles: backticks, percent_x, mixed
         | 
| 129 | 
            +
            Style/CommandLiteral:
         | 
| 130 | 
            +
              Exclude:
         | 
| 131 | 
            +
                - 'graphql-batch.gemspec'
         | 
| 132 | 
            +
             | 
| 133 | 
            +
            # Offense count: 2
         | 
| 134 | 
            +
            # Cop supports --auto-correct.
         | 
| 135 | 
            +
            Style/EmptyLiteral:
         | 
| 136 | 
            +
              Exclude:
         | 
| 137 | 
            +
                - 'test/support/schema.rb'
         | 
| 138 | 
            +
             | 
| 139 | 
            +
            # Offense count: 24
         | 
| 140 | 
            +
            # Cop supports --auto-correct.
         | 
| 141 | 
            +
            # Configuration parameters: EnforcedStyle.
         | 
| 142 | 
            +
            # SupportedStyles: always, never
         | 
| 143 | 
            +
            Style/FrozenStringLiteralComment:
         | 
| 144 | 
            +
              Enabled: false
         | 
| 145 | 
            +
             | 
| 146 | 
            +
            # Offense count: 72
         | 
| 147 | 
            +
            # Cop supports --auto-correct.
         | 
| 148 | 
            +
            # Configuration parameters: IgnoreMacros, IgnoredMethods, IgnoredPatterns, IncludedMacros, AllowParenthesesInMultilineCall, AllowParenthesesInChaining, AllowParenthesesInCamelCaseMethod, EnforcedStyle.
         | 
| 149 | 
            +
            # SupportedStyles: require_parentheses, omit_parentheses
         | 
| 150 | 
            +
            Style/MethodCallWithArgsParentheses:
         | 
| 151 | 
            +
              Exclude:
         | 
| 152 | 
            +
                - 'Gemfile'
         | 
| 153 | 
            +
                - 'graphql-batch.gemspec'
         | 
| 154 | 
            +
                - 'test/batch_test.rb'
         | 
| 155 | 
            +
                - 'test/custom_executor_test.rb'
         | 
| 156 | 
            +
                - 'test/executor_test.rb'
         | 
| 157 | 
            +
                - 'test/graphql_test.rb'
         | 
| 158 | 
            +
                - 'test/loader_test.rb'
         | 
| 159 | 
            +
                - 'test/multiplex_test.rb'
         | 
| 160 | 
            +
                - 'test/support/schema.rb'
         | 
| 161 | 
            +
                - 'test/test_helper.rb'
         | 
| 162 | 
            +
             | 
| 163 | 
            +
            # Offense count: 1
         | 
| 164 | 
            +
            # Cop supports --auto-correct.
         | 
| 165 | 
            +
            Style/RedundantBegin:
         | 
| 166 | 
            +
              Exclude:
         | 
| 167 | 
            +
                - 'lib/graphql/batch.rb'
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            # Offense count: 1
         | 
| 170 | 
            +
            # Cop supports --auto-correct.
         | 
| 171 | 
            +
            # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods.
         | 
| 172 | 
            +
            # AllowedMethods: present?, blank?, presence, try, try!
         | 
| 173 | 
            +
            Style/SafeNavigation:
         | 
| 174 | 
            +
              Exclude:
         | 
| 175 | 
            +
                - 'test/support/db.rb'
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            # Offense count: 1
         | 
| 178 | 
            +
            # Cop supports --auto-correct.
         | 
| 179 | 
            +
            # Configuration parameters: AllowAsExpressionSeparator.
         | 
| 180 | 
            +
            Style/Semicolon:
         | 
| 181 | 
            +
              Exclude:
         | 
| 182 | 
            +
                - 'test/support/schema.rb'
         | 
| 183 | 
            +
             | 
| 184 | 
            +
            # Offense count: 3
         | 
| 185 | 
            +
            # Cop supports --auto-correct.
         | 
| 186 | 
            +
            # Configuration parameters: EnforcedStyleForMultiline.
         | 
| 187 | 
            +
            # SupportedStylesForMultiline: comma, consistent_comma, no_comma
         | 
| 188 | 
            +
            Style/TrailingCommaInArrayLiteral:
         | 
| 189 | 
            +
              Exclude:
         | 
| 190 | 
            +
                - 'test/graphql_test.rb'
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            # Offense count: 23
         | 
| 193 | 
            +
            # Cop supports --auto-correct.
         | 
| 194 | 
            +
            # Configuration parameters: EnforcedStyleForMultiline.
         | 
| 195 | 
            +
            # SupportedStylesForMultiline: comma, consistent_comma, no_comma
         | 
| 196 | 
            +
            Style/TrailingCommaInHashLiteral:
         | 
| 197 | 
            +
              Exclude:
         | 
| 198 | 
            +
                - 'test/graphql_test.rb'
         | 
| 199 | 
            +
                - 'test/multiplex_test.rb'
         | 
    
        data/Gemfile
    CHANGED
    
    | @@ -2,6 +2,8 @@ source 'https://rubygems.org' | |
| 2 2 |  | 
| 3 3 | 
             
            gemspec
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 5 | 
            +
            gem 'graphql', ENV['GRAPHQL_VERSION'] if ENV['GRAPHQL_VERSION']
         | 
| 6 | 
            +
            if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
         | 
| 7 | 
            +
              gem 'rubocop', '~> 1.12.0', require: false
         | 
| 8 | 
            +
              gem "rubocop-shopify", '~> 1.0.7', require: false
         | 
| 7 9 | 
             
            end
         | 
    
        data/README.md
    CHANGED
    
    | @@ -48,7 +48,9 @@ class RecordLoader < GraphQL::Batch::Loader | |
| 48 48 | 
             
            end
         | 
| 49 49 | 
             
            ```
         | 
| 50 50 |  | 
| 51 | 
            -
            Use `GraphQL::Batch` as a plugin in your schema  | 
| 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.
         | 
| 52 54 |  | 
| 53 55 | 
             
            ```ruby
         | 
| 54 56 | 
             
            class MySchema < GraphQL::Schema
         | 
| @@ -59,25 +61,6 @@ class MySchema < GraphQL::Schema | |
| 59 61 | 
             
            end
         | 
| 60 62 | 
             
            ```
         | 
| 61 63 |  | 
| 62 | 
            -
            For pre `1.5.0` versions:
         | 
| 63 | 
            -
             | 
| 64 | 
            -
            ```ruby
         | 
| 65 | 
            -
            MySchema = GraphQL::Schema.define do
         | 
| 66 | 
            -
              query MyQueryType
         | 
| 67 | 
            -
             | 
| 68 | 
            -
              GraphQL::Batch.use(self)
         | 
| 69 | 
            -
            end
         | 
| 70 | 
            -
            ```
         | 
| 71 | 
            -
             | 
| 72 | 
            -
            ##### With `1.9.0`'s `Interpreter` runtime
         | 
| 73 | 
            -
             | 
| 74 | 
            -
            Add `GraphQL::Batch` _after_ the interpreter, so that `GraphQL::Batch` can detect the interpreter and attach the right integrations:
         | 
| 75 | 
            -
             | 
| 76 | 
            -
            ```ruby
         | 
| 77 | 
            -
            use GraphQL::Execution::Interpreter
         | 
| 78 | 
            -
            use GraphQL::Batch
         | 
| 79 | 
            -
            ```
         | 
| 80 | 
            -
             | 
| 81 64 | 
             
            #### Field Usage
         | 
| 82 65 |  | 
| 83 66 | 
             
            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.
         | 
| @@ -99,7 +82,7 @@ field :products, [Types::Product, null: true], null: false do | |
| 99 82 | 
             
              argument :ids, [ID], required: true
         | 
| 100 83 | 
             
            end
         | 
| 101 84 |  | 
| 102 | 
            -
            def  | 
| 85 | 
            +
            def products(ids:)
         | 
| 103 86 | 
             
              RecordLoader.for(Product).load_many(ids)
         | 
| 104 87 | 
             
            end
         | 
| 105 88 | 
             
            ```
         | 
| @@ -150,11 +133,11 @@ end | |
| 150 133 | 
             
            ```ruby
         | 
| 151 134 | 
             
            def product(id:)
         | 
| 152 135 | 
             
              # Try the cache first ...
         | 
| 153 | 
            -
              CacheLoader.for(Product).load( | 
| 136 | 
            +
              CacheLoader.for(Product).load(id).then(nil, lambda do |exc|
         | 
| 154 137 | 
             
                # But if there's a connection error, go to the underlying database
         | 
| 155 138 | 
             
                raise exc unless exc.is_a?(Redis::BaseConnectionError)
         | 
| 156 139 | 
             
                logger.warn err.message
         | 
| 157 | 
            -
                RecordLoader.for(Product).load( | 
| 140 | 
            +
                RecordLoader.for(Product).load(id)
         | 
| 158 141 | 
             
              end)
         | 
| 159 142 | 
             
            end
         | 
| 160 143 | 
             
            ```
         | 
| @@ -162,19 +145,19 @@ end | |
| 162 145 | 
             
            ## Unit Testing
         | 
| 163 146 |  | 
| 164 147 | 
             
            Your loaders can be tested outside of a GraphQL query by doing the
         | 
| 165 | 
            -
            batch loads in a block passed to GraphQL::Batch.batch | 
| 148 | 
            +
            batch loads in a block passed to `GraphQL::Batch.batch`. That method
         | 
| 166 149 | 
             
            will set up thread-local state to store the loaders, batch load any
         | 
| 167 150 | 
             
            promise returned from the block then clear the thread-local state
         | 
| 168 151 | 
             
            to avoid leaking state between tests.
         | 
| 169 152 |  | 
| 170 153 | 
             
            ```ruby
         | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
                end
         | 
| 176 | 
            -
                assert_equal product.title, title
         | 
| 154 | 
            +
            def test_single_query
         | 
| 155 | 
            +
              product = products(:snowboard)
         | 
| 156 | 
            +
              title = GraphQL::Batch.batch do
         | 
| 157 | 
            +
                RecordLoader.for(Product).load(product.id).then(&:title)
         | 
| 177 158 | 
             
              end
         | 
| 159 | 
            +
              assert_equal product.title, title
         | 
| 160 | 
            +
            end
         | 
| 178 161 | 
             
            ```
         | 
| 179 162 |  | 
| 180 163 | 
             
            ## Development
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -7,4 +7,13 @@ Rake::TestTask.new(:test) do |t| | |
| 7 7 | 
             
              t.test_files = FileList['test/**/*_test.rb']
         | 
| 8 8 | 
             
            end
         | 
| 9 9 |  | 
| 10 | 
            -
            task | 
| 10 | 
            +
            task(default: :test)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
         | 
| 13 | 
            +
              task :rubocop do
         | 
| 14 | 
            +
                require 'rubocop/rake_task'
         | 
| 15 | 
            +
                RuboCop::RakeTask.new
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              task(default: :rubocop)
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,75 @@ | |
| 1 | 
            +
            ####
         | 
| 2 | 
            +
            # This is a loader for has_one_attached and has_many_attached Active Storage attachments
         | 
| 3 | 
            +
            # To load a variant for an attachment, 2 queries are required
         | 
| 4 | 
            +
            # Using preloading via the includes method.
         | 
| 5 | 
            +
            ####
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ####
         | 
| 8 | 
            +
            # The model with an attached image and many attached pictures
         | 
| 9 | 
            +
            ####
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # class Event < ApplicationRecord
         | 
| 12 | 
            +
            #   has_one_attached :image
         | 
| 13 | 
            +
            #   has_many_attached :pictures
         | 
| 14 | 
            +
            # end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ####
         | 
| 17 | 
            +
            # An example data type using the AttachmentLoader
         | 
| 18 | 
            +
            ####
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            # class Types::EventType < Types::BaseObject
         | 
| 21 | 
            +
            #   graphql_name 'Event'
         | 
| 22 | 
            +
            #
         | 
| 23 | 
            +
            #   field :id, ID, null: false
         | 
| 24 | 
            +
            #   field :image, String, null: true
         | 
| 25 | 
            +
            #   field :pictures, String, null: true
         | 
| 26 | 
            +
            #
         | 
| 27 | 
            +
            #   def image
         | 
| 28 | 
            +
            #     AttachmentLoader.for(:Event, :image).load(object.id).then do |image|
         | 
| 29 | 
            +
            #       Rails.application.routes.url_helpers.url_for(
         | 
| 30 | 
            +
            #         image.variant({ quality: 75 })
         | 
| 31 | 
            +
            #       )
         | 
| 32 | 
            +
            #     end
         | 
| 33 | 
            +
            #   end
         | 
| 34 | 
            +
            #
         | 
| 35 | 
            +
            #   def pictures
         | 
| 36 | 
            +
            #     AttachmentLoader.for(:Event, :pictures, association_type: :has_many_attached).load(object.id).then do |pictures|
         | 
| 37 | 
            +
            #       pictures.map do |picture|
         | 
| 38 | 
            +
            #         Rails.application.routes.url_helpers.url_for(
         | 
| 39 | 
            +
            #           picture.variant({ quality: 75 })
         | 
| 40 | 
            +
            #         )
         | 
| 41 | 
            +
            #       end
         | 
| 42 | 
            +
            #     end
         | 
| 43 | 
            +
            #   end
         | 
| 44 | 
            +
            # end
         | 
| 45 | 
            +
            module Loaders
         | 
| 46 | 
            +
              class ActiveStorageLoader < GraphQL::Batch::Loader
         | 
| 47 | 
            +
                attr_reader :record_type, :attachment_name, :association_type # should be has_one_attached or has_many_attached
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def initialize(record_type, attachment_name, association_type: :has_one_attached)
         | 
| 50 | 
            +
                  super()
         | 
| 51 | 
            +
                  @record_type = record_type
         | 
| 52 | 
            +
                  @attachment_name = attachment_name
         | 
| 53 | 
            +
                  @association_type = association_type
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def perform(record_ids)
         | 
| 57 | 
            +
                  # find records and fulfill promises
         | 
| 58 | 
            +
                  attachments = ActiveStorage::Attachment.includes(:blob).where(
         | 
| 59 | 
            +
                    record_type: record_type, record_id: record_ids, name: attachment_name
         | 
| 60 | 
            +
                  )
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  if @association_type == :has_one_attached
         | 
| 63 | 
            +
                    attachments.each do |attachment|
         | 
| 64 | 
            +
                      fulfill(attachment.record_id, attachment)
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    record_ids.each { |id| fulfill(id, nil) unless fulfilled?(id) }
         | 
| 68 | 
            +
                  else
         | 
| 69 | 
            +
                    record_ids.each do |record_id|
         | 
| 70 | 
            +
                      fulfill(record_id, attachments.select { |attachment| attachment.record_id == record_id })
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
            end
         | 
| @@ -5,6 +5,7 @@ class AssociationLoader < GraphQL::Batch::Loader | |
| 5 5 | 
             
              end
         | 
| 6 6 |  | 
| 7 7 | 
             
              def initialize(model, association_name)
         | 
| 8 | 
            +
                super()
         | 
| 8 9 | 
             
                @model = model
         | 
| 9 10 | 
             
                @association_name = association_name
         | 
| 10 11 | 
             
                validate
         | 
| @@ -35,7 +36,7 @@ class AssociationLoader < GraphQL::Batch::Loader | |
| 35 36 | 
             
              end
         | 
| 36 37 |  | 
| 37 38 | 
             
              def preload_association(records)
         | 
| 38 | 
            -
                ::ActiveRecord::Associations::Preloader.new | 
| 39 | 
            +
                ::ActiveRecord::Associations::Preloader.new(records: records, associations: @association_name).call
         | 
| 39 40 | 
             
              end
         | 
| 40 41 |  | 
| 41 42 | 
             
              def read_association(record)
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            # A sample HTTP loader using:
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # 1. https://github.com/httprb/http
         | 
| 4 | 
            +
            # 2. https://github.com/mperham/connection_pool
         | 
| 5 | 
            +
            # 3. https://github.com/ruby-concurrency/concurrent-ruby
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # Setup:
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #   field :weather, String, null: true do
         | 
| 10 | 
            +
            #     argument :lat, Float, required: true
         | 
| 11 | 
            +
            #     argument :lng, Float, required: true
         | 
| 12 | 
            +
            #     argument :lang, String, required: true
         | 
| 13 | 
            +
            #   end
         | 
| 14 | 
            +
            #
         | 
| 15 | 
            +
            #   def weather(lat:, lng:, lang:)
         | 
| 16 | 
            +
            #     key = Rails.application.credentials.darksky_key
         | 
| 17 | 
            +
            #     path = "/forecast/#{key}/#{lat},#{lng}?lang=#{lang}"
         | 
| 18 | 
            +
            #     Loaders::HTTPLoader
         | 
| 19 | 
            +
            #       .for(host: 'https://api.darksky.net')
         | 
| 20 | 
            +
            #       .load(->(connection) { connection.get(path).flush })
         | 
| 21 | 
            +
            #       .then do |response|
         | 
| 22 | 
            +
            #         if response.status.ok?
         | 
| 23 | 
            +
            #           json = JSON.parse(response.body)
         | 
| 24 | 
            +
            #           json['currently']['summary']
         | 
| 25 | 
            +
            #         end
         | 
| 26 | 
            +
            #       end
         | 
| 27 | 
            +
            #   end
         | 
| 28 | 
            +
            #
         | 
| 29 | 
            +
            # Querying:
         | 
| 30 | 
            +
            #
         | 
| 31 | 
            +
            #   <<~GQL
         | 
| 32 | 
            +
            #     query Weather {
         | 
| 33 | 
            +
            #       montreal: weather(lat: 45.5017, lng: -73.5673, lang: "fr")
         | 
| 34 | 
            +
            #       waterloo: weather(lat: 43.4643, lng: -80.5204, lang: "en")
         | 
| 35 | 
            +
            #     }
         | 
| 36 | 
            +
            #   GQL
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            module Loaders
         | 
| 39 | 
            +
              class HTTPLoader < GraphQL::Batch::Loader
         | 
| 40 | 
            +
                def initialize(host:, size: 4, timeout: 4)
         | 
| 41 | 
            +
                  super()
         | 
| 42 | 
            +
                  @host = host
         | 
| 43 | 
            +
                  @size = size
         | 
| 44 | 
            +
                  @timeout = timeout
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def perform(operations)
         | 
| 48 | 
            +
                  futures = operations.map do |operation|
         | 
| 49 | 
            +
                    Concurrent::Promises.future do
         | 
| 50 | 
            +
                      pool.with { |connection| operation.call(connection) }
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                  operations.each_with_index.each do |operation, index|
         | 
| 54 | 
            +
                    fulfill(operation, futures[index].value)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              private
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def pool
         | 
| 61 | 
            +
                  @pool ||= ConnectionPool.new(size: @size, timeout: @timeout) do
         | 
| 62 | 
            +
                    HTTP.persistent(@host)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
    
        data/examples/record_loader.rb
    CHANGED
    
    
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            ####
         | 
| 2 | 
            +
            # This is a has_many loader which takes advantage of Postgres'
         | 
| 3 | 
            +
            # windowing functionality to load the first N records for
         | 
| 4 | 
            +
            # a given relationship.
         | 
| 5 | 
            +
            ####
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ####
         | 
| 8 | 
            +
            # An example data type using the WindowKeyLoader
         | 
| 9 | 
            +
            ####
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # class Types::CategoryType < Types::BaseObject
         | 
| 12 | 
            +
            #   graphql_name 'Category'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            #   field :id, ID, null: false
         | 
| 15 | 
            +
            #   field :events, [Types::EventType], null: false do
         | 
| 16 | 
            +
            #     argument :first, Int, required: false, default_value: 5
         | 
| 17 | 
            +
            #   end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            #   def events(first:)
         | 
| 20 | 
            +
            #     WindowKeyLoader.for(
         | 
| 21 | 
            +
            #       Event,
         | 
| 22 | 
            +
            #       :category_id,
         | 
| 23 | 
            +
            #       limit: first, order_col: :start_time, order_dir: :desc
         | 
| 24 | 
            +
            #     ).load(object.id)
         | 
| 25 | 
            +
            #   end
         | 
| 26 | 
            +
            # end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ####
         | 
| 29 | 
            +
            # The SQL that is produced
         | 
| 30 | 
            +
            ####
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            # SELECT
         | 
| 33 | 
            +
            #   "events".*
         | 
| 34 | 
            +
            # FROM (
         | 
| 35 | 
            +
            #   SELECT
         | 
| 36 | 
            +
            #     "events".*,
         | 
| 37 | 
            +
            #     row_number() OVER (PARTITION BY category_id ORDER BY start_time DESC) AS rank
         | 
| 38 | 
            +
            #   FROM
         | 
| 39 | 
            +
            #     "events"
         | 
| 40 | 
            +
            #   WHERE
         | 
| 41 | 
            +
            #     "events"."category_id" IN(1, 2, 3, 4, 5)) AS events
         | 
| 42 | 
            +
            # WHERE (rank <= 5)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            class WindowKeyLoader < GraphQL::Batch::Loader
         | 
| 45 | 
            +
              attr_reader :model, :foreign_key, :limit, :order_col, :order_dir
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              def initialize(model, foreign_key, limit:, order_col:, order_dir: :asc)
         | 
| 48 | 
            +
                super()
         | 
| 49 | 
            +
                @model = model
         | 
| 50 | 
            +
                @foreign_key = foreign_key
         | 
| 51 | 
            +
                @limit = limit
         | 
| 52 | 
            +
                @order_col = order_col
         | 
| 53 | 
            +
                @order_dir = order_dir
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              def perform(foreign_ids)
         | 
| 57 | 
            +
                # build the sub-query, limiting results by foreign key at this point
         | 
| 58 | 
            +
                # we don't want to execute this query but get its SQL to be used later
         | 
| 59 | 
            +
                ranked_from =
         | 
| 60 | 
            +
                  model.select(
         | 
| 61 | 
            +
                    "*",
         | 
| 62 | 
            +
                    "row_number() OVER (
         | 
| 63 | 
            +
                      PARTITION BY #{foreign_key} ORDER BY #{order_col} #{order_dir}
         | 
| 64 | 
            +
                    ) as rank"
         | 
| 65 | 
            +
                  ).where(foreign_key => foreign_ids).to_sql
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                # use the sub-query from above to query records which have a rank
         | 
| 68 | 
            +
                # value less than or equal to our limit
         | 
| 69 | 
            +
                records =
         | 
| 70 | 
            +
                  model.from("(#{ranked_from}) as #{model.table_name}").where(
         | 
| 71 | 
            +
                    "rank <= #{limit}"
         | 
| 72 | 
            +
                  ).to_a
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                # match records and fulfill promises
         | 
| 75 | 
            +
                foreign_ids.each do |foreign_id|
         | 
| 76 | 
            +
                  matching_records =
         | 
| 77 | 
            +
                    records.select { |r| foreign_id == r.send(foreign_key) }
         | 
| 78 | 
            +
                  fulfill(foreign_id, matching_records)
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
    
        data/graphql-batch.gemspec
    CHANGED
    
    | @@ -1,7 +1,4 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            lib = File.expand_path('../lib', __FILE__)
         | 
| 3 | 
            -
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 4 | 
            -
            require 'graphql/batch/version'
         | 
| 1 | 
            +
            require_relative 'lib/graphql/batch/version'
         | 
| 5 2 |  | 
| 6 3 | 
             
            Gem::Specification.new do |spec|
         | 
| 7 4 | 
             
              spec.name          = "graphql-batch"
         | 
| @@ -18,10 +15,12 @@ Gem::Specification.new do |spec| | |
| 18 15 | 
             
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 19 16 | 
             
              spec.require_paths = ["lib"]
         | 
| 20 17 |  | 
| 21 | 
            -
              spec. | 
| 18 | 
            +
              spec.metadata['allowed_push_host'] = "https://rubygems.org"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              spec.add_runtime_dependency "graphql", ">= 1.10", "< 2"
         | 
| 22 21 | 
             
              spec.add_runtime_dependency "promise.rb", "~> 0.7.2"
         | 
| 23 22 |  | 
| 24 23 | 
             
              spec.add_development_dependency "byebug" if RUBY_ENGINE == 'ruby'
         | 
| 25 | 
            -
              spec.add_development_dependency "rake", " | 
| 24 | 
            +
              spec.add_development_dependency "rake", ">= 12.3.3"
         | 
| 26 25 | 
             
              spec.add_development_dependency "minitest"
         | 
| 27 26 | 
             
            end
         | 
    
        data/lib/graphql/batch/loader.rb
    CHANGED
    
    | @@ -1,21 +1,20 @@ | |
| 1 1 | 
             
            module GraphQL::Batch
         | 
| 2 2 | 
             
              class Loader
         | 
| 3 | 
            -
                 | 
| 4 | 
            -
             | 
| 5 | 
            -
                   | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
                     | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 3 | 
            +
                # Use new argument forwarding syntax if available as an optimization
         | 
| 4 | 
            +
                if RUBY_ENGINE && Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7")
         | 
| 5 | 
            +
                  class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
         | 
| 6 | 
            +
                    def self.for(...)
         | 
| 7 | 
            +
                      current_executor.loader(loader_key_for(...)) { new(...) }
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
                  RUBY
         | 
| 10 | 
            +
                else
         | 
| 11 | 
            +
                  def self.for(*group_args)
         | 
| 12 | 
            +
                    current_executor.loader(loader_key_for(*group_args)) { new(*group_args) }
         | 
| 12 13 | 
             
                  end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                  executor.loader(loader_key) { new(*group_args) }
         | 
| 15 14 | 
             
                end
         | 
| 16 15 |  | 
| 17 | 
            -
                def self.loader_key_for(*group_args)
         | 
| 18 | 
            -
                  [self] | 
| 16 | 
            +
                def self.loader_key_for(*group_args, **group_kwargs)
         | 
| 17 | 
            +
                  [self, group_kwargs, group_args]
         | 
| 19 18 | 
             
                end
         | 
| 20 19 |  | 
| 21 20 | 
             
                def self.load(key)
         | 
| @@ -26,6 +25,23 @@ module GraphQL::Batch | |
| 26 25 | 
             
                  self.for.load_many(keys)
         | 
| 27 26 | 
             
                end
         | 
| 28 27 |  | 
| 28 | 
            +
                class << self
         | 
| 29 | 
            +
                  private
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def current_executor
         | 
| 32 | 
            +
                    executor = Executor.current
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    unless executor
         | 
| 35 | 
            +
                      raise GraphQL::Batch::NoExecutorError, 'Cannot create loader without'\
         | 
| 36 | 
            +
                        ' an Executor. Wrap the call to `for` with `GraphQL::Batch.batch`'\
         | 
| 37 | 
            +
                        ' or use `GraphQL::Batch::SetupMultiplex` as a query instrumenter if'\
         | 
| 38 | 
            +
                        ' using with `graphql-ruby`'
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    executor
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 29 45 | 
             
                attr_accessor :loader_key, :executor
         | 
| 30 46 |  | 
| 31 47 | 
             
                def load(key)
         | 
| @@ -79,7 +95,15 @@ module GraphQL::Batch | |
| 79 95 |  | 
| 80 96 | 
             
                # Returns true when the key has already been fulfilled, otherwise returns false
         | 
| 81 97 | 
             
                def fulfilled?(key)
         | 
| 82 | 
            -
                  promise_for(key) | 
| 98 | 
            +
                  promise = promise_for(key)
         | 
| 99 | 
            +
                  # When a promise is fulfilled through this class, it will either:
         | 
| 100 | 
            +
                  #   become fulfilled, if fulfilled with a literal value
         | 
| 101 | 
            +
                  #   become pending with a new source if fulfilled with a promise
         | 
| 102 | 
            +
                  # Either of these is acceptable, promise.rb will automatically re-wait
         | 
| 103 | 
            +
                  # on the new source promise as needed.
         | 
| 104 | 
            +
                  return true if promise.fulfilled?
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                  promise.pending? && promise.source != self
         | 
| 83 107 | 
             
                end
         | 
| 84 108 |  | 
| 85 109 | 
             
                # Must override to load the keys and call #fulfill for each key
         | 
| @@ -124,7 +148,13 @@ module GraphQL::Batch | |
| 124 148 |  | 
| 125 149 | 
             
                def check_for_broken_promises(load_keys)
         | 
| 126 150 | 
             
                  load_keys.each do |key|
         | 
| 127 | 
            -
                     | 
| 151 | 
            +
                    promise = promise_for(key)
         | 
| 152 | 
            +
                    # When a promise is fulfilled through this class, it will either:
         | 
| 153 | 
            +
                    #   become not pending, if fulfilled with a literal value
         | 
| 154 | 
            +
                    #   become pending with a new source if fulfilled with a promise
         | 
| 155 | 
            +
                    # Either of these is acceptable, promise.rb will automatically re-wait
         | 
| 156 | 
            +
                    # on the new source promise as needed.
         | 
| 157 | 
            +
                    next unless promise.pending? && promise.source == self
         | 
| 128 158 |  | 
| 129 159 | 
             
                    reject(key, ::Promise::BrokenError.new("#{self.class} didn't fulfill promise for key #{key.inspect}"))
         | 
| 130 160 | 
             
                  end
         | 
| @@ -6,15 +6,11 @@ module GraphQL::Batch | |
| 6 6 | 
             
                end
         | 
| 7 7 |  | 
| 8 8 | 
             
                def before_multiplex(multiplex)
         | 
| 9 | 
            -
                   | 
| 9 | 
            +
                  GraphQL::Batch::Executor.start_batch(@executor_class)
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 12 | 
             
                def after_multiplex(multiplex)
         | 
| 13 | 
            -
                   | 
| 14 | 
            -
                end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                def instrument(type, field)
         | 
| 17 | 
            -
                  Setup.instrument_field(@schema, type, field)
         | 
| 13 | 
            +
                  GraphQL::Batch::Executor.end_batch
         | 
| 18 14 | 
             
                end
         | 
| 19 15 | 
             
              end
         | 
| 20 16 | 
             
            end
         | 
    
        data/lib/graphql/batch.rb
    CHANGED
    
    | @@ -16,26 +16,17 @@ module GraphQL | |
| 16 16 | 
             
                end
         | 
| 17 17 |  | 
| 18 18 | 
             
                def self.use(schema_defn, executor_class: GraphQL::Batch::Executor)
         | 
| 19 | 
            -
                   | 
| 20 | 
            -
                   | 
| 19 | 
            +
                  instrumentation = GraphQL::Batch::SetupMultiplex.new(schema_defn, executor_class: executor_class)
         | 
| 20 | 
            +
                  schema_defn.instrument(:multiplex, instrumentation)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  if schema_defn.mutation
         | 
| 21 23 | 
             
                    require_relative "batch/mutation_field_extension"
         | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                        field.extension(GraphQL::Batch::MutationFieldExtension)
         | 
| 26 | 
            -
                      end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    schema_defn.mutation.fields.each do |name, field|
         | 
| 26 | 
            +
                      field.extension(GraphQL::Batch::MutationFieldExtension)
         | 
| 27 27 | 
             
                    end
         | 
| 28 | 
            -
                    instrumentation = GraphQL::Batch::SetupMultiplex.new(schema, executor_class: executor_class)
         | 
| 29 | 
            -
                    schema_defn.instrument(:multiplex, instrumentation)
         | 
| 30 | 
            -
                  elsif GraphQL::VERSION >= "1.6.0"
         | 
| 31 | 
            -
                    instrumentation = GraphQL::Batch::SetupMultiplex.new(schema, executor_class: executor_class)
         | 
| 32 | 
            -
                    schema_defn.instrument(:multiplex, instrumentation)
         | 
| 33 | 
            -
                    schema_defn.instrument(:field, instrumentation)
         | 
| 34 | 
            -
                  else
         | 
| 35 | 
            -
                    instrumentation = GraphQL::Batch::Setup.new(schema, executor_class: executor_class)
         | 
| 36 | 
            -
                    schema_defn.instrument(:query, instrumentation)
         | 
| 37 | 
            -
                    schema_defn.instrument(:field, instrumentation)
         | 
| 38 28 | 
             
                  end
         | 
| 29 | 
            +
             | 
| 39 30 | 
             
                  schema_defn.lazy_resolve(::Promise, :sync)
         | 
| 40 31 | 
             
                end
         | 
| 41 32 | 
             
              end
         | 
| @@ -44,5 +35,4 @@ end | |
| 44 35 | 
             
            require_relative "batch/version"
         | 
| 45 36 | 
             
            require_relative "batch/loader"
         | 
| 46 37 | 
             
            require_relative "batch/executor"
         | 
| 47 | 
            -
            require_relative "batch/setup"
         | 
| 48 38 | 
             
            require_relative "batch/setup_multiplex"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: graphql-batch
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Dylan Thacker-Smith
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2022-01-18 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: graphql
         | 
| @@ -16,7 +16,7 @@ dependencies: | |
| 16 16 | 
             
                requirements:
         | 
| 17 17 | 
             
                - - ">="
         | 
| 18 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: '1. | 
| 19 | 
            +
                    version: '1.10'
         | 
| 20 20 | 
             
                - - "<"
         | 
| 21 21 | 
             
                  - !ruby/object:Gem::Version
         | 
| 22 22 | 
             
                    version: '2'
         | 
| @@ -26,7 +26,7 @@ dependencies: | |
| 26 26 | 
             
                requirements:
         | 
| 27 27 | 
             
                - - ">="
         | 
| 28 28 | 
             
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            -
                    version: '1. | 
| 29 | 
            +
                    version: '1.10'
         | 
| 30 30 | 
             
                - - "<"
         | 
| 31 31 | 
             
                  - !ruby/object:Gem::Version
         | 
| 32 32 | 
             
                    version: '2'
         | 
| @@ -62,16 +62,16 @@ dependencies: | |
| 62 62 | 
             
              name: rake
         | 
| 63 63 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 64 64 | 
             
                requirements:
         | 
| 65 | 
            -
                - - " | 
| 65 | 
            +
                - - ">="
         | 
| 66 66 | 
             
                  - !ruby/object:Gem::Version
         | 
| 67 | 
            -
                    version:  | 
| 67 | 
            +
                    version: 12.3.3
         | 
| 68 68 | 
             
              type: :development
         | 
| 69 69 | 
             
              prerelease: false
         | 
| 70 70 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 71 71 | 
             
                requirements:
         | 
| 72 | 
            -
                - - " | 
| 72 | 
            +
                - - ">="
         | 
| 73 73 | 
             
                  - !ruby/object:Gem::Version
         | 
| 74 | 
            -
                    version:  | 
| 74 | 
            +
                    version: 12.3.3
         | 
| 75 75 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 76 76 | 
             
              name: minitest
         | 
| 77 77 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -93,8 +93,10 @@ executables: [] | |
| 93 93 | 
             
            extensions: []
         | 
| 94 94 | 
             
            extra_rdoc_files: []
         | 
| 95 95 | 
             
            files:
         | 
| 96 | 
            +
            - ".github/workflows/ci.yml"
         | 
| 96 97 | 
             
            - ".gitignore"
         | 
| 97 | 
            -
            - ". | 
| 98 | 
            +
            - ".rubocop.yml"
         | 
| 99 | 
            +
            - ".rubocop_todo.yml"
         | 
| 98 100 | 
             
            - CONTRIBUTING.md
         | 
| 99 101 | 
             
            - Gemfile
         | 
| 100 102 | 
             
            - LICENSE.txt
         | 
| @@ -102,20 +104,23 @@ files: | |
| 102 104 | 
             
            - Rakefile
         | 
| 103 105 | 
             
            - bin/console
         | 
| 104 106 | 
             
            - bin/setup
         | 
| 107 | 
            +
            - examples/active_storage_loader.rb
         | 
| 105 108 | 
             
            - examples/association_loader.rb
         | 
| 109 | 
            +
            - examples/http_loader.rb
         | 
| 106 110 | 
             
            - examples/record_loader.rb
         | 
| 111 | 
            +
            - examples/window_key_loader.rb
         | 
| 107 112 | 
             
            - graphql-batch.gemspec
         | 
| 108 113 | 
             
            - lib/graphql/batch.rb
         | 
| 109 114 | 
             
            - lib/graphql/batch/executor.rb
         | 
| 110 115 | 
             
            - lib/graphql/batch/loader.rb
         | 
| 111 116 | 
             
            - lib/graphql/batch/mutation_field_extension.rb
         | 
| 112 | 
            -
            - lib/graphql/batch/setup.rb
         | 
| 113 117 | 
             
            - lib/graphql/batch/setup_multiplex.rb
         | 
| 114 118 | 
             
            - lib/graphql/batch/version.rb
         | 
| 115 119 | 
             
            homepage: https://github.com/Shopify/graphql-batch
         | 
| 116 120 | 
             
            licenses:
         | 
| 117 121 | 
             
            - MIT
         | 
| 118 | 
            -
            metadata: | 
| 122 | 
            +
            metadata:
         | 
| 123 | 
            +
              allowed_push_host: https://rubygems.org
         | 
| 119 124 | 
             
            post_install_message: 
         | 
| 120 125 | 
             
            rdoc_options: []
         | 
| 121 126 | 
             
            require_paths:
         | 
| @@ -131,8 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 131 136 | 
             
                - !ruby/object:Gem::Version
         | 
| 132 137 | 
             
                  version: '0'
         | 
| 133 138 | 
             
            requirements: []
         | 
| 134 | 
            -
             | 
| 135 | 
            -
            rubygems_version: 2.7.6
         | 
| 139 | 
            +
            rubygems_version: 3.2.20
         | 
| 136 140 | 
             
            signing_key: 
         | 
| 137 141 | 
             
            specification_version: 4
         | 
| 138 142 | 
             
            summary: A query batching executor for the graphql gem
         | 
    
        data/.travis.yml
    DELETED
    
    
    
        data/lib/graphql/batch/setup.rb
    DELETED
    
    | @@ -1,45 +0,0 @@ | |
| 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
         |