graphql-activerecord 0.10.0.pre.alpha2 → 0.10.0.pre.alpha3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1de65b95cb8f19cdca7643404e8dd55c072e2a3b
4
- data.tar.gz: a21768845b286ae9f539725e2d7ab52002b6d3c0
3
+ metadata.gz: 51188ba9e6b78d0a5db797c4cba6501995c01b85
4
+ data.tar.gz: 1d38ee1f0b1b5ef17e8a38d9ef8e9cb06019631c
5
5
  SHA512:
6
- metadata.gz: 97aa43f8f65466f9b069786fbba22f7552f775c42693b3d148fdc931cd7d41606e729c4af73e75f4708a23ea4af7397b11ddf202bac57e9a501d96d787371cce
7
- data.tar.gz: 9b541a012f17e0b7cb6dbba60110d64fcc5df04fd34eec2624d2ee7f6c91990bac411e42860b4211b62416581d5fbdf6aae5cb008b536fd7dd6712cc41870bd3
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) gem. It assumes that you're using Rails, Relay and PostgreSQL.
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 "AddressGraph".
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.query_execution_strategy = GraphQL::Batch::ExecutionStrategy
137
- Schema.mutation_execution_strategy = GraphQL::Batch::MutationExecutionStrategy
138
- Schema.middleware << GraphQL::Models::Middleware.new
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
- The code in this gem was originally part of our main codebase, before we extracted it. So there are a few places where some of
144
- of our own conventions leak through. Eventually, I'd like to clean these up, but you'll have to live with them for now.
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
- module ActiveRecordExtensions
167
- extend ActiveSupport::Concern
168
-
164
+ class ApplicationRecord < ActiveRecord::Base
169
165
  def gid
170
- NodeHelpers.encode_id(self.class.name, self.id)
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
- ### GraphQL Enum's
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
- Active Record allows you to define enum fields on your models. They're stored as integers, but treated as strings in your app.
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
- class MyModel < ActiveRecord::Base
186
- enum status: [:active, :inactive]
187
- graphql_enum :status
180
+ backed_by_model :employee do
181
+ attr :first_name
182
+ attr :last_name
188
183
  end
184
+ ```
189
185
 
190
- # You can access the auto-built type if you need to:
191
- MyModel.graphql_enum_types[:status]
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
- # When you use it inside of your GraphQL schema, it'll know to use the GraphQL enum type:
194
- MyModelGraph = GraphQL::ObjectType.define do
195
- backed_by_model :my_model do
196
- attr :status
197
- end
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
- You can also manually specify the type to use, if you just want the type mapping behavior:
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
- graphql_enum :status, type: StatusEnum
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
- # TODO: Figure out a way to remove this from the gem. It's only applicable to GoCo's codebase.
230
- if Object.const_defined?('GraphSupport') && GraphSupport.respond_to?(:secure)
231
- GraphQL::Define::AssignConnection.call(graph_type, camel_name, connection_type) do
232
- resolve -> (model, args, context) do
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
 
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Models
3
- VERSION = "0.10.0-alpha2"
3
+ VERSION = "0.10.0-alpha3"
4
4
  end
5
5
  end
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.alpha2
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-02-02 00:00:00.000000000 Z
11
+ date: 2017-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport