grape-roar 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +6 -2
  5. data/.rubocop_todo.yml +38 -9
  6. data/.travis.yml +29 -3
  7. data/Appraisals +9 -0
  8. data/CHANGELOG.md +10 -0
  9. data/Gemfile +6 -3
  10. data/README.md +218 -0
  11. data/Rakefile +3 -1
  12. data/gemfiles/with_activerecord.gemfile +22 -0
  13. data/gemfiles/with_mongoid.gemfile +22 -0
  14. data/grape-roar.gemspec +3 -0
  15. data/lib/grape-roar.rb +2 -0
  16. data/lib/grape/roar.rb +3 -0
  17. data/lib/grape/roar/decorator.rb +2 -0
  18. data/lib/grape/roar/extensions.rb +3 -0
  19. data/lib/grape/roar/extensions/relations.rb +23 -0
  20. data/lib/grape/roar/extensions/relations/adapters.rb +22 -0
  21. data/lib/grape/roar/extensions/relations/adapters/active_record.rb +35 -0
  22. data/lib/grape/roar/extensions/relations/adapters/base.rb +49 -0
  23. data/lib/grape/roar/extensions/relations/adapters/mongoid.rb +38 -0
  24. data/lib/grape/roar/extensions/relations/dsl_methods.rb +86 -0
  25. data/lib/grape/roar/extensions/relations/exceptions.rb +14 -0
  26. data/lib/grape/roar/extensions/relations/mapper.rb +89 -0
  27. data/lib/grape/roar/extensions/relations/validations.rb +5 -0
  28. data/lib/grape/roar/extensions/relations/validations/active_record.rb +67 -0
  29. data/lib/grape/roar/extensions/relations/validations/misc.rb +18 -0
  30. data/lib/grape/roar/extensions/relations/validations/mongoid.rb +88 -0
  31. data/lib/grape/roar/formatter.rb +2 -0
  32. data/lib/grape/roar/representer.rb +2 -0
  33. data/lib/grape/roar/version.rb +3 -1
  34. data/spec/config/mongoid.yml +6 -0
  35. data/spec/decorator_spec.rb +3 -1
  36. data/spec/extensions/relations/adapters/active_record_spec.rb +26 -0
  37. data/spec/extensions/relations/adapters/adapters_module_spec.rb +11 -0
  38. data/spec/extensions/relations/adapters/mongoid_spec.rb +26 -0
  39. data/spec/extensions/relations/dsl_methods_spec.rb +159 -0
  40. data/spec/extensions/relations/mapper_spec.rb +88 -0
  41. data/spec/extensions/relations/validations/active_record_spec.rb +46 -0
  42. data/spec/extensions/relations/validations/mongoid_spec.rb +88 -0
  43. data/spec/nested_representer_spec.rb +4 -13
  44. data/spec/present_with_spec.rb +3 -12
  45. data/spec/relations_spec.rb +76 -0
  46. data/spec/representer_spec.rb +3 -12
  47. data/spec/spec_helper.rb +15 -1
  48. data/spec/support/{article.rb → all/article.rb} +3 -1
  49. data/spec/support/{article_representer.rb → all/article_representer.rb} +2 -0
  50. data/spec/support/all/grape_app_context.rb +18 -0
  51. data/spec/support/{order.rb → all/order.rb} +3 -1
  52. data/spec/support/{order_representer.rb → all/order_representer.rb} +3 -1
  53. data/spec/support/{product.rb → all/product.rb} +2 -0
  54. data/spec/support/{product_representer.rb → all/product_representer.rb} +3 -1
  55. data/spec/support/{user.rb → all/user.rb} +2 -0
  56. data/spec/support/{user_representer.rb → all/user_representer.rb} +2 -0
  57. data/spec/support/mongoid/relational_models/cart.rb +7 -0
  58. data/spec/support/mongoid/relational_models/item.rb +7 -0
  59. data/spec/support/mongoid/relational_models/mongoid_cart_representer.rb +27 -0
  60. data/spec/support/mongoid/relational_models/mongoid_item_representer.rb +13 -0
  61. metadata +55 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35902dfffe597a1d52445e821a2bfa9dd69659eb
