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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ describe Grape::Roar::Extensions::Relations::Validations::ActiveRecord, active_record: true do
3
+ let(:model_klass) { double }
4
+
5
+ subject do
6
+ klass = Class.new do
7
+ def initialize(klass)
8
+ @klass = klass
9
+ end
10
+
11
+ attr_reader :klass
12
+ end
13
+
14
+ klass.include(described_class)
15
+ klass.new(model_klass)
16
+ end
17
+
18
+ before do
19
+ expect(model_klass).to receive(:reflections).twice
20
+ .and_return(reflections)
21
+ end
22
+
23
+ %w[
24
+ belongs_to
25
+ has_many
26
+ has_one
27
+ has_and_belongs_to_many
28
+ ].each do |relation|
29
+ context "##{relation}_valid?" do
30
+ let(:relation_klass) do
31
+ "::ActiveRecord::Reflection::#{relation.camelize}"\
32
+ 'Reflection'.constantize.allocate
33
+ end
34
+
35
+ let(:reflections) { { test: relation_klass, fail: Class.new } }
36
+
37
+ it 'properly validates the relation' do
38
+ expect(subject.send("#{relation}_valid?", :test)).to eql(true)
39
+ expect { subject.send("#{relation}_valid?", :fail) }.to raise_error(
40
+ Grape::Roar::Extensions::Relations::\
41
+ Exceptions::InvalidRelationError
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ describe Grape::Roar::Extensions::Relations::Validations::Mongoid, mongoid: true do
3
+ let(:model_klass) { double }
4
+
5
+ subject do
6
+ klass = Class.new do
7
+ def initialize(klass)
8
+ @klass = klass
9
+ end
10
+
11
+ attr_reader :klass
12
+ end
13
+
14
+ klass.include(described_class)
15
+ klass.new(model_klass)
16
+ end
17
+
18
+ let(:map_reflection) do
19
+ proc do |r|
20
+ case r
21
+ when /and/ then 'ManyToMany'
22
+ when /many/ then 'Many'
23
+ when /one/ then 'One'
24
+ when /belongs/ then 'In'
25
+ end
26
+ end
27
+ end
28
+
29
+ before do
30
+ expect(model_klass).to receive(:reflect_on_association).twice do |r|
31
+ if r == :test_rel
32
+ {
33
+ relation: "Mongoid::Relations::#{relation_klass}"\
34
+ "::#{map_reflection.call(test_method)}".constantize
35
+ }
36
+ else
37
+ { relation: double }
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'referenced' do
43
+ let(:relation_klass) { 'Referenced' }
44
+
45
+ %i[
46
+ belongs_to
47
+ has_and_belongs_to_many
48
+ has_many
49
+ has_one
50
+ ].each do |test_method|
51
+ context "##{test_method}_valid? validates the relation" do
52
+ let(:test_method) { test_method }
53
+
54
+ it 'properly validates the relation' do
55
+ expect(subject.send("#{test_method}_valid?", :test_rel)).to eql(true)
56
+
57
+ expect do
58
+ subject.send("#{test_method}_valid?", :fail)
59
+ end.to raise_error(
60
+ Grape::Roar::Extensions::Relations::\
61
+ Exceptions::InvalidRelationError
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'referenced' do
69
+ let(:relation_klass) { 'Embedded' }
70
+
71
+ %i[embeds_one embeds_many].each do |test_method|
72
+ context "##{test_method}_valid? validates the relation" do
73
+ let(:test_method) { test_method }
74
+
75
+ it 'properly validates the relation' do
76
+ expect(subject.send("#{test_method}_valid?", :test_rel)).to eql(true)
77
+
78
+ expect do
79
+ subject.send("#{test_method}_valid?", :fail)
80
+ end.to raise_error(
81
+ Grape::Roar::Extensions::Relations::\
82
+ Exceptions::InvalidRelationError
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,20 +1,11 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe Grape::Roar do
4
- subject do
5
- Class.new(Grape::API)
6
- end
7
3
 
8
- before do
9
- subject.format :json
10
- subject.formatter :json, Grape::Formatter::Roar
11
- end
12
-
13
- def app
14
- subject
15
- end
16
4
 
5
+ describe Grape::Roar do
17
6
  context 'nested representer' do
7
+ include_context 'Grape API App'
8
+
18
9
  before do
19
10
  subject.get('/order/:id') do
20
11
  order = Order.new(id: params[:id], client_id: 42)
@@ -1,18 +1,9 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe Grape::Roar do
4
- subject do
5
- Class.new(Grape::API)
6
- end
7
3
 
8
- before do
9
- subject.format :json
10
- subject.formatter :json, Grape::Formatter::Roar
11
- end
12
4
 
13
- def app
14
- subject
15
- end
5
+ describe Grape::Roar do
6
+ include_context 'Grape API App'
16
7
 
17
8
  context 'using present' do
18
9
  before do
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Grape::Roar::Extensions::Relations do
4
+ context '.included' do
5
+ subject { Class.new }
6
+ after { subject.include(described_class) }
7
+
8
+ it 'mixes DSLMethods into the singleton class of its target' do
9
+ expect(subject.singleton_class).to receive(:include).with(
10
+ Grape::Roar::Extensions::Relations::DSLMethods
11
+ )
12
+ end
13
+ end
14
+
15
+ context 'with mongoid', mongoid: true do
16
+ include_context 'Grape API App'
17
+
18
+ # Make sure Mongo is empty
19
+ before(:each) { Mongoid::Config.purge! }
20
+
21
+ let(:result) { JSON.parse(last_response.body) }
22
+
23
+ context 'has_many, embedded: false' do
24
+ before do
25
+ subject.get('/carts/:id') do
26
+ cart = Cart.create(id: params[:id])
27
+ Array.new(5).map { Item.create(cart: cart) }
28
+ present(cart, with: MongoidCartRepresenter)
29
+ end
30
+
31
+ get '/carts/1'
32
+ end
33
+
34
+ it 'correctly generates the self link' do
35
+ expect(result['_links']['self']['href']).to eql(
36
+ 'http://example.org/carts/1'
37
+ )
38
+ end
39
+
40
+ it 'correctly generates links for items' do
41
+ expect(result['_links']['items'].map { |l| l['href'] }).to all(
42
+ match(/^http:\/\/example\.org\/items\/[A-Za-z0-9]*$/)
43
+ )
44
+ end
45
+ end
46
+
47
+ context 'belongs_to, embedded: true' do
48
+ before do
49
+ subject.get('/items/:id') do
50
+ present(
51
+ Item.create(id: params[:id], cart: Cart.create),
52
+ with: MongoidItemRepresenter
53
+ )
54
+ end
55
+
56
+ get '/items/1'
57
+ end
58
+
59
+ it 'correctly generates the self link' do
60
+ expect(result['_links']['self']['href']).to eql(
61
+ 'http://example.org/items/1'
62
+ )
63
+ end
64
+
65
+ it 'correctly represents the embedded cart' do
66
+ expect(result['_embedded']['cart']['_links']['self']['href'])
67
+ .to match(/^http:\/\/example\.org\/carts\/[A-Za-z0-9]*$/)
68
+
69
+ expect(result['_embedded']['cart']['_links']['items'].count).to eql(1)
70
+ expect(result['_embedded']['cart']['_links']['items'].first).to eql(
71
+ result['_links']['self']
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,18 +1,9 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe Grape::Roar do
4
- subject do
5
- Class.new(Grape::API)
6
- end
7
3
 
8
- before do
9
- subject.format :json
10
- subject.formatter :json, Grape::Formatter::Roar
11
- end
12
4
 
13
- def app
14
- subject
15
- end
5
+ describe Grape::Roar do
6
+ include_context 'Grape API App'
16
7
 
17
8
  context 'representer' do
18
9
  before do
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
4
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
5
 
4
6
  require 'bundler'
5
7
  Bundler.setup :default, :test
8
+ Bundler.require
6
9
 
7
10
  require 'grape'
8
11
 
@@ -12,10 +15,21 @@ require 'roar/hypermedia'
12
15
 
13
16
  require 'grape/roar'
14
17
  require 'rack/test'
18
+
15
19
  require 'rspec'
16
20
 
17
21
  RSpec.configure do |config|
18
22
  config.include Rack::Test::Methods
23
+ config.filter_run_excluding(
24
+ active_record: true, mongoid: true
25
+ )
19
26
  end
20
27
 
21
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
28
+ Dir["#{File.dirname(__FILE__)}/support/all/**/*.rb"].each { |f| require f }
29
+
30
+ # For Relational Extension Tests
31
+ if defined?(Mongoid)
32
+ ENV['MONGOID_ENV'] ||= 'test'
33
+ Mongoid.load!('./spec/config/mongoid.yml')
34
+ Dir["#{File.dirname(__FILE__)}/support/mongoid/**/*.rb"].each { |f| require f }
35
+ end
@@ -1,4 +1,6 @@
1
- require 'support/article_representer'
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/all/article_representer'
2
4
 
3
5
  class Article
4
6
  include Roar::JSON
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ArticleRepresenter
2
4
  include Roar::JSON
3
5
  include Roar::Hypermedia
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+
5
+ shared_context 'Grape API App' do
6
+ subject do
7
+ Class.new(Grape::API)
8
+ end
9
+
10
+ before do
11
+ subject.format :json
12
+ subject.formatter :json, Grape::Formatter::Roar
13
+ end
14
+
15
+ def app
16
+ subject
17
+ end
18
+ end
@@ -1,4 +1,6 @@
1
- require 'support/order_representer'
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/all/order_representer'
2
4
 
3
5
  class Order
4
6
  include Roar::JSON
@@ -1,4 +1,6 @@
1
- require 'support/article'
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/all/article'
2
4
 
3
5
  module OrderRepresenter
4
6
  include Roar::JSON
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Product
2
4
  attr_accessor :title, :id
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'roar/json/hal'
2
4
 
3
5
  module ProductRepresenter
@@ -10,6 +12,6 @@ module ProductRepresenter
10
12
 
11
13
  link :self do |opts|
12
14
  request = Grape::Request.new(opts[:env])
13
- "#{request.url}"
15
+ request.url.to_s
14
16
  end
15
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class User
2
4
  attr_accessor :id, :name
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class UserRepresenter < Grape::Roar::Decorator
2
4
  include Roar::JSON
3
5
  include Roar::Hypermedia
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Cart
4
+ include ::Mongoid::Document
5
+
6
+ has_many :items
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Item
4
+ include ::Mongoid::Document
5
+
6
+ belongs_to :cart
7
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MongoidCartRepresenter
4
+ include Roar::JSON
5
+ include Roar::JSON::HAL
6
+ include Roar::Hypermedia
7
+
8
+ include Grape::Roar::Extensions::Relations
9
+
10
+ relation :has_many, :items
11
+
12
+ link_self
13
+ end
14
+
15
+ # Class style below, but want to test module
16
+
17
+ # class MongoidCartRepresenter < Grape::Roar::Decorator
18
+ # include Roar::JSON
19
+ # include Roar::JSON::HAL
20
+ # include Roar::Hypermedia
21
+
22
+ # include Grape::Roar::Extensions::Relations
23
+
24
+ # relation :has_many, :items
25
+
26
+ # link_self
27
+ # end