graphql-activerecord 0.10.0.pre.alpha2 → 0.10.0.pre.alpha3
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/CHANGELOG.md +17 -0
- data/README.md +120 -42
- data/lib/graphql/models/definition_helpers/associations.rb +4 -14
- data/lib/graphql/models/hash_combiner.rb +1 -1
- data/lib/graphql/models/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51188ba9e6b78d0a5db797c4cba6501995c01b85
|
4
|
+
data.tar.gz: 1d38ee1f0b1b5ef17e8a38d9ef8e9cb06019631c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3198879eb4d21bcc2f52098a8c0ae70a511e5e7700e2a16185abf65cdedfc5b95dc840e70a9a8393959aa5ed3e6c7f155342be177006d6d2b0cd4e54041aaaf2
|
7
|
+
data.tar.gz: cfe77dd4439bb6068a1d4283bd7d16297258b5637fa40d846b625a2f4e7005fc2a236accc23ab51840a10b2f26aff4c6e5f478ee88147ca8efac060dd73e02b2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
# 0.10.0
|
4
|
+
There are a few breaking changes:
|
5
|
+
- Added automatic nullability checking for attributes. It’s enabled by default; see the README for more info.
|
6
|
+
- The gem now assumes that the object types for your models are called "ModelNameType" instead of "ModelNameGraph",
|
7
|
+
to bring it more in line with common practice. You can get the old behavior by adding this to an initializer:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
GraphQL::Models.model_to_graphql_type -> (model_class) { "#{model_class.name}Graph".safe_constantize }
|
11
|
+
```
|
12
|
+
|
13
|
+
- Fixed a bug with the `has_many_connection` helper, which deserves some explanation. This helper constructs a
|
14
|
+
connection field that returns an ActiveRecord relation. There isn't an easy way to inject functionality into the resolvers
|
15
|
+
that are used by connections (to my knowledge) - eg, by using middleware - so this helper had some GoCo-specific code
|
16
|
+
baked into it, which probably caused odd errors about an undefined constant `GraphSupport` whenever it was used. I can’t
|
17
|
+
quite remove that functionality yet, but I did take it one step closer by having the code first check to see if the constant
|
18
|
+
was defined, and bypass it if it’s not.
|
19
|
+
|
3
20
|
## 0.9.0
|
4
21
|
- Support for graphql version 1.2.1 and higher, but it no longer works with 0.x versions
|
5
22
|
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# GraphQL::Models
|
2
2
|
|
3
|
-
This gem is designed to help you map Active Record models to GraphQL types, both for queries and mutations, using the [`graphql`](https://github.com/rmosolgo/graphql-ruby)
|
3
|
+
This gem is designed to help you map Active Record models to GraphQL types, both for queries and mutations, using the [`graphql`](https://github.com/rmosolgo/graphql-ruby)
|
4
|
+
gem. It assumes that you're using Rails and have `graphql-batch` set up.
|
4
5
|
|
5
6
|
It extends the `define` methods for GraphQL object types to provide a simple syntax for adding fields that access attributes
|
6
7
|
on your model. It also makes it easy to "flatten" models together across associations, or to create fields that just access
|
7
8
|
the association as a separate object type.
|
8
9
|
|
9
|
-
In the process, this gem also converts `snake_case` attribute names into `camelCase` field names. When you go to build a mutation
|
10
|
-
using this gem, it knows how to revert that process, even in cases where the conversion isn't symmetric.
|
10
|
+
In the process, this gem also converts `snake_case` attribute names into `camelCase` field names. When you go to build a mutation using this gem, it knows how to revert that process, even in cases where the conversion isn't symmetric.
|
11
11
|
|
12
12
|
Here's an example:
|
13
13
|
```ruby
|
@@ -31,7 +31,7 @@ EmployeeGraph = GraphQL::ObjectType.define do
|
|
31
31
|
|
32
32
|
# You can also provide the association itself as an object field. In this example, a
|
33
33
|
# Person has one Address. The gem assumes that the corresponding GraphQL object type
|
34
|
-
# is called "
|
34
|
+
# is called "AddressType" (but you can override, see installation section below).
|
35
35
|
has_one :address
|
36
36
|
end
|
37
37
|
end
|
@@ -129,19 +129,23 @@ GraphQL::Models.authorize = -> (context, action, model) {
|
|
129
129
|
user = context['user']
|
130
130
|
model.authorize_changes!(action, user)
|
131
131
|
}
|
132
|
+
|
133
|
+
# The gem assumes that if your model is called `MyModel`, the corresponding type is `MyModelType`.
|
134
|
+
# You can override that convention. Return `nil` if the model doesn't have a GraphQL type:
|
135
|
+
GraphQL::Models.model_to_graphql_type -> (model_class) { "#{model_class.name}Graph".safe_constantize }
|
132
136
|
```
|
133
137
|
|
134
138
|
Finally, you need to set a few options on your schema:
|
135
139
|
```ruby
|
136
|
-
Schema.
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
## Other notes
|
140
|
+
GraphQL::Schema.define do
|
141
|
+
# Set up the graphql-batch gem
|
142
|
+
lazy_resolve(Promise, :sync)
|
143
|
+
instrument(:query, GraphQL::Batch::Setup)
|
142
144
|
|
143
|
-
|
144
|
-
|
145
|
+
# This middleware should be somewhere near the top of your middleware chain
|
146
|
+
middleware GraphQL::Models::Middleware.new
|
147
|
+
end
|
148
|
+
```
|
145
149
|
|
146
150
|
### Database compatibility
|
147
151
|
|
@@ -149,58 +153,83 @@ This gem uses `graphql-batch` to optimize loading associated models in your grap
|
|
149
153
|
queries. It tries to do that in a way that preserves things like scopes that change the order or filter the rows retrieved.
|
150
154
|
|
151
155
|
Unfortunately, that means that it needs to build some custom SQL expressions, and they might not be compatible with every
|
152
|
-
database engine. They should work correctly on PostgreSQL.
|
153
|
-
|
154
|
-
### Naming of GraphQL types
|
155
|
-
|
156
|
-
If your model is named `Something`, this gem assumes that the corresponding object type is called `SomethingGraph`, and it uses
|
157
|
-
the Rails `String#constantize` method to find it. This is mainly needed when you're specifying associations on your object types.
|
156
|
+
database engine. They should work correctly on PostgreSQL. For other databases, your mileage may vary.
|
158
157
|
|
159
158
|
### Global ID's
|
160
159
|
|
161
160
|
When you use the `has_one` or `has_many_array` helpers to output associations, the gem will also include a field that only
|
162
|
-
returns the global ID's of the models. To do that, it calls a method named `gid` on the model. You'll need to provide that method
|
163
|
-
for those fields to work. We do that by defining it in a concern that we include into `ActiveRecord::Base`:
|
161
|
+
returns the global ID's of the models. To do that, it calls a method named `gid` on the model. You'll need to provide that method for those fields to work. We do that by adding it to our `ApplicationRecord` base class:
|
164
162
|
|
165
163
|
```ruby
|
166
|
-
|
167
|
-
extend ActiveSupport::Concern
|
168
|
-
|
164
|
+
class ApplicationRecord < ActiveRecord::Base
|
169
165
|
def gid
|
170
|
-
|
166
|
+
# add code to return a global object ID here
|
171
167
|
end
|
172
168
|
end
|
173
|
-
|
174
|
-
ActiveRecord::Base.send(:include, ActiveRecordExtensions)
|
175
169
|
```
|
176
170
|
|
177
171
|
## Usage
|
178
172
|
|
179
|
-
|
173
|
+
Inside of your GraphQL object types, you use `backed_by_model` to create fields that are tied to your models (see
|
174
|
+
example above). Inside of those blocks, you have some helper methods.
|
180
175
|
|
181
|
-
|
182
|
-
You can use a helper to automatically build GraphQL enum types for them:
|
176
|
+
### Attribute helpers
|
183
177
|
|
178
|
+
You use the `attr` method to add an ordinary attribute from your model to your schema:
|
184
179
|
```ruby
|
185
|
-
|
186
|
-
|
187
|
-
|
180
|
+
backed_by_model :employee do
|
181
|
+
attr :first_name
|
182
|
+
attr :last_name
|
188
183
|
end
|
184
|
+
```
|
189
185
|
|
190
|
-
|
191
|
-
|
186
|
+
The gem knows how to handle basic attribute types: boolean, int, float, and string. For other types, you need
|
187
|
+
to tell it what GraphQL type to use:
|
192
188
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
189
|
+
```ruby
|
190
|
+
# config/initializers/graphql_activerecord.rb
|
191
|
+
GraphQL::Models::DatabaseTypes.register(:decimal, DecimalType)
|
192
|
+
|
193
|
+
# You can wrap the type in a proc, or use a string, so that you don't break code reloading:
|
194
|
+
GraphQL::Models::DatabaseTypes.register(:decimal, -> { DecimalType })
|
195
|
+
GraphQL::Models::DatabaseTypes.register(:decimal, "DecimalType")
|
196
|
+
|
197
|
+
# If you're not using a scalar, you need to give it separate input/output types:
|
198
|
+
GraphQL::Models::DatabaseTypes.register(:date, DateType, DateInputType)
|
199
|
+
```
|
200
|
+
|
201
|
+
#### Nullability of attributes
|
202
|
+
The gem will mark a field as non-nullable if:
|
203
|
+
- the database column is non-null
|
204
|
+
- the attribute has an unconditional presence validator on it
|
205
|
+
|
206
|
+
There are two ways you can override this behavior:
|
207
|
+
- You can pass either `nullable: true` or `nullable: false` to the helper, and no automatic detection happens
|
208
|
+
- You can disable null detection for the entire `backed_by_model` block
|
209
|
+
|
210
|
+
Example:
|
211
|
+
```ruby
|
212
|
+
backed_by_model :employee do
|
213
|
+
# All fields created by the gem will be nullable
|
214
|
+
detect_nulls false
|
215
|
+
|
216
|
+
# Override it on a per-field basis
|
217
|
+
attr :first_name, nullable: false
|
198
218
|
end
|
199
219
|
```
|
200
220
|
|
201
|
-
|
221
|
+
If you use a `proxy_to` block, the gem will automatically detect whether the associated model
|
222
|
+
has a presence validator on it. If it doesn’t, all fields inside of the block are nullable:
|
223
|
+
|
202
224
|
```ruby
|
203
|
-
|
225
|
+
backed_by_model :employee do
|
226
|
+
|
227
|
+
# If you have `validates :person, presence: true` in your model, then nullability
|
228
|
+
# on these fields is preserved. Otherwise, they will all be nullable.
|
229
|
+
proxy_to :person do
|
230
|
+
attr :birthday
|
231
|
+
end
|
232
|
+
end
|
204
233
|
```
|
205
234
|
|
206
235
|
### Association helpers
|
@@ -210,6 +239,29 @@ There are three helpers that you can use to build fields for associated models:
|
|
210
239
|
- `has_many_array` will return all of the associated models as a GraphQL list
|
211
240
|
- `has_many_connection` will return a paged connection of the associated models
|
212
241
|
|
242
|
+
#### Nullability of associations
|
243
|
+
When you use the `has_one` helper, the gem follows the same rules for nullability as it does for attributes. Thus,
|
244
|
+
it’ll check for a presence validator on the association itself:
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
class MyModel < ApplicationRecord
|
248
|
+
belongs_to :some_other_model
|
249
|
+
validates :some_other_model, presence: true
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
In addition, for `belongs_to` models, it’ll check the nullability of the foreign key:
|
254
|
+
```ruby
|
255
|
+
class MyModel < ApplicationRecord
|
256
|
+
belongs_to :some_other_model
|
257
|
+
validates :some_other_model_id, presence: true
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
For `has_many` associations, it does not check for presence validators; rather, it assumes that an empty array
|
262
|
+
will be returned if there are no associated models, so the field is always marked non-null (but subject to the same rules
|
263
|
+
as attributes regarding proxy blocks).
|
264
|
+
|
213
265
|
### Fields inside of `proxy_to` blocks
|
214
266
|
You can also define ordinary fields inside of `proxy_to` blocks. When you do that, your field will receive the associated model
|
215
267
|
as the object, instead of the original model. This is meant to allow you to take advantage of the optimized association loading
|
@@ -227,6 +279,33 @@ backed_by_model :employee do
|
|
227
279
|
end
|
228
280
|
```
|
229
281
|
|
282
|
+
### GraphQL Enum's
|
283
|
+
|
284
|
+
Active Record allows you to define enum fields on your models. They're stored as integers, but treated as strings in your app.
|
285
|
+
You can use a helper to automatically build GraphQL enum types for them:
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
class MyModel < ApplicationRecord
|
289
|
+
enum status: [:active, :inactive]
|
290
|
+
graphql_enum :status
|
291
|
+
end
|
292
|
+
|
293
|
+
# You can access the auto-built type if you need to:
|
294
|
+
MyModel.graphql_enum_types[:status]
|
295
|
+
|
296
|
+
# When you use it inside of your GraphQL schema, it'll know to use the GraphQL enum type:
|
297
|
+
MyModelGraph = GraphQL::ObjectType.define do
|
298
|
+
backed_by_model :my_model do
|
299
|
+
attr :status
|
300
|
+
end
|
301
|
+
end
|
302
|
+
```
|
303
|
+
|
304
|
+
You can also manually specify the type to use, if you just want the type mapping behavior:
|
305
|
+
```ruby
|
306
|
+
graphql_enum :status, type: StatusEnum
|
307
|
+
```
|
308
|
+
|
230
309
|
### Defining Mutations
|
231
310
|
|
232
311
|
When you define a mutation, there are a few parameters that you need to pass. Here's an example:
|
@@ -306,7 +385,6 @@ them. It will destroy extra models, or create missing models.
|
|
306
385
|
- `object_to_model`
|
307
386
|
- Retrieving field metadata (for building an authorization middleware)
|
308
387
|
- Validation error exceptions
|
309
|
-
- Why all fields on query types are nullable
|
310
388
|
|
311
389
|
|
312
390
|
## Development
|
@@ -226,20 +226,10 @@ module GraphQL
|
|
226
226
|
object_to_base_model: object_to_model
|
227
227
|
})
|
228
228
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
return nil unless model
|
234
|
-
GraphSupport.secure(model.public_send(association), context, permission: options[:permission] || :read)
|
235
|
-
end
|
236
|
-
end
|
237
|
-
else
|
238
|
-
GraphQL::Define::AssignConnection.call(graph_type, camel_name, connection_type) do
|
239
|
-
resolve -> (model, args, context) do
|
240
|
-
return nil unless model
|
241
|
-
model.public_send(association)
|
242
|
-
end
|
229
|
+
GraphQL::Define::AssignConnection.call(graph_type, camel_name, connection_type) do
|
230
|
+
resolve -> (model, args, context) do
|
231
|
+
return nil unless model
|
232
|
+
model.public_send(association)
|
243
233
|
end
|
244
234
|
end
|
245
235
|
end
|
@@ -3,7 +3,7 @@ module GraphQL::Models::HashCombiner
|
|
3
3
|
# Takes a set of hashes that represent conditions, and combines them into the smallest number of hashes
|
4
4
|
def combine(hashes)
|
5
5
|
# Group the hashes by keys. If they are querying different columns, they can't be combined
|
6
|
-
by_keys = hashes.group_by { |h| h.keys }
|
6
|
+
by_keys = hashes.group_by { |h| h.keys.sort }
|
7
7
|
by_keys.map { |keys, values| combine_core(values, keys) }.flatten
|
8
8
|
end
|
9
9
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.0.pre.
|
4
|
+
version: 0.10.0.pre.alpha3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Foster
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|