4
- data.tar.gz: b0e312c15b9f890086c4238e1cfc6bd21a939d73
3
+ metadata.gz: 275fcec452345c4903f635a7a4d3cf87ac5f662a
4
+ data.tar.gz: fa6541ccfd36c24e05460da5a5eb9f3c7b837f7e
5
5
  SHA512:
6
- metadata.gz: 9764c7913adbb061397bc7a589ace6205e05d91ad4abcfcc6db4fe846789236e67938eda3cd47a78f1fd8b5af97dfd56d05553a2ad04fb03fdb00f1827d4220e
7
- data.tar.gz: 4e2b82c862999d5f8e748624d7b8cfd099a5f3b4e057efc122bc2ecbdeb5e0aca356b034d28f393ec112bba45cbc2323406f38004765343eefb168f669ebea43
6
+ metadata.gz: 773562779d81ea01fdeb3cf9b51ebc85d222f39945204424a917b07586bfa3a7b426b220ba5aa8b5c2daf16797e001c795cba7114c976aed348e98d4c929da77
7
+ data.tar.gz: 3b3f6c811fa78c9b3215fd2c100d1acbd94cd2fe53b00812837b0674284f0668e34a6c3c355adaab69085056439765d3772a50a4b5ef8510c88332599d959637
data/.gitignore CHANGED
@@ -2,3 +2,4 @@ Gemfile.lock
2
2
  doc
3
3
  pkg
4
4
  .bundle
5
+ gemfiles/*.lock
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
+ --require spec_helper
1
2
  --color
2
3
  --format=documentation
3
4
 
@@ -1,6 +1,10 @@
1
1
  AllCops:
2
+ TargetRubyVersion: 2.3
3
+ DisplayCopNames: true
2
4
  Exclude:
3
- - vendor/**/*
4
5
  - bin/**/*
6
+ - gemfiles/**/*
7
+ - spec/**/*
8
+ - vendor/**/*
5
9
 
6
- inherit_from: .rubocop_todo.yml
10
+ inherit_from: .rubocop_todo.yml
@@ -1,20 +1,49 @@
1
- # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2014-12-18 10:52:56 -0500 using RuboCop version 0.28.0.
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2017-07-02 09:34:39 -0400 using RuboCop version 0.49.1.
3
4
  # The point is for the user to remove these configuration records
4
5
  # one by one as the offenses are removed from the code base.
5
6
  # Note that changes in the inspected code, or installation of new
6
7
  # versions of RuboCop, may require this file to be generated again.
7
8
 
8
- # Offense count: 8
9
- # Configuration parameters: AllowURI, URISchemes.
9
+ # Offense count: 7
10
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
11
+ # URISchemes: http, https
10
12
  Metrics/LineLength:
11
- Max: 304
13
+ Max: 108
12
14
 
13
- # Offense count: 13
15
+ # Offense count: 14
14
16
  Style/Documentation:
15
- Enabled: false
17
+ Exclude:
18
+ - 'spec/**/*'
19
+ - 'test/**/*'
20
+ - 'lib/grape/roar/decorator.rb'
21
+ - 'lib/grape/roar/extensions/relations.rb'
22
+ - 'lib/grape/roar/extensions/relations/adapters.rb'
23
+ - 'lib/grape/roar/extensions/relations/adapters/active_record.rb'
24
+ - 'lib/grape/roar/extensions/relations/adapters/base.rb'
25
+ - 'lib/grape/roar/extensions/relations/adapters/mongoid.rb'
26
+ - 'lib/grape/roar/extensions/relations/dsl_methods.rb'
27
+ - 'lib/grape/roar/extensions/relations/mapper.rb'
28
+ - 'lib/grape/roar/extensions/relations/validations/active_record.rb'
29
+ - 'lib/grape/roar/extensions/relations/validations/misc.rb'
30
+ - 'lib/grape/roar/extensions/relations/validations/mongoid.rb'
31
+ - 'lib/grape/roar/formatter.rb'
32
+ - 'lib/grape/roar/representer.rb'
16
33
 
17
34
  # Offense count: 1
18
- # Configuration parameters: Exclude.
35
+ # Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
36
+ # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
19
37
  Style/FileName:
