grape-roar 0.4.0 → 0.4.1

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.
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: "../"