graphql-sources 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c5e22be9513129488382bdbbf924e8556528176f0b1f4a8313ab4c1637c8c752
4
+ data.tar.gz: ac16703211688fb933cf42bcb0d0b3cfea158027aee5341c2332daf6849ef0ed
5
+ SHA512:
6
+ metadata.gz: 11eed0b5dc6e7176c92a985ebc5b7a3eb63907aa9a1604541322539f3a1f9f0e0605c87d63ee8d1997eba838ebeb2558158e2a90dfc61c9fd1deb8bb36d25e0d
7
+ data.tar.gz: 2e8bf4989abec1c9cf34a21be060917052e97f03635e4116a4efa9e1a82164d4df9eb5a27583fc81e4ffd651d77f9a0d2fdf79fd6b5a66d3841a25851140f420
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ gem 'debug'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Kevin Sylvestre
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.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # GraphQL::Sources
2
+
3
+ ## Installation
4
+
5
+ Install the gem and add to the application's Gemfile by executing:
6
+
7
+ $ bundle add graphql-sources
8
+
9
+ If bundler is not being used to manage dependencies, install the gem by executing:
10
+
11
+ $ gem install graphql-sources
12
+
13
+ ## Usage
14
+
15
+ ### Loading `has_one` Associations
16
+
17
+ ```ruby
18
+ class Profile < ActiveRecord::Base
19
+ belongs_to :user
20
+ end
21
+ ```
22
+
23
+ ```ruby
24
+ class User < ActiveRecord::Base
25
+ has_one :profile
26
+ end
27
+ ```
28
+
29
+ ```ruby
30
+ class UserType < GraphQL::Schema::Object
31
+ field :profile, [ProfileType], null: false
32
+
33
+ def profile
34
+ dataloader
35
+ .with(GraphQL::Sources::ActiveRecordObject, ::Profile, key: :user_id)
36
+ .load(object.id)
37
+ end
38
+ end
39
+ ```
40
+
41
+ ```sql
42
+ SELECT "profiles".*
43
+ FROM "profiles"
44
+ WHERE "profiles"."user_id" IN (...)
45
+ ORDER BY "profiles"."id"
46
+ ```
47
+
48
+ ### Loading `has_many` Associations
49
+
50
+ ```ruby
51
+ class User
52
+ has_many :comments
53
+ end
54
+ ```
55
+
56
+ ```ruby
57
+ class Comment
58
+ belongs_to :user
59
+ end
60
+ ```
61
+
62
+ ```ruby
63
+ class UserType < GraphQL::Schema::Object
64
+ field :comments, [CommentType], null: false
65
+
66
+ def profile
67
+ dataloader
68
+ .with(GraphQL::Sources::ActiveRecordCollection, ::Comment, key: :user_id)
69
+ .load(object.id)
70
+ end
71
+ end
72
+ ```
73
+
74
+ ```sql
75
+ SELECT "comments".*
76
+ FROM "comments"
77
+ WHERE "comments"."user_id" IN (...)
78
+ ORDER BY "comments"."id"
79
+ ```
80
+
81
+ ### Loading Counts
82
+
83
+ ```ruby
84
+ class Like
85
+ belongs_to :post
86
+ end
87
+ ```
88
+
89
+ ```ruby
90
+ class Post
91
+ has_many :likes
92
+ end
93
+ ```
94
+
95
+ ```ruby
96
+ class PostType < GraphQL::Schema::Object
97
+ field :likes, Integer, null: false
98
+
99
+ def comments
100
+ dataloader
101
+ .with(GraphQL::Sources::ActiveRecordCount, ::Like, key: :post_id)
102
+ .load(object.id)
103
+ end
104
+ end
105
+ ```
106
+
107
+ ```sql
108
+ SELECT "likes"."post_id", COUNT(*)
109
+ FROM "likes"
110
+ WHERE "likes"."post_id" IN (1, 2, 3, ...)
111
+ GROUP BY "likes"."post_id"
112
+ ```
113
+
114
+ ## License
115
+
116
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Sources
5
+ # An abstract class for interacting with active record.
6
+ class ActiveRecordBase < GraphQL::Dataloader::Source
7
+ # @param model [Class] a child class of ActiveRecord::Base (e.g. Comment)
8
+ # @param key [Symbol] an attribute (typically a foreign key) to use for loading (e.g. :user_id)
9
+ def initialize(model, key:)
10
+ super()
11
+ @model = model
12
+ @key = key
13
+ end
14
+
15
+ protected
16
+
17
+ # @param keys [Array] an array of keys
18
+ # @return [ActiveRecord_Relation] a collection of records
19
+ def models(keys:)
20
+ @model.where(@key => keys)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './active_record_base'
4
+
5
+ module GraphQL
6
+ module Sources
7
+ # A class for loading `has_many` style associations.
8
+ #
9
+ # class User
10
+ # has_many :comments
11
+ # end
12
+ #
13
+ # class Comment
14
+ # belongs_to :user
15
+ # end
16
+ #
17
+ # class UserType < GraphQL::Schema::Object
18
+ # field :comments, [CommentType], null: false
19
+ #
20
+ # def comments
21
+ # dataloader
22
+ # .with(GraphQL::Sources::ActiveRecordCollection, ::Comment, key: :user_id)
23
+ # .load(object.id)
24
+ # end
25
+ # end
26
+ #
27
+ # The resulting SQL query is:
28
+ #
29
+ # SELECT "comments".*
30
+ # FROM "comments"
31
+ # WHERE "comments"."user_id" IN (...)
32
+ # ORDER BY "comments"."id"
33
+ class ActiveRecordCollection < ActiveRecordBase
34
+ def fetch(keys)
35
+ models = models(keys: keys).order(:id).load_async
36
+ dataloader.yield
37
+
38
+ map = models.group_by { |model| model[@key] }
39
+ keys.map { |key| map[key] || [] }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './active_record_base'
4
+
5
+ module GraphQL
6
+ module Sources
7
+ # A class for loading a count of records.
8
+ #
9
+ # class Post
10
+ # has_many :likes
11
+ # end
12
+ #
13
+ # class Like
14
+ # belongs_to :post
15
+ # end
16
+ #
17
+ # class PostType < GraphQL::Schema::Object
18
+ # field :likes, Integer, null: false
19
+ #
20
+ # def likes
21
+ # dataloader
22
+ # .with(GraphQL::Sources::ActiveRecordCount, ::Like, key: :post_id)
23
+ # .load(object.id)
24
+ # end
25
+ # end
26
+ #
27
+ # The resulting SQL query is:
28
+ #
29
+ # SELECT "likes"."post_id", COUNT(*)
30
+ # FROM "likes"
31
+ # WHERE "likes"."post_id" IN (1, 2, 3, ...)
32
+ # GROUP BY "likes"."post_id"
33
+ class ActiveRecordCount < ActiveRecordBase
34
+ def fetch(keys)
35
+ map = models(keys: keys).group(@key).count
36
+ keys.map { |key| map[key] || 0 }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './active_record_base'
4
+
5
+ module GraphQL
6
+ module Sources
7
+ # A class for loading `has_one` style associations.
8
+ #
9
+ # class User
10
+ # has_one :profile
11
+ # end
12
+ #
13
+ # class Profile
14
+ # belongs_to :user
15
+ # end
16
+ #
17
+ # class UserType < GraphQL::Schema::Object
18
+ # field :profile, [ProfileType], null: false
19
+ #
20
+ # def profile
21
+ # dataloader
22
+ # .with(GraphQL::Sources::ActiveRecordCollection, ::Profile, key: :user_id)
23
+ # .load(object.id)
24
+ # end
25
+ # end
26
+ #
27
+ # The resulting SQL query is:
28
+ #
29
+ # SELECT "profiles".*
30
+ # FROM "profiles"
31
+ # WHERE "profiles"."user_id" IN (1, 2, 3, ...)
32
+ # ORDER BY "profiles"."id"
33
+ class ActiveRecordObject < ActiveRecordBase
34
+ def fetch(keys)
35
+ models = models(keys: keys).order(:id).load_async
36
+ dataloader.yield
37
+
38
+ map = models.index_by { |model| model[@key] }
39
+ keys.map { |key| map[key] }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Sources
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql'
4
+
5
+ require_relative './sources/active_record_count'
6
+ require_relative './sources/active_record_collection'
7
+ require_relative './sources/active_record_object'
8
+
9
+ module GraphQL
10
+ # A collection of common GraphQL dataloader classes.
11
+ module Sources
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql-sources
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Sylvestre
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-07-05 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: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: factory_bot
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pg
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec_junit_formatter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: Common loaders for various database or cache operations.
154
+ email:
155
+ - kevin@ksylvest.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - Gemfile
161
+ - LICENSE
162
+ - README.md
163
+ - lib/graphql/sources.rb
164
+ - lib/graphql/sources/active_record_base.rb
165
+ - lib/graphql/sources/active_record_collection.rb
166
+ - lib/graphql/sources/active_record_count.rb
167
+ - lib/graphql/sources/active_record_object.rb
168
+ - lib/graphql/sources/version.rb
169
+ homepage: https://github.com/ksylvest/graphql-sources
170
+ licenses:
171
+ - MIT
172
+ metadata:
173
+ homepage_uri: https://github.com/ksylvest/graphql-sources
174
+ source_code_uri: https://github.com/ksylvest/graphql-sources
175
+ changelog_uri: https://github.com/ksylvest/graphql-sources/blob/main/CHANGELOG.md
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: 2.6.0
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ requirements: []
191
+ rubygems_version: 3.3.7
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: A set of common GraphQL DataLoader sources.
195
+ test_files: []