20
- Enabled: false
38
+ Exclude:
39
+ - 'lib/grape-roar.rb'
40
+
41
+ # Offense count: 3
42
+ # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
43
+ # NamePrefix: is_, has_, have_
44
+ # NamePrefixBlacklist: is_, has_, have_
45
+ # NameWhitelist: is_a?
46
+ Style/PredicateName:
47
+ Exclude:
48
+ - 'spec/**/*'
49
+ - 'lib/grape/roar/extensions/relations/validations/active_record.rb'
@@ -5,6 +5,32 @@ language: ruby
5
5
  cache: bundler
6
6
 
7
7
  rvm:
8
- - ruby-head
9
- - 2.2.2
10
- - 2.3.1
8
+ - 2.4.1
9
+ - 2.3.4
10
+
11
+ gemfile:
12
+ - Gemfile
13
+ - gemfiles/with_activerecord.gemfile
14
+ - gemfiles/with_mongoid.gemfile
15
+
16
+ services:
17
+ - mongodb
18
+ - pg
19
+
20
+ matrix:
21
+ include:
22
+ - env: SPEC_OPTS="--tag active_record"
23
+ gemfile: gemfiles/with_activerecord.gemfile
24
+ rvm: 2.4.1
25
+ - env: SPEC_OPTS="--tag mongoid"
26
+ gemfile: gemfiles/with_mongoid.gemfile
27
+ rvm: 2.4.1
28
+ - env: SPEC_OPTS="--tag active_record"
29
+ gemfile: gemfiles/with_activerecord.gemfile
30
+ rvm: 2.3.4
31
+ - env: SPEC_OPTS="--tag mongoid"
32
+ gemfile: gemfiles/with_mongoid.gemfile
33
+ rvm: 2.3.4
34
+
35
+
36
+ script: bundle exec rake
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'with-activerecord' do
4
+ gem 'activerecord', '>= 4.0.0', require: 'active_record'
5
+ end
6
+
7
+ appraise 'with-mongoid' do
8
+ gem 'mongoid', '>= 5.0.0', require: 'mongoid'
9
+ end
@@ -1,17 +1,27 @@
1
+ 0.4.1 (07/14/2017)
2
+ ----
3
+
4
+ * [#22](https://github.com/ruby-grape/grape-roar/pull/22): Adds Grape::Roar::Extensions::Relations.
5
+
6
+
1
7
  0.4.0 (02/18/2017)
2
8
  ------------------
9
+
3
10
  * [#21](https://github.com/ruby-grape/grape-roar/pull/21): Fixes serialization issue due to [representable](https://github.com/trailblazer/representable) API change, drop support for Ruby < `2.1.0` - [@mach-kernel](https://github.com/mach-kernel).
4
11
 
12
+
5
13
  0.3.0 (12/31/2014)
6
14
  ------------------
7
15
 
8
16
  * Added support for Roar 1.0 - [@dblock](https://github.com/dblock).
9
17
 
18
+
10
19
  0.2.0 (12/18/2014)
11
20
  ------------------
12
21
 
13
22
  * [#10](https://github.com/ruby-grape/grape-roar/pull/10): Support for Roar decorator - [@sdbondi](https://github.com/sdbondi).
14
23
 
24
+
15
25
  0.1.0 (7/17/2014)
16
26
  -----------------
17
27
 
data/Gemfile CHANGED
@@ -1,17 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
4
6
 
5
7
  group :development do
6
- gem 'rake', '~> 10.5.0'
8
+ gem 'appraisal', '2.2.0'
7
9
  end
8
10
 
9
11
  group :test do
10
- gem 'rspec', '~> 3.1'
11
12
  gem 'rack-test'
13
+ gem 'rspec', '~> 3.1'
12
14
  end
13
15
 
14
16
  group :development, :test do
15
- gem 'rubocop', '0.28.0'
16
17
  gem 'nokogiri', '1.6.3.1'
18
+ gem 'rake', '~> 10.5.0'
19
+ gem 'rubocop', '0.49.1'
17
20
  end
data/README.md CHANGED
@@ -121,6 +121,224 @@ get 'products' do
121
121
  end
122
122
  ```
123
123
 
124
+ ### Relation Extensions
125
+
126
+ If you use either `ActiveRecord` or `Mongoid`, you can use the `Grape::Roar::Extensions::Relations` DSL to expose the relationships in between your models as a HAL response. The DSL methods used are the same regardless of what your ORM/ODM is, as long as there exists [an adapter for it](#designing-adapters).
127
+
128
+ #### Designing Representers
129
+
130
+ Arguments passed to `#relation` are forwarded to `roar`. Single member relations (e.g. `belongs_to`) are represented using `#property`, collections are represented using `#collection`; arguments provided to `#relation` will be passed through these methods (i.e. additional arguments [roar](https://github.com/trailblazer/roar) and [representable](http://trailblazer.to/gems/representable/3.0/api.html) accept).
131
+
132
+ A default base URI is constructed from a `Grape::Request` by concatenating the `#base_url` and `#script_name` properties. The resource path is extracted from the name of the relation.
133
+
134
+ Otherwise, the extensions attempt to look up the correct representer module/class for the objects (e.g. we infer the `extend` argument). You can always specify the correct representer to use on your own.
135
+
136
+ ##### Example Models
137
+
138
+ ```ruby
139
+ class Item < ActiveRecord::Base
140
+ belongs_to :cart
141
+ end
142
+
143
+ class Cart < ActiveRecord::Base
144
+ has_many :items
145
+ end
146
+ ```
147
+
148
+ ##### Example Representers
149
+
150
+ ```ruby
151
+ class ItemEntity < Grape::Roar::Decorator
152
+ include Roar::JSON
153
+ include Roar::JSON::HAL
154
+ include Roar::Hypermedia
155
+
156
+ include Grape::Roar::Extensions::Relations
157
+
158
+ # Cart will be presented under the _embedded key
159
+ relation :belongs_to, :cart, embedded: true
160
+
161
+ link_self
162
+ end
163
+
164
+ class CartEntity < Grape::Roar::Decorator
165
+ include Roar::JSON
166
+ include Roar::JSON::HAL
167
+ include Roar::Hypermedia
168
+
169
+ include Grape::Roar::Extensions::Relations
170
+
171
+ # Items will be presented under the _links key
172
+ relation :has_many, :items, embedded: false
173
+
174
+ link_self
175
+ end
176
+ ```
177
+
178
+ Although this example uses `Grape::Roar::Decorator`, you can also use a module as show in prior examples above. If doing so, you no longer have to mix in `Grape::Roar::Representer`.
179
+
180
+ ##### Example Item
181
+ ```javascript
182
+ {
183
+ "_embedded": {
184
+ "cart": {
185
+ "_links": {
186
+ "self": {
187
+ "href": "http://example.org/carts/1"
188
+ },
189
+ "items": [{
190
+ "href": "http://example.org/items/1"
191
+ }]
192
+ }
193
+ }
194
+ },
195
+ "_links": {
196
+ "self": {
197
+ "href": "http://example.org/items/1"
198
+ }
199
+ }
200
+ }
201
+ ```
202
+
203
+ ##### Example Cart
204
+ ```javascript
205
+ {
206
+ "_links": {
207
+ "self": {
208
+ "href": "http://example.org/carts/1"
209
+ },
210
+ "items": [{
211
+ "href": "http://example.org/items/1"
212
+ }, {
213
+ "href": "http://example.org/items/2"
214
+ }, {
215
+ "href": "http://example.org/items/3"
216
+ }, {
217
+ "href": "http://example.org/items/4"
218
+ }, {
219
+ "href": "http://example.org/items/5"
220
+ }]
221
+ }
222
+ }
223
+ ```
224
+
225
+ #### Errors
226
+
227
+ Should you incorrectly describe a relationship (e.g. you specify has_one but your model specifies has_many), an exception will be raised to notify you of the mismatch:
228
+
229
+ ```ruby
230
+ Grape::Roar::Extensions::Relations::Exceptions::InvalidRelationError:
231
+ Expected Mongoid::Relations::Referenced::One, got Mongoid::Relations::Referenced::Many!
232
+ ```
233
+
234
+ #### Change how URLs are presented
235
+
236
+ The `opts` hash below is the same one as shown in prior examples.
237
+
238
+ ##### Override base URI mappings
239
+ ```ruby
240
+ class BarEntity < Grape::Roar::Decorator
241
+ include Roar::JSON
242
+ include Roar::JSON::HAL
243
+ include Roar::Hypermedia
244
+
245
+ include Grape::Roar::Extensions::Relations
246
+
247
+ # This is our default implementation
248
+ map_base_url do |opts|
249
+ request = Grape::Request.new(opts[:env])
250
+ "#{request.base_url}#{request.script_name}"
251
+ end
252
+
253
+ relation :has_many, :bars, embedded: false
254
+ end
255
+ ```
256
+
257
+ ##### Override resource URI mappings
258
+ ```ruby
259
+ class BarEntity < Grape::Roar::Decorator
260
+ include Roar::JSON
261
+ include Roar::JSON::HAL
262
+ include Roar::Hypermedia
263
+
264
+ include Grape::Roar::Extensions::Relations
265
+
266
+ # This is our default implementation
267
+ map_resource_path do |_opts, object, relation_name|
268
+ "#{relation_name}/#{object.id}"
269
+ end
270
+
271
+ relation :has_many, :bars, embedded: false
272
+ end
273
+ ```
274
+
275
+ #### Designing Adapters
276
+
277
+ If you have custom domain objects, you can create an adapter to make your models compatible with the DSL methods. Below is an example of the `ActiveRecord` adapter.
278
+
279
+ ##### Example: ActiveRecord Adapter
280
+
281
+ ```ruby
282
+ module Extensions
283
+ module RelationalModels
284
+ module Adapter
285
+ class ActiveRecord < Base
286
+ include Validations::ActiveRecord
287
+
288
+ # We map your domain object to the correct adapter
289
+ # during runtime.
290
+ valid_for { |klass| klass < ::ActiveRecord::Base }
291
+
292
+ def collection_methods
293
+ @collection_methods ||= %i(has_many has_and_belongs_to_many)
294
+ end
295
+
296
+ def name_for_represented(represented)
297
+ klass_name = case represented
298
+ when ::ActiveRecord::Relation
299
+ represented.klass.name
300
+ else
301
+ represented.class.name
302
+ end
303
+ klass_name.demodulize.pluralize.downcase
304
+ end
305
+
306
+ def single_entity_methods
307
+ @single_entity_methods ||= %i(has_one belongs_to)
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
313
+ ```
314
+
315
+ ##### Validations
316
+
317
+ Errors are handled by creating methods corresponding to those in `collection_methods` and `single_entity_methods`. For example, this is the validator for `belongs_to`:
318
+
319
+ ```ruby
320
+ module ActiveRecord
321
+ include Validations::Misc
322
+
323
+ def belongs_to_valid?(relation)
324
+ relation = klass.reflections[relation]
325
+
326
+ return true if relation.is_a?(
327
+ ::ActiveRecord::Reflection::BelongsToReflection
328
+ )
329
+
330
+ # Provided by Validations::Misc
331
+ invalid_relation(
332
+ ::ActiveRecord::Reflection::BelongsToReflection,
333
+ relation.class
334
+ )
335
+ end
336
+ end
337
+ ```
338
+
339
+ After writing your validation methods, just mix them into your adapter. You can choose to not write validation methods; they are only invoked if your adapter responds to them.
340
+
341
+
124
342
  Contributing
125
343
  ------------
126
344
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'bundler'
3
5
 
@@ -13,4 +15,4 @@ end
13
15
  require 'rubocop/rake_task'
14
16
  RuboCop::RakeTask.new(:rubocop)
15
17
 
16
- task default: [:rubocop, :spec]
18
+ task default: %i[rubocop spec]
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", ">= 4.0.0", require: "active_record"
6
+
7
+ group :development do
8
+ gem "appraisal", "2.2.0"
9
+ end
10
+
11
+ group :test do
12
+ gem "rack-test"
13
+ gem "rspec", "~> 3.1"
14
+ end
15
+
16
+ group :development, :test do
17
+ gem "nokogiri", "1.6.3.1"
18
+ gem "rake", "~> 10.5.0"
19
+ gem "rubocop", "0.49.1"
20
+ end
21
+
22
+ gemspec path: "../"