instant-api 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|