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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +6 -2
- data/.rubocop_todo.yml +38 -9
- data/.travis.yml +29 -3
- data/Appraisals +9 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +6 -3
- data/README.md +218 -0
- data/Rakefile +3 -1
- data/gemfiles/with_activerecord.gemfile +22 -0
- data/gemfiles/with_mongoid.gemfile +22 -0
- data/grape-roar.gemspec +3 -0
- data/lib/grape-roar.rb +2 -0
- data/lib/grape/roar.rb +3 -0
- data/lib/grape/roar/decorator.rb +2 -0
- data/lib/grape/roar/extensions.rb +3 -0
- data/lib/grape/roar/extensions/relations.rb +23 -0
- data/lib/grape/roar/extensions/relations/adapters.rb +22 -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/dsl_methods.rb +86 -0
- data/lib/grape/roar/extensions/relations/exceptions.rb +14 -0
- data/lib/grape/roar/extensions/relations/mapper.rb +89 -0
- data/lib/grape/roar/extensions/relations/validations.rb +5 -0
- data/lib/grape/roar/extensions/relations/validations/active_record.rb +67 -0
- data/lib/grape/roar/extensions/relations/validations/misc.rb +18 -0
- data/lib/grape/roar/extensions/relations/validations/mongoid.rb +88 -0
- data/lib/grape/roar/formatter.rb +2 -0
- data/lib/grape/roar/representer.rb +2 -0
- data/lib/grape/roar/version.rb +3 -1
- data/spec/config/mongoid.yml +6 -0
- data/spec/decorator_spec.rb +3 -1
- data/spec/extensions/relations/adapters/active_record_spec.rb +26 -0
- data/spec/extensions/relations/adapters/adapters_module_spec.rb +11 -0
- data/spec/extensions/relations/adapters/mongoid_spec.rb +26 -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 +46 -0
- data/spec/extensions/relations/validations/mongoid_spec.rb +88 -0
- data/spec/nested_representer_spec.rb +4 -13
- data/spec/present_with_spec.rb +3 -12
- data/spec/relations_spec.rb +76 -0
- data/spec/representer_spec.rb +3 -12
- data/spec/spec_helper.rb +15 -1
- data/spec/support/{article.rb → all/article.rb} +3 -1
- data/spec/support/{article_representer.rb → all/article_representer.rb} +2 -0
- data/spec/support/all/grape_app_context.rb +18 -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 +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
|
-
|
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)
|
data/spec/present_with_spec.rb
CHANGED
@@ -1,18 +1,9 @@
|
|
1
|
-
|
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
|
-
|
14
|
-
|
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
|
data/spec/representer_spec.rb
CHANGED
@@ -1,18 +1,9 @@
|
|
1
|
-
|
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
|
-
|
14
|
-
|
15
|
-
end
|
5
|
+
describe Grape::Roar do
|
6
|
+
include_context 'Grape API App'
|
16
7
|
|
17
8
|
context 'representer' do
|
18
9
|
before do
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
@@ -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,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
|
-
|
15
|
+
request.url.to_s
|
14
16
|
end
|
15
17
|
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
|