grape-roar 0.4.0 → 0.5.0

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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +2 -0
  3. data/.github/dependabot.yml +6 -0
  4. data/.github/workflows/danger.yml +22 -0
  5. data/.github/workflows/lint.yml +15 -0
  6. data/.github/workflows/test-activerecord.yml +34 -0
  7. data/.github/workflows/test-mongodb.yml +33 -0
  8. data/.github/workflows/test.yml +19 -0
  9. data/.gitignore +1 -0
  10. data/.rspec +1 -0
  11. data/.rubocop.yml +6 -1
  12. data/.rubocop_todo.yml +181 -10
  13. data/CHANGELOG.md +20 -8
  14. data/Dangerfile +4 -0
  15. data/Gemfile +18 -7
  16. data/Gemfile.danger +6 -0
  17. data/README.md +255 -18
  18. data/Rakefile +3 -1
  19. data/grape-roar.gemspec +5 -3
  20. data/lib/grape/roar/decorator.rb +2 -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/adapters.rb +22 -0
  25. data/lib/grape/roar/extensions/relations/dsl_methods.rb +86 -0
  26. data/lib/grape/roar/extensions/relations/exceptions.rb +14 -0
  27. data/lib/grape/roar/extensions/relations/mapper.rb +94 -0
  28. data/lib/grape/roar/extensions/relations/validations/active_record.rb +40 -0
  29. data/lib/grape/roar/extensions/relations/validations/misc.rb +18 -0
  30. data/lib/grape/roar/extensions/relations/validations/mongoid/6.rb +48 -0
  31. data/lib/grape/roar/extensions/relations/validations/mongoid/7.rb +75 -0
  32. data/lib/grape/roar/extensions/relations/validations/mongoid.rb +9 -0
  33. data/lib/grape/roar/extensions/relations/validations.rb +5 -0
  34. data/lib/grape/roar/extensions/relations.rb +23 -0
  35. data/lib/grape/roar/extensions.rb +3 -0
  36. data/lib/grape/roar/formatter.rb +2 -0
  37. data/lib/grape/roar/representer.rb +6 -1
  38. data/lib/grape/roar/version.rb +3 -1
  39. data/lib/grape/roar.rb +3 -0
  40. data/lib/grape-roar.rb +2 -0
  41. data/spec/config/mongoid.yml +6 -0
  42. data/spec/decorator_spec.rb +1 -1
  43. data/spec/extensions/relations/adapters/active_record_spec.rb +30 -0
  44. data/spec/extensions/relations/adapters/adapters_module_spec.rb +12 -0
  45. data/spec/extensions/relations/adapters/mongoid_spec.rb +30 -0
  46. data/spec/extensions/relations/dsl_methods_spec.rb +159 -0
  47. data/spec/extensions/relations/mapper_spec.rb +88 -0
  48. data/spec/extensions/relations/validations/active_record_spec.rb +49 -0
  49. data/spec/extensions/relations/validations/mongoid_spec.rb +91 -0
  50. data/spec/nested_representer_spec.rb +3 -14
  51. data/spec/present_with_spec.rb +2 -13
  52. data/spec/relations_spec.rb +77 -0
  53. data/spec/representer_spec.rb +36 -19
  54. data/spec/spec_helper.rb +19 -1
  55. data/spec/support/{article.rb → all/article.rb} +3 -1
  56. data/spec/support/{article_representer.rb → all/article_representer.rb} +3 -0
  57. data/spec/support/all/grape_app_context.rb +16 -0
  58. data/spec/support/{order.rb → all/order.rb} +3 -1
  59. data/spec/support/{order_representer.rb → all/order_representer.rb} +3 -1
  60. data/spec/support/{product.rb → all/product.rb} +2 -0
  61. data/spec/support/{product_representer.rb → all/product_representer.rb} +3 -1
  62. data/spec/support/{user.rb → all/user.rb} +2 -0
  63. data/spec/support/{user_representer.rb → all/user_representer.rb} +2 -0
  64. data/spec/support/mongoid/relational_models/cart.rb +7 -0
  65. data/spec/support/mongoid/relational_models/item.rb +7 -0
  66. data/spec/support/mongoid/relational_models/mongoid_cart_representer.rb +27 -0
  67. data/spec/support/mongoid/relational_models/mongoid_item_representer.rb +13 -0
  68. metadata +68 -17
  69. data/.travis.yml +0 -10
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Roar
5
+ module Extensions
6
+ module Relations
7
+ module Validations
8
+ module ActiveRecord
9
+ include Validations::Misc
10
+
11
+ def belongs_to_valid?(relation)
12
+ _valid_relation? relation, ::ActiveRecord::Reflection::BelongsToReflection
13
+ end
14
+
15
+ def has_many_valid?(relation)
16
+ _valid_relation? relation, ::ActiveRecord::Reflection::HasManyReflection
17
+ end
18
+
19
+ def has_and_belongs_to_many_valid?(relation)
20
+ _valid_relation? relation, ::ActiveRecord::Reflection::HasAndBelongsToManyReflection
21
+ end
22
+
23
+ def has_one_valid?(relation)
24
+ _valid_relation? relation, ::ActiveRecord::Reflection::HasOneReflection
25
+ end
26
+
27
+ private
28
+
29
+ def _valid_relation?(relation, relation_klass)
30
+ relation = klass.reflections[relation]
31
+ return true if relation.is_a?(relation_klass)
32
+
33
+ invalid_relation(relation_klass, relation.class)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Roar
5
+ module Extensions
6
+ module Relations
7
+ module Validations
8
+ module Misc
9
+ def invalid_relation(valid, invalid)
10
+ raise Exceptions::InvalidRelationError,
11
+ "Expected #{valid}, got #{invalid}!"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Roar
5
+ module Extensions
6
+ module Relations
7
+ module Validations
8
+ module Mongoid
9
+ include Validations::Misc
10
+
11
+ def belongs_to_valid?(relation)
12
+ _valid_relation? relation, ::Mongoid::Relations::Referenced::In
13
+ end
14
+
15
+ def embeds_many_valid?(relation)
16
+ _valid_relation? relation, ::Mongoid::Relations::Embedded::Many
17
+ end
18
+
19
+ def embeds_one_valid?(relation)
20
+ _valid_relation? relation, ::Mongoid::Relations::Embedded::One
21
+ end
22
+
23
+ def has_many_valid?(relation)
24
+ _valid_relation? relation, ::Mongoid::Relations::Referenced::Many
25
+ end
26
+
27
+ def has_and_belongs_to_many_valid?(relation)
28
+ _valid_relation? relation, ::Mongoid::Relations::Referenced::ManyToMany
29
+ end
30
+
31
+ def has_one_valid?(relation)
32
+ _valid_relation? relation, ::Mongoid::Relations::Referenced::One
33
+ end
34
+
35
+ private
36
+
37
+ def _valid_relation?(relation, relation_klass)
38
+ relation = klass.reflect_on_association(relation)
39
+ return true if relation[:relation] == relation_klass
40
+
41
+ invalid_relation(relation_klass, relation[:relation])
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Roar
5
+ module Extensions
6
+ module Relations
7
+ module Validations
8
+ module Mongoid
9
+ include Validations::Misc
10
+
11
+ def belongs_to_valid?(relation)
12
+ _valid_relation?(
13
+ relation,
14
+ ::Mongoid::Association::Referenced::BelongsTo,
15
+ ::Mongoid::Association::Referenced::BelongsTo::Proxy
16
+ )
17
+ end
18
+
19
+ def embeds_many_valid?(relation)
20
+ _valid_relation?(
21
+ relation,
22
+ ::Mongoid::Association::Embedded::EmbedsMany,
23
+ ::Mongoid::Association::Embedded::EmbedsMany::Proxy
24
+ )
25
+ end
26
+
27
+ def embeds_one_valid?(relation)
28
+ _valid_relation?(
29
+ relation,
30
+ ::Mongoid::Association::Embedded::EmbedsOne,
31
+ ::Mongoid::Association::Embedded::EmbedsOne::Proxy
32
+ )
33
+ end
34
+
35
+ def has_many_valid?(relation)
36
+ _valid_relation?(
37
+ relation,
38
+ ::Mongoid::Association::Referenced::HasMany,
39
+ ::Mongoid::Association::Referenced::HasMany::Proxy
40
+ )
41
+ end
42
+
43
+ def has_and_belongs_to_many_valid?(relation)
44
+ _valid_relation?(
45
+ relation,
46
+ ::Mongoid::Association::Referenced::HasAndBelongsToMany,
47
+ ::Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy
48
+ )
49
+ end
50
+
51
+ def has_one_valid?(relation)
52
+ _valid_relation?(
53
+ relation,
54
+ ::Mongoid::Association::Referenced::HasOne,
55
+ ::Mongoid::Association::Referenced::HasOne::Proxy
56
+ )
57
+ end
58
+
59
+ private
60
+
61
+ def _valid_relation?(relation, relation_klass, relation_proxy_klass)
62
+ relation = klass.reflect_on_association(relation)
63
+
64
+ related = relation.is_a?(Hash) ? relation[:relation] : relation.relation
65
+
66
+ return true if related == relation_klass || related == relation_proxy_klass
67
+
68
+ invalid_relation(relation.class, related)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Mongoid)
4
+ if Gem::Version.new(Mongoid::VERSION) < Gem::Version.new('7')
5
+ require_relative 'mongoid/6'
6
+ else
7
+ require_relative 'mongoid/7'
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/roar/extensions/relations/validations/misc'
4
+ require 'grape/roar/extensions/relations/validations/active_record'
5
+ require 'grape/roar/extensions/relations/validations/mongoid'
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/roar/extensions/relations/validations'
4
+ require 'grape/roar/extensions/relations/adapters'
5
+ require 'grape/roar/extensions/relations/dsl_methods'
6
+ require 'grape/roar/extensions/relations/exceptions'
7
+ require 'grape/roar/extensions/relations/mapper'
8
+
9
+ module Grape
10
+ module Roar
11
+ module Extensions
12
+ module Relations
13
+ class << self
14
+ def included(other)
15
+ class << other
16
+ include Extensions::Relations::DSLMethods
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/roar/extensions/relations'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Formatter
3
5
  module Roar
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Roar
3
5
  module Representer
