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.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +2 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/danger.yml +22 -0
- data/.github/workflows/lint.yml +15 -0
- data/.github/workflows/test-activerecord.yml +34 -0
- data/.github/workflows/test-mongodb.yml +33 -0
- data/.github/workflows/test.yml +19 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +6 -1
- data/.rubocop_todo.yml +181 -10
- data/CHANGELOG.md +20 -8
- data/Dangerfile +4 -0
- data/Gemfile +18 -7
- data/Gemfile.danger +6 -0
- data/README.md +255 -18
- data/Rakefile +3 -1
- data/grape-roar.gemspec +5 -3
- data/lib/grape/roar/decorator.rb +2 -0
- data/lib/grape/roar/extensions/relations/adapters/active_record.rb +35 -0
- data/lib/grape/roar/extensions/relations/adapters/base.rb +49 -0
- data/lib/grape/roar/extensions/relations/adapters/mongoid.rb +38 -0
- data/lib/grape/roar/extensions/relations/adapters.rb +22 -0
- data/lib/grape/roar/extensions/relations/dsl_methods.rb +86 -0
- data/lib/grape/roar/extensions/relations/exceptions.rb +14 -0
- data/lib/grape/roar/extensions/relations/mapper.rb +94 -0
- data/lib/grape/roar/extensions/relations/validations/active_record.rb +40 -0
- data/lib/grape/roar/extensions/relations/validations/misc.rb +18 -0
- data/lib/grape/roar/extensions/relations/validations/mongoid/6.rb +48 -0
- data/lib/grape/roar/extensions/relations/validations/mongoid/7.rb +75 -0
- data/lib/grape/roar/extensions/relations/validations/mongoid.rb +9 -0
- data/lib/grape/roar/extensions/relations/validations.rb +5 -0
- data/lib/grape/roar/extensions/relations.rb +23 -0
- data/lib/grape/roar/extensions.rb +3 -0
- data/lib/grape/roar/formatter.rb +2 -0
- data/lib/grape/roar/representer.rb +6 -1
- data/lib/grape/roar/version.rb +3 -1
- data/lib/grape/roar.rb +3 -0
- data/lib/grape-roar.rb +2 -0
- data/spec/config/mongoid.yml +6 -0
- data/spec/decorator_spec.rb +1 -1
- data/spec/extensions/relations/adapters/active_record_spec.rb +30 -0
- data/spec/extensions/relations/adapters/adapters_module_spec.rb +12 -0
- data/spec/extensions/relations/adapters/mongoid_spec.rb +30 -0
- data/spec/extensions/relations/dsl_methods_spec.rb +159 -0
- data/spec/extensions/relations/mapper_spec.rb +88 -0
- data/spec/extensions/relations/validations/active_record_spec.rb +49 -0
- data/spec/extensions/relations/validations/mongoid_spec.rb +91 -0
- data/spec/nested_representer_spec.rb +3 -14
- data/spec/present_with_spec.rb +2 -13
- data/spec/relations_spec.rb +77 -0
- data/spec/representer_spec.rb +36 -19
- data/spec/spec_helper.rb +19 -1
- data/spec/support/{article.rb → all/article.rb} +3 -1
- data/spec/support/{article_representer.rb → all/article_representer.rb} +3 -0
- data/spec/support/all/grape_app_context.rb +16 -0
- data/spec/support/{order.rb → all/order.rb} +3 -1
- data/spec/support/{order_representer.rb → all/order_representer.rb} +3 -1
- data/spec/support/{product.rb → all/product.rb} +2 -0
- data/spec/support/{product_representer.rb → all/product_representer.rb} +3 -1
- data/spec/support/{user.rb → all/user.rb} +2 -0
- data/spec/support/{user_representer.rb → all/user_representer.rb} +2 -0
- data/spec/support/mongoid/relational_models/cart.rb +7 -0
- data/spec/support/mongoid/relational_models/item.rb +7 -0
- data/spec/support/mongoid/relational_models/mongoid_cart_representer.rb +27 -0
- data/spec/support/mongoid/relational_models/mongoid_item_representer.rb +13 -0
- metadata +68 -17
- 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,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
|
data/lib/grape/roar/formatter.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/grape/roar/version.rb
CHANGED
data/lib/grape/roar.rb
CHANGED
data/lib/grape-roar.rb
CHANGED
data/spec/decorator_spec.rb
CHANGED
@@ -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
|