instant-api 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +142 -0
- data/README.md +231 -0
- data/instant_api.gemspec +20 -0
- data/lib/instant_api.rb +13 -0
- data/lib/instant_api/controller/build_create.rb +34 -0
- data/lib/instant_api/controller/build_destroy.rb +23 -0
- data/lib/instant_api/controller/build_edit.rb +11 -0
- data/lib/instant_api/controller/build_index.rb +39 -0
- data/lib/instant_api/controller/build_new.rb +22 -0
- data/lib/instant_api/controller/build_resource.rb +29 -0
- data/lib/instant_api/controller/build_show.rb +25 -0
- data/lib/instant_api/controller/build_update.rb +46 -0
- data/lib/instant_api/controller/builder.rb +54 -0
- data/lib/instant_api/controller/exception_handler.rb +38 -0
- data/lib/instant_api/controller/parameters.rb +43 -0
- data/lib/instant_api/controller/routes.rb +46 -0
- data/lib/instant_api/model/active_record_query_builder.rb +36 -0
- data/lib/instant_api/model/association_reflector.rb +93 -0
- data/lib/instant_api/model/builder.rb +95 -0
- data/lib/instant_api/model/collection.rb +43 -0
- data/lib/instant_api/model/resource.rb +14 -0
- data/lib/instant_api/util/array.rb +15 -0
- data/lib/instant_api/version.rb +3 -0
- data/spec/dummy/Gemfile +58 -0
- data/spec/dummy/Gemfile.lock +193 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +16 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/a.rb +5 -0
- data/spec/dummy/app/models/address.rb +4 -0
- data/spec/dummy/app/models/b.rb +4 -0
- data/spec/dummy/app/models/c.rb +4 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/country.rb +4 -0
- data/spec/dummy/app/models/d.rb +3 -0
- data/spec/dummy/app/models/movie.rb +4 -0
- data/spec/dummy/app/models/user.rb +8 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +4 -0
- data/spec/dummy/config/database.yml +39 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/instant_api.rb +3 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/migrate/20131019140756_create_users.rb +14 -0
- data/spec/dummy/db/migrate/20131019141942_create_addresses.rb +14 -0
- data/spec/dummy/db/migrate/20131020003152_a.rb +9 -0
- data/spec/dummy/db/migrate/20131020003245_b.rb +10 -0
- data/spec/dummy/db/migrate/20131020003354_c.rb +11 -0
- data/spec/dummy/db/migrate/20131020164202_d.rb +9 -0
- data/spec/dummy/db/migrate/20131020164349_ad.rb +8 -0
- data/spec/dummy/db/migrate/20140419205834_create_countries.rb +9 -0
- data/spec/dummy/db/migrate/20140421005321_create_movies.rb +8 -0
- data/spec/dummy/db/migrate/20140421005435_create_countries_movies.rb +8 -0
- data/spec/dummy/db/schema.rb +88 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/lib/tasks/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/vendor/assets/javascripts/.keep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.keep +0 -0
- data/spec/factories/address_factory.rb +8 -0
- data/spec/factories/country_factory.rb +5 -0
- data/spec/factories/movies_factory.rb +5 -0
- data/spec/factories/user_factory.rb +10 -0
- data/spec/functional/create_spec.rb +16 -0
- data/spec/functional/destroy_spec.rb +21 -0
- data/spec/functional/edit_spec.rb +19 -0
- data/spec/functional/index_spec.rb +82 -0
- data/spec/functional/new_spec.rb +14 -0
- data/spec/functional/show_spec.rb +20 -0
- data/spec/functional/update_spec.rb +47 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/database_cleaner.rb +20 -0
- data/spec/support/helpers.rb +31 -0
- data/spec/unit/lib/instant_api/controller/build_create_spec.rb +38 -0
- data/spec/unit/lib/instant_api/controller/build_destroy_spec.rb +25 -0
- data/spec/unit/lib/instant_api/controller/build_edit_spec.rb +24 -0
- data/spec/unit/lib/instant_api/controller/build_index_spec.rb +49 -0
- data/spec/unit/lib/instant_api/controller/build_new_spec.rb +18 -0
- data/spec/unit/lib/instant_api/controller/build_resource_spec.rb +27 -0
- data/spec/unit/lib/instant_api/controller/build_show_spec.rb +24 -0
- data/spec/unit/lib/instant_api/controller/build_update_spec.rb +66 -0
- data/spec/unit/lib/instant_api/controller/builder_spec.rb +18 -0
- data/spec/unit/lib/instant_api/controller/parameters_spec.rb +53 -0
- data/spec/unit/lib/instant_api/model/active_record_query_builder_spec.rb +133 -0
- data/spec/unit/lib/instant_api/model/builder_spec.rb +237 -0
- data/spec/unit/lib/instant_api/model/join_calculator_spec.rb +27 -0
- metadata +202 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe InstantApi::Controller::BuildNew do
|
|
4
|
+
let(:controller) { Object.new }
|
|
5
|
+
|
|
6
|
+
subject { InstantApi::Controller::BuildNew.new(controller) }
|
|
7
|
+
|
|
8
|
+
describe '#build' do
|
|
9
|
+
before { subject.build }
|
|
10
|
+
|
|
11
|
+
it { controller.respond_to?(:new).should be_true }
|
|
12
|
+
|
|
13
|
+
context 'call to new' do
|
|
14
|
+
before { controller.should_receive(:head).with(:ok).and_return(true) }
|
|
15
|
+
it { controller.new.should be_true }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe InstantApi::Controller::BuildResource do
|
|
4
|
+
let(:controller) { Object.new }
|
|
5
|
+
let(:model_class_name) { 'Aclass' }
|
|
6
|
+
|
|
7
|
+
subject { InstantApi::Controller::BuildResource.new(controller, model_class_name) }
|
|
8
|
+
|
|
9
|
+
describe '#build' do
|
|
10
|
+
before { subject.build }
|
|
11
|
+
|
|
12
|
+
context 'call to resource' do
|
|
13
|
+
let(:resource) { Object.new }
|
|
14
|
+
let(:params) { {id: 3} }
|
|
15
|
+
let(:request) { double(:request, path: '/a/3')}
|
|
16
|
+
class Aclass; end
|
|
17
|
+
|
|
18
|
+
before do
|
|
19
|
+
controller.should_receive(:params).and_return(params)
|
|
20
|
+
controller.should_receive(:request).and_return(request)
|
|
21
|
+
Aclass.should_receive(:find).with(3).and_return(resource)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it { controller.send(:resource).should eq(resource) }
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe InstantApi::Controller::BuildShow do
|
|
4
|
+
let(:controller) { Object.new }
|
|
5
|
+
|
|
6
|
+
subject { InstantApi::Controller::BuildShow.new(controller) }
|
|
7
|
+
|
|
8
|
+
describe '#build' do
|
|
9
|
+
before { subject.build }
|
|
10
|
+
|
|
11
|
+
it { controller.respond_to?(:show).should be_true }
|
|
12
|
+
|
|
13
|
+
context 'call to show' do
|
|
14
|
+
let(:resource) { Object.new }
|
|
15
|
+
|
|
16
|
+
before do
|
|
17
|
+
controller.should_receive(:resource).and_return(resource)
|
|
18
|
+
controller.should_receive(:render).with({json: resource}).and_return(true)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it { controller.show.should be_true }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe InstantApi::Controller::BuildUpdate do
|
|
4
|
+
let(:controller) { Object.new }
|
|
5
|
+
let(:model_class_name) { 'OtherClass' }
|
|
6
|
+
|
|
7
|
+
subject { InstantApi::Controller::BuildUpdate.new(controller, model_class_name) }
|
|
8
|
+
|
|
9
|
+
describe '#build' do
|
|
10
|
+
before { subject.build }
|
|
11
|
+
|
|
12
|
+
it { controller.respond_to?(:update).should be_true }
|
|
13
|
+
|
|
14
|
+
describe '#update' do
|
|
15
|
+
let(:resource) { double('resource', valid?: true, invalid?: false) }
|
|
16
|
+
let(:strong_params) { Object.new }
|
|
17
|
+
|
|
18
|
+
before do
|
|
19
|
+
resource.should_receive(:update_attributes!).with(strong_params)
|
|
20
|
+
|
|
21
|
+
controller.should_receive(:resource).any_number_of_times.and_return(resource)
|
|
22
|
+
controller.should_receive(:check_strong_parameters).and_return(strong_params)
|
|
23
|
+
controller.should_receive(:render).with(json: resource).and_return(true)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it { controller.update.should be_true }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#check_strong_parameters' do
|
|
30
|
+
context 'with strong parameters' do
|
|
31
|
+
let(:params) { Object.new }
|
|
32
|
+
let(:params_require) { Object.new }
|
|
33
|
+
let(:model_class_name) { 'AnotherClass' }
|
|
34
|
+
class AnotherClass
|
|
35
|
+
def self.strong_parameters
|
|
36
|
+
'a'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
before do
|
|
41
|
+
params.should_receive(:require).with(:another_class).and_return(params_require)
|
|
42
|
+
params_require.should_receive(:permit).with('a').and_return(true)
|
|
43
|
+
controller.should_receive(:params).and_return(params)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it { controller.send(:check_strong_parameters).should be_true }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'without strong parameters' do
|
|
50
|
+
let(:params) { Object.new }
|
|
51
|
+
let(:params_require) { Object.new }
|
|
52
|
+
let(:model_class_name) { 'OtherClass' }
|
|
53
|
+
class OtherClass; end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
before do
|
|
57
|
+
params.should_receive(:require).with(:other_class).and_return(params_require)
|
|
58
|
+
params_require.should_receive(:permit!).and_return(true)
|
|
59
|
+
controller.should_receive(:params).and_return(params)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it { controller.send(:check_strong_parameters).should be_true }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'instant_api/controller/builder'
|
|
3
|
+
|
|
4
|
+
describe InstantApi::Controller::Builder do
|
|
5
|
+
subject { InstantApi::Controller::Builder.new('users', [:index, :show]) }
|
|
6
|
+
|
|
7
|
+
describe '#build_class' do
|
|
8
|
+
it do
|
|
9
|
+
controller = subject.build_class
|
|
10
|
+
|
|
11
|
+
instance = controller.new
|
|
12
|
+
instance.respond_to?(:show).should be_true
|
|
13
|
+
instance.respond_to?(:index).should be_true
|
|
14
|
+
instance.respond_to?(:edit).should be_false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'instant_api/controller/parameters'
|
|
3
|
+
|
|
4
|
+
describe InstantApi::Controller::Parameters do
|
|
5
|
+
subject { InstantApi::Controller::Parameters.new(params, request_path) }
|
|
6
|
+
|
|
7
|
+
describe '#[]' do
|
|
8
|
+
let(:request_path) { '/users' }
|
|
9
|
+
|
|
10
|
+
context 'a string key' do
|
|
11
|
+
let(:params) { { 'id' => 3 } }
|
|
12
|
+
it { subject['id'].should eq(3) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'a symbol key' do
|
|
16
|
+
let(:params) { { id: 3 } }
|
|
17
|
+
it { subject[:id].should eq(3) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'remove internal rails params' do
|
|
21
|
+
%w(controller action format _method only_path).each do |param|
|
|
22
|
+
let(:params) { { param => 'hi' } }
|
|
23
|
+
it { subject[param].should be_nil }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '#resources' do
|
|
29
|
+
let(:params) { Hash.new }
|
|
30
|
+
|
|
31
|
+
context 'return the resources' do
|
|
32
|
+
let(:request_path) { '/users/2/addresses' }
|
|
33
|
+
it { subject.resources.should eq([:users, :addresses]) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'ignore trailing /' do
|
|
37
|
+
let(:request_path) { '/users/2/addresses/' }
|
|
38
|
+
it { subject.resources.should eq([:users, :addresses]) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'ignore trailing /' do
|
|
42
|
+
let(:request_path) { '/users/2/addresses/2/ ' }
|
|
43
|
+
it { subject.resources.should eq([:users, :addresses]) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context 'remove the rails methods' do
|
|
47
|
+
%w(new edit).each do |method|
|
|
48
|
+
let(:request_path) { "/users/2/addresses/#{method}" }
|
|
49
|
+
it { subject.resources.should eq([:users, :addresses]) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'instant_api/controller/parameters'
|
|
3
|
+
require 'instant_api/model/builder'
|
|
4
|
+
require 'instant_api/model/active_record_query_builder'
|
|
5
|
+
|
|
6
|
+
describe InstantApi::Model::ActiveRecordQueryBuilder do
|
|
7
|
+
subject { InstantApi::Model::ActiveRecordQueryBuilder.new(model) }
|
|
8
|
+
let(:params) { InstantApi::Controller::Parameters.new(rails_params, request_path) }
|
|
9
|
+
|
|
10
|
+
let(:user) { create(:user) }
|
|
11
|
+
let(:address) { create(:address, user_id: user.id) }
|
|
12
|
+
let(:country) { create(:country, address_id: address.id) }
|
|
13
|
+
|
|
14
|
+
let(:another_user) { create(:user) }
|
|
15
|
+
let(:another_address) { create(:address, user_id: another_user.id) }
|
|
16
|
+
let(:another_country) { create(:country, address_id: another_address.id) }
|
|
17
|
+
|
|
18
|
+
describe '#query' do
|
|
19
|
+
context 'returns all first level model' do
|
|
20
|
+
let(:model) { User }
|
|
21
|
+
let(:rails_params) { Hash.new }
|
|
22
|
+
let(:request_path) { '/users' }
|
|
23
|
+
it { subject.query(params).should eq([user]) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context 'return all second level model' do
|
|
27
|
+
let(:model) { Address }
|
|
28
|
+
let(:rails_params) { { user_id: user.id } }
|
|
29
|
+
let(:request_path) { "/users/#{user.id}/addresses" }
|
|
30
|
+
|
|
31
|
+
it { subject.query(params).should eq([address]) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'additional parameters' do
|
|
35
|
+
let(:model) { Address }
|
|
36
|
+
let(:rails_params) { { user_id: user.id, city: 'Barcelona' } }
|
|
37
|
+
let(:request_path) { "/users/#{user.id}/addresses" }
|
|
38
|
+
let(:address2) { create(:address, user_id: user.id, city: 'Barcelona') }
|
|
39
|
+
let(:address3) { create(:address, user_id: user.id, city: 'Madrid') }
|
|
40
|
+
|
|
41
|
+
it { subject.query(params).should eq([address2]) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'query second level model with an invalid id, returns nothing' do
|
|
45
|
+
let(:model) { Address }
|
|
46
|
+
let(:rails_params) { { user_id: user.id } }
|
|
47
|
+
let(:request_path) { "/users/#{user.id}/addresses/#{another_address.id}" }
|
|
48
|
+
|
|
49
|
+
it { subject.query(params).should be_empty }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context 'return all third level model' do
|
|
53
|
+
let(:model) { Country }
|
|
54
|
+
let(:rails_params) { { user_id: user.id, address_id: address.id } }
|
|
55
|
+
let(:request_path) { "/users/#{user.id}/addresses/#{address.id}/countries" }
|
|
56
|
+
|
|
57
|
+
it { subject.query(params).should eq([country]) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context 'query third level model with an invalid id, returns nothing' do
|
|
61
|
+
let(:model) { Country }
|
|
62
|
+
let(:rails_params) { { user_id: user.id, address_id: another_address.id } }
|
|
63
|
+
let(:request_path) { "/users/#{user.id}/addresses/#{another_address.id}/countries" }
|
|
64
|
+
|
|
65
|
+
it { subject.query(params).should be_empty }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'return all third level model with additional params' do
|
|
69
|
+
let(:model) { Country }
|
|
70
|
+
let(:rails_params) { { user_id: user.id, address_id: address.id } }
|
|
71
|
+
let(:request_path) { "/users/#{user.id}/addresses/#{address.id}/countries" }
|
|
72
|
+
|
|
73
|
+
context 'return data' do
|
|
74
|
+
before { rails_params[:name] = country.name }
|
|
75
|
+
it { subject.query(params).should eq([country]) }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# TODO
|
|
79
|
+
#context 'return nothins' do
|
|
80
|
+
# before { params[:name] = country.name + 'nothing' }
|
|
81
|
+
# it { subject.query(params).should be_empty }
|
|
82
|
+
#end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context 'has_and_belongs_to_many' do
|
|
86
|
+
let(:movie) { create(:movie, countries: [country]) }
|
|
87
|
+
before { country.movies = [movie] }
|
|
88
|
+
|
|
89
|
+
let(:model) { Country }
|
|
90
|
+
let(:rails_params) { { movie_id: movie.id } }
|
|
91
|
+
let(:request_path) { "/movies/#{movie.id}/countries" }
|
|
92
|
+
|
|
93
|
+
it { subject.query(params).should eq([country]) }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe '#find_first' do
|
|
98
|
+
context 'returns first level model' do
|
|
99
|
+
let(:model) { User }
|
|
100
|
+
let(:rails_params) { { id: user.id } }
|
|
101
|
+
let(:request_path) { "/users/#{user.id}" }
|
|
102
|
+
it { subject.find_first(params).should eq(user) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
context 'raises exception if no item exists' do
|
|
106
|
+
let(:model) { User }
|
|
107
|
+
let(:rails_params) { { id: user.id + another_user.id } }
|
|
108
|
+
let(:request_path) { "/users/#{user.id}" }
|
|
109
|
+
it { expect { subject.find_first(params) }.to raise_error(ActiveRecord::RecordNotFound) }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context 'raises exception if no id is passed' do
|
|
113
|
+
let(:model) { User }
|
|
114
|
+
let(:rails_params) { Hash.new }
|
|
115
|
+
let(:request_path) { "/users/#{user.id}" }
|
|
116
|
+
it { expect { subject.find_first(params) }.to raise_error(ActiveRecord::RecordNotFound) }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
context 'returns second level model' do
|
|
120
|
+
let(:model) { Address }
|
|
121
|
+
let(:rails_params) { { user_id: user.id, id: address.id } }
|
|
122
|
+
let(:request_path) { "/users/#{user.id}/addresses/#{address.id}" }
|
|
123
|
+
it { subject.find_first(params).should eq(address) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context 'second level model with invalid id, raises exception' do
|
|
127
|
+
let(:model) { Address }
|
|
128
|
+
let(:rails_params) { { user_id: user.id, id: another_address.id } }
|
|
129
|
+
let(:request_path) { "/users/#{user.id}/addresses/#{another_address.id}" }
|
|
130
|
+
it { expect { subject.find_first(params) }.to raise_error(ActiveRecord::RecordNotFound) }
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'instant_api/model/builder'
|
|
3
|
+
|
|
4
|
+
describe InstantApi::Model::Builder do
|
|
5
|
+
subject { InstantApi::Model::Builder.new(params, model, true) }
|
|
6
|
+
|
|
7
|
+
describe '#build' do
|
|
8
|
+
let(:model) { User }
|
|
9
|
+
let(:params) { { user: data }.with_indifferent_access }
|
|
10
|
+
|
|
11
|
+
context 'successful' do
|
|
12
|
+
context 'string parameter' do
|
|
13
|
+
let(:data) { { email: 'a' } }
|
|
14
|
+
it { validate_record(subject.build, :email, 'a') }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context 'integer parameter' do
|
|
18
|
+
let(:data) { { age: 1 } }
|
|
19
|
+
it { validate_record(subject.build, :age, 1) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'date parameter' do
|
|
23
|
+
let(:data) { { born_at: '01/03/1974' } }
|
|
24
|
+
let(:born_at) { Date.parse(data[:born_at]) }
|
|
25
|
+
it { validate_record(subject.build, :born_at, born_at) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context 'datetime parameter' do
|
|
29
|
+
let(:data) { { registered_at: '10:29 01/03/1974' } }
|
|
30
|
+
let(:registered_at) { DateTime.parse(data[:registered_at]) }
|
|
31
|
+
it { validate_record(subject.build, :registered_at, registered_at) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'decimal parameter' do
|
|
35
|
+
let(:data) { { money: '10.99' } }
|
|
36
|
+
let(:money) { BigDecimal.new('10.99') }
|
|
37
|
+
it { validate_record(subject.build, :money, money) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
context 'boolean parameter' do
|
|
41
|
+
context 'value true' do
|
|
42
|
+
let(:data) { { terms_accepted: 1 } }
|
|
43
|
+
it { validate_record(subject.build, :terms_accepted, true) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context 'value false' do
|
|
47
|
+
let(:data) { { terms_accepted: 0 } }
|
|
48
|
+
it { validate_record(subject.build, :terms_accepted, false) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context 'build nested model' do
|
|
53
|
+
let(:data) { { addresses: { street: 'a' } }.with_indifferent_access }
|
|
54
|
+
|
|
55
|
+
it 'creates a record' do
|
|
56
|
+
record = subject.build
|
|
57
|
+
record.valid?.should be_true
|
|
58
|
+
record.addresses.size.should eq(1)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'nested objects' do
|
|
64
|
+
let(:model) { A }
|
|
65
|
+
let(:params) do
|
|
66
|
+
{ a: { value: 'a', b: { value: 'b', c: { value: 'c' } } } }.with_indifferent_access
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'successful' do
|
|
70
|
+
it 'build the nested objects' do
|
|
71
|
+
record = subject.build
|
|
72
|
+
|
|
73
|
+
record.value.should eq('a')
|
|
74
|
+
record.b.size.should eq(1)
|
|
75
|
+
record.b.first.value.should eq('b')
|
|
76
|
+
record.b.first.c.size.should eq(1)
|
|
77
|
+
record.b.first.c.first.value.should eq('c')
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'has_one' do
|
|
83
|
+
let(:model) { A }
|
|
84
|
+
let(:params) do
|
|
85
|
+
{ a: { value: 'a', c: { value: 'c' } } }.with_indifferent_access
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'build the nested objects' do
|
|
89
|
+
record = subject.build
|
|
90
|
+
|
|
91
|
+
record.valid?.should be_true
|
|
92
|
+
record.value.should eq('a')
|
|
93
|
+
record.c.value.should eq('c')
|
|
94
|
+
record.c.a.value.should eq('a')
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'has_many' do
|
|
99
|
+
let(:model) { A }
|
|
100
|
+
let(:params) do
|
|
101
|
+
{ a: { value: 'a', b: { value: 'b' } } }.with_indifferent_access
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'build the nested objects' do
|
|
105
|
+
record = subject.build
|
|
106
|
+
|
|
107
|
+
record.valid?.should be_true
|
|
108
|
+
record.value.should eq('a')
|
|
109
|
+
record.b.map(&:value).to_a.should eq(['b'])
|
|
110
|
+
record.b.first.a.should eq(record)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context 'belongs_to' do
|
|
115
|
+
let(:model) { B }
|
|
116
|
+
let(:params) do
|
|
117
|
+
{ b: { value: 'b', a: { value: 'a' } } }.with_indifferent_access
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'build the nested objects' do
|
|
121
|
+
record = subject.build
|
|
122
|
+
|
|
123
|
+
record.valid?.should be_true
|
|
124
|
+
record.value.should eq('b')
|
|
125
|
+
record.a.value.should eq('a')
|
|
126
|
+
record.a.b.to_a.should eq([record])
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context 'has_and_belongs_to_many' do
|
|
131
|
+
let(:model) { A }
|
|
132
|
+
let(:params) do
|
|
133
|
+
{ a: { value: 'a', d: { value: 'd' } } }.with_indifferent_access
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'build the nested objects' do
|
|
137
|
+
record = subject.build
|
|
138
|
+
|
|
139
|
+
record.valid?.should be_true
|
|
140
|
+
record.value.should eq('a')
|
|
141
|
+
record.d.map(&:value).to_a.should eq(['d'])
|
|
142
|
+
record.d.first.a.to_a.should eq([record])
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context 'failed' do
|
|
147
|
+
let(:data) { { email: 'a' } }
|
|
148
|
+
after { User.reset_callbacks(:validate) }
|
|
149
|
+
|
|
150
|
+
context 'mandatory parameter' do
|
|
151
|
+
let(:data) { Hash.new }
|
|
152
|
+
before { User.class_eval { validates_presence_of :email } }
|
|
153
|
+
|
|
154
|
+
it {
|
|
155
|
+
begin
|
|
156
|
+
validate_error(subject.build, :email, "can't be blank")
|
|
157
|
+
rescue
|
|
158
|
+
puts $!.message
|
|
159
|
+
puts $!.backtrace
|
|
160
|
+
|
|
161
|
+
end
|
|
162
|
+
}
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
context 'length validations' do
|
|
166
|
+
before { User.class_eval { validates :email, length: { minimum: 2 } } }
|
|
167
|
+
|
|
168
|
+
it { validate_error(subject.build, :email, 'is too short (minimum is 2 characters)') }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context 'acceptance validation' do
|
|
172
|
+
before { User.class_eval { validates_acceptance_of :terms, allow_nil: false } }
|
|
173
|
+
|
|
174
|
+
it { validate_error(subject.build, :terms, 'must be accepted') }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context 'confirmation validation' do
|
|
178
|
+
let(:data) { { email: 'a', email_confirmation: 'b' } }
|
|
179
|
+
before { User.class_eval { validates :email, confirmation: true } }
|
|
180
|
+
|
|
181
|
+
it { validate_error(subject.build, :email_confirmation, "doesn't match Email") }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context 'exclusion validation' do
|
|
185
|
+
before { User.class_eval { validates :email, exclusion: { in: %w(a b) } } }
|
|
186
|
+
|
|
187
|
+
it { validate_error(subject.build, :email, 'is reserved') }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
context 'format validation' do
|
|
191
|
+
before { User.class_eval { validates :email, format: { with: /[0-9]/ } } }
|
|
192
|
+
|
|
193
|
+
it { validate_error(subject.build, :email, 'is invalid') }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
context 'inclusion validation' do
|
|
197
|
+
before { User.class_eval { validates :email, inclusion: { in: %w(b c) } } }
|
|
198
|
+
|
|
199
|
+
it { validate_error(subject.build, :email, 'is not included in the list') }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
context 'numericality validation' do
|
|
203
|
+
before { User.class_eval { validates :email, numericality: true } }
|
|
204
|
+
|
|
205
|
+
it { validate_error(subject.build, :email, 'is not a number') }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context 'absence validation' do
|
|
209
|
+
before { User.class_eval { validates :email, absence: true } }
|
|
210
|
+
|
|
211
|
+
it { validate_error(subject.build, :email, 'must be blank') }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context 'uniqueness validation' do
|
|
215
|
+
before do
|
|
216
|
+
User.create!(data)
|
|
217
|
+
User.class_eval { validates :email, uniqueness: true }
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it { validate_error(subject.build, :email, 'has already been taken', 1) }
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
private
|
|
225
|
+
def validate_error(record, field, msg, count = 0)
|
|
226
|
+
record.valid?.should be_false
|
|
227
|
+
record.errors.size.should eq(1)
|
|
228
|
+
record.errors[field].should eq([msg])
|
|
229
|
+
record.class.count.should eq(count)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def validate_record(record, field, value)
|
|
233
|
+
record.valid?.should be_true
|
|
234
|
+
record.send(field).should eq(value)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|