@@ -7,7 +9,10 @@ module Grape
7
9
 
8
10
  module ClassMethods
9
11
  def represent(object, _options = {})
10
- object.extend self
12
+ # See https://github.com/ruby-grape/grape-roar/issues/16
13
+ raise TypeError if object.singleton_class == object.class
14
+
15
+ object.extend(self)
11
16
  object
12
17
  end
13
18
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Roar
3
- VERSION = '0.4.0'
5
+ VERSION = '0.5.0'
4
6
  end
5
7
  end
data/lib/grape/roar.rb CHANGED
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/roar/version'
2
4
  require 'grape/roar/formatter'
3
5
  require 'grape/roar/representer'
4
6
  require 'grape/roar/decorator'
7
+ require 'grape/roar/extensions'
data/lib/grape-roar.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'roar'
2
4
  require 'grape'
3
5
  require 'grape/roar'
@@ -0,0 +1,6 @@
1
+ test:
2
+ clients:
3
+ default:
4
+ database: grape_roar_test
5
+ hosts:
6
+ - localhost:27017
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Grape::Roar::Decorator do
4
4
  subject do
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActiveRecord)
4
+ describe Grape::Roar::Extensions::Relations::Adapters::ActiveRecord, :activerecord do
5
+ let(:model) { Class.new(ActiveRecord::Base) }
6
+
7
+ subject { described_class.new(model) }
8
+
9
+ describe '.valid_for?' do
10
+ it 'is only valid for ActiveRecord::Base descendants' do
11
+ expect(described_class.valid_for?(model)).to eql(true)
12
+ expect(described_class.valid_for?('foo')).to eql(false)
13
+ end
14
+ end
15
+
16
+ describe '#collection_methods' do
17
+ it 'returns all collection methods' do
18
+ expect(subject.collection_methods)
19
+ .to match_array(%i[has_many has_and_belongs_to_many])
20
+ end
21
+ end
22
+
23
+ describe '#single_entity_methods' do
24
+ it 'returns all single entity methods' do
25
+ expect(subject.single_entity_methods)
26
+ .to match_array(%i[has_one belongs_to])
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Grape::Roar::Extensions::Relations::Adapters, :activerecord do
4
+ describe '.for' do
5
+ let(:model) { Class.new(ActiveRecord::Base) }
6
+
7
+ it 'looks up the correct adapter for a given class' do
8
+ expect(described_class.for(model))
9
+ .to be_an_instance_of(described_class::ActiveRecord)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Mongoid)
4
+ describe Grape::Roar::Extensions::Relations::Adapters::Mongoid, :mongoid do
5
+ let(:model) { Class.new.tap { |c| c.include(Mongoid::Document) } }
6
+
7
+ subject { described_class.new(model) }
8
+
9
+ describe '.valid_for?' do
10
+ it 'is only valid for classes that mixed in Mongoid::Document' do
11
+ expect(described_class.valid_for?(model)).to eql(true)
12
+ expect(described_class.valid_for?('foo')).to eql(false)
13
+ end
14
+ end
15
+
16
+ describe '#collection_methods' do
17
+ it 'returns all collection methods' do
18
+ expect(subject.collection_methods)
19
+ .to match_array(%i[embeds_many has_many has_and_belongs_to_many])
20
+ end
21
+ end
22
+
23
+ describe '#single_entity_methods' do
24
+ it 'returns all single entity methods' do
25
+ expect(subject.single_entity_methods)
26
+ .to match_array(%i[has_one belongs_to embeds_one])
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Grape::Roar::Extensions::Relations::DSLMethods do
4
+ subject do
5
+ Class.new.tap { |c| c.singleton_class.include(described_class) }
6
+ end
7
+
8
+ describe '#link_relation' do
9
+ let(:relation) { double }
10
+ let(:collection) { false }
11
+
12
+ before do
13
+ allow(subject).to receive(:link)
14
+ allow(subject).to receive(:links)
15
+ end
16
+
17
+ after { subject.link_relation(relation, collection) }
18
+
19
+ it 'calls the correct method' do
20
+ expect(subject).to receive(:link).with(relation)
21
+ end
22
+
23
+ context 'with a collection of objects' do
24
+ let(:collection) { true }
25
+
26
+ it 'uses the links method' do
27
+ expect(subject).to receive(:links).with(relation)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#name_for_represented' do
33
+ let(:represented) { double }
34
+
35
+ after { subject.name_for_represented(represented) }
36
+
37
+ it 'calls the methods correctly' do
38
+ expect(subject).to receive_message_chain(
39
+ :relational_mapper, :adapter, :name_for_represented
40
+ ).with(represented)
41
+ end
42
+ end
43
+
44
+ context 'with relational mapper' do
45
+ let(:relational_mapper) { double }
46
+
47
+ before do
48
+ allow(subject).to receive(:relational_mapper)
49
+ .and_return(relational_mapper)
50
+ end
51
+
52
+ describe '#relation' do
53
+ let(:relation_name) { double }
54
+ let(:relation_kind) { :has_many }
55
+ let(:opts) { {} }
56
+
57
+ after { subject.relation(relation_kind, relation_name, opts) }
58
+
59
+ it 'correctly stores the info in mapper' do
60
+ expect(relational_mapper).to receive(:[]=).with(
61
+ relation_name, opts.merge(relation_kind: relation_kind)
62
+ )
63
+ end
64
+ end
65
+
66
+ describe '#link_self' do
67
+ after { subject.link_self }
68
+
69
+ it 'calls the method correctly' do
70
+ expect(relational_mapper).to receive(:[]=).with(
71
+ :self, { relation_kind: :self }
72
+ )
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#map_base_url' do
78
+ let(:grape_request) do
79
+ OpenStruct.new(base_url: 'foo/', script_name: 'v1')
80
+ end
81
+
82
+ let(:opts) { { env: double } }
83
+
84
+ before do
85
+ allow(Grape::Request).to receive(:new).with(opts[:env])
86
+ .and_return(grape_request)
87
+ end
88
+
89
+ it 'provides a default implementation' do
90
+ expect(subject.map_base_url.call(opts)).to eql('foo/v1')
91
+ end
92
+
93
+ context 'with user provided block' do
94
+ let(:block) { proc {} }
95
+
96
+ it 'returns the user block' do
97
+ subject.map_base_url(&block)
98
+ expect(subject.map_base_url).to eql(block)
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#map_self_url' do
104
+ after { subject.map_self_url }
105
+
106
+ it 'calls the correct method' do
107
+ expect(subject).to receive(:link).with(:self)
108
+ end
109
+ end
110
+
111
+ describe '#map_resource_path' do
112
+ let(:object) { OpenStruct.new(id: 4) }
113
+ let(:opts) { double }
114
+ let(:relation) { 'baz' }
115
+
116
+ it 'provides a default implementation' do
117
+ expect(
118
+ subject.map_resource_path.call(opts, object, relation)
119
+ ).to eql('baz/4')
120
+ end
121
+
122
+ context 'with user provided block' do
123
+ let(:block) { proc {} }
124
+
125
+ it 'returns the user block' do
126
+ subject.map_resource_path(&block)
127
+ expect(subject.map_resource_path).to eql(block)
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#represent' do
133
+ let(:object) { double }
134
+ let(:options) { double }
135
+
136
+ let(:relations_mapped) { false }
137
+
138
+ before do
139
+ expect(subject).to receive(:relations_mapped)
140
+ .and_return(relations_mapped)
141
+ end
142
+
143
+ after { subject.represent(object, options) }
144
+
145
+ it 'maps relations and invoke super' do
146
+ expect(subject).to receive(:map_relations).with(object)
147
+ expect(subject.superclass).to receive(:represent).with(object, options)
148
+ end
149
+
150
+ context 'with relations mapped' do
151
+ let(:relations_mapped) { true }
152
+
153
+ it 'does not map relations and invoke super' do
154
+ expect(subject).not_to receive(:map_relations).with(object)
155
+ expect(subject.superclass).to receive(:represent).with(object, options)
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Grape::Roar::Extensions::Relations::Mapper do
4
+ subject { described_class.new(entity) }
5
+
6
+ let(:entity) { double }
7
+ let(:klass) { double }
8
+
9
+ describe '#initialize' do
10
+ it 'assigns the correct variables' do
11
+ expect(subject.instance_variable_get(:@entity)).to eql(entity)
12
+ expect(subject.instance_variable_get(:@config)).to eql({})
13
+ subject
14
+ end
15
+ end
16
+
17
+ describe '#adapter' do
18
+ let(:klass) { class_double('ActiveRecord::Base') }
19
+
20
+ before do
21
+ subject.instance_variable_set(:@model_klass, klass)
22
+ end
23
+
24
+ after { subject.adapter }
25
+
26
+ it 'calls the correct method' do
27
+ expect(Grape::Roar::Extensions::Relations::Adapters)
28
+ .to receive(:for).with(klass)
29
+ end
30
+ end
31
+
32
+ describe '#decorate' do
33
+ let(:adapter) { double }
34
+ let(:config) do
35
+ { test_single: {
36
+ relation_kind: :belongs_to, embedded: true, misc_opt: 'foo'
37
+ },
38
+ test_collection: {
39
+ relation_kind: :has_many, embedded: true, misc_opt: 'baz'
40
+ } }
41
+ end
42
+
43
+ let(:klass) { double }
44
+
45
+ before do
46
+ allow(subject).to receive(:adapter).and_return(adapter)
47
+
48
+ allow(adapter).to receive(:collection_methods).and_return(%i[has_many])
49
+ allow(adapter).to receive(:single_entity_methods).and_return(%i[belongs_to])
50
+ allow(entity).to receive(:name).and_return('Foo::Bar')
51
+
52
+ subject.instance_variable_set(:@config, config)
53
+ end
54
+
55
+ it 'correctlies decorate the entity' do
56
+ expect(adapter).to receive(:belongs_to_valid?).with(
57
+ 'test_single'
58
+ ).and_return(true)
59
+
60
+ expect(adapter).to receive(:has_many_valid?).with(
61
+ 'test_collection'
62
+ ).and_return(true)
63
+
64
+ expect(entity).to receive(:collection).with(
65
+ :test_collection, config[:test_collection]
66
+ )
67
+
68
+ expect(entity).to receive(:property).with(
69
+ :test_single, config[:test_single]
70
+ )
71
+
72
+ subject.decorate(klass)
73
+ expect(subject.instance_variable_get(:@model_klass)).to eql(klass)
74
+ end
75
+
76
+ context 'with an invalid relation type' do
77
+ let(:config) do
78
+ { test_single: { relation_kind: :has_baz, misc_opt: 'foo' } }
79
+ end
80
+
81
+ it 'raises the correct exception' do
82
+ expect { subject.decorate(klass) }.to raise_error(
83
+ Grape::Roar::Extensions::Relations::Exceptions::UnsupportedRelationError
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(ActiveRecord)
4
+ describe Grape::Roar::Extensions::Relations::Validations::ActiveRecord, :activerecord do
5
+ let(:model_klass) { double }
6
+
7
+ subject do
8
+ klass = Class.new do
9
+ def initialize(klass)
10
+ @klass = klass
11
+ end
12
+
13
+ attr_reader :klass
14
+ end
15
+
16
+ klass.include(described_class)
17
+ klass.new(model_klass)
18
+ end
19
+
20
+ before do
21
+ expect(model_klass).to receive(:reflections).twice
22
+ .and_return(reflections)
23
+ end
24
+
25
+ %w[
26
+ belongs_to
27
+ has_many
28
+ has_one
29
+ has_and_belongs_to_many
30
+ ].each do |relation|
31
+ describe "##{relation}_valid?" do
32
+ let(:relation_klass) do
33
+ "::ActiveRecord::Reflection::#{relation.camelize}" \
34
+ 'Reflection'.constantize.allocate
35
+ end
36
+
37
+ let(:reflections) { { test: relation_klass, fail: Class.new } }
38
+
39
+ it 'properly validates the relation' do
40
+ expect(subject.send("#{relation}_valid?", :test)).to eql(true)
41
+ expect { subject.send("#{relation}_valid?", :fail) }.to raise_error(
42
+ Grape::Roar::Extensions::Relations::
43
+ Exceptions::InvalidRelationError
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end