instant-api 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +14 -0
  4. data/Gemfile.lock +142 -0
  5. data/README.md +231 -0
  6. data/instant_api.gemspec +20 -0
  7. data/lib/instant_api.rb +13 -0
  8. data/lib/instant_api/controller/build_create.rb +34 -0
  9. data/lib/instant_api/controller/build_destroy.rb +23 -0
  10. data/lib/instant_api/controller/build_edit.rb +11 -0
  11. data/lib/instant_api/controller/build_index.rb +39 -0
  12. data/lib/instant_api/controller/build_new.rb +22 -0
  13. data/lib/instant_api/controller/build_resource.rb +29 -0
  14. data/lib/instant_api/controller/build_show.rb +25 -0
  15. data/lib/instant_api/controller/build_update.rb +46 -0
  16. data/lib/instant_api/controller/builder.rb +54 -0
  17. data/lib/instant_api/controller/exception_handler.rb +38 -0
  18. data/lib/instant_api/controller/parameters.rb +43 -0
  19. data/lib/instant_api/controller/routes.rb +46 -0
  20. data/lib/instant_api/model/active_record_query_builder.rb +36 -0
  21. data/lib/instant_api/model/association_reflector.rb +93 -0
  22. data/lib/instant_api/model/builder.rb +95 -0
  23. data/lib/instant_api/model/collection.rb +43 -0
  24. data/lib/instant_api/model/resource.rb +14 -0
  25. data/lib/instant_api/util/array.rb +15 -0
  26. data/lib/instant_api/version.rb +3 -0
  27. data/spec/dummy/Gemfile +58 -0
  28. data/spec/dummy/Gemfile.lock +193 -0
  29. data/spec/dummy/Rakefile +6 -0
  30. data/spec/dummy/app/assets/images/.keep +0 -0
  31. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  32. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  33. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  34. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  35. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  36. data/spec/dummy/app/mailers/.keep +0 -0
  37. data/spec/dummy/app/models/.keep +0 -0
  38. data/spec/dummy/app/models/a.rb +5 -0
  39. data/spec/dummy/app/models/address.rb +4 -0
  40. data/spec/dummy/app/models/b.rb +4 -0
  41. data/spec/dummy/app/models/c.rb +4 -0
  42. data/spec/dummy/app/models/concerns/.keep +0 -0
  43. data/spec/dummy/app/models/country.rb +4 -0
  44. data/spec/dummy/app/models/d.rb +3 -0
  45. data/spec/dummy/app/models/movie.rb +4 -0
  46. data/spec/dummy/app/models/user.rb +8 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/bin/bundle +3 -0
  49. data/spec/dummy/bin/rails +4 -0
  50. data/spec/dummy/bin/rake +4 -0
  51. data/spec/dummy/config.ru +4 -0
  52. data/spec/dummy/config/application.rb +23 -0
  53. data/spec/dummy/config/boot.rb +4 -0
  54. data/spec/dummy/config/database.yml +39 -0
  55. data/spec/dummy/config/environment.rb +5 -0
  56. data/spec/dummy/config/environments/development.rb +29 -0
  57. data/spec/dummy/config/environments/production.rb +80 -0
  58. data/spec/dummy/config/environments/test.rb +36 -0
  59. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  60. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  61. data/spec/dummy/config/initializers/inflections.rb +16 -0
  62. data/spec/dummy/config/initializers/instant_api.rb +3 -0
  63. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  64. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  65. data/spec/dummy/config/initializers/session_store.rb +3 -0
  66. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  67. data/spec/dummy/config/locales/en.yml +23 -0
  68. data/spec/dummy/config/routes.rb +3 -0
  69. data/spec/dummy/db/migrate/20131019140756_create_users.rb +14 -0
  70. data/spec/dummy/db/migrate/20131019141942_create_addresses.rb +14 -0
  71. data/spec/dummy/db/migrate/20131020003152_a.rb +9 -0
  72. data/spec/dummy/db/migrate/20131020003245_b.rb +10 -0
  73. data/spec/dummy/db/migrate/20131020003354_c.rb +11 -0
  74. data/spec/dummy/db/migrate/20131020164202_d.rb +9 -0
  75. data/spec/dummy/db/migrate/20131020164349_ad.rb +8 -0
  76. data/spec/dummy/db/migrate/20140419205834_create_countries.rb +9 -0
  77. data/spec/dummy/db/migrate/20140421005321_create_movies.rb +8 -0
  78. data/spec/dummy/db/migrate/20140421005435_create_countries_movies.rb +8 -0
  79. data/spec/dummy/db/schema.rb +88 -0
  80. data/spec/dummy/db/seeds.rb +7 -0
  81. data/spec/dummy/lib/assets/.keep +0 -0
  82. data/spec/dummy/lib/tasks/.keep +0 -0
  83. data/spec/dummy/log/.keep +0 -0
  84. data/spec/dummy/public/404.html +58 -0
  85. data/spec/dummy/public/422.html +58 -0
  86. data/spec/dummy/public/500.html +57 -0
  87. data/spec/dummy/public/favicon.ico +0 -0
  88. data/spec/dummy/public/robots.txt +5 -0
  89. data/spec/dummy/vendor/assets/javascripts/.keep +0 -0
  90. data/spec/dummy/vendor/assets/stylesheets/.keep +0 -0
  91. data/spec/factories/address_factory.rb +8 -0
  92. data/spec/factories/country_factory.rb +5 -0
  93. data/spec/factories/movies_factory.rb +5 -0
  94. data/spec/factories/user_factory.rb +10 -0
  95. data/spec/functional/create_spec.rb +16 -0
  96. data/spec/functional/destroy_spec.rb +21 -0
  97. data/spec/functional/edit_spec.rb +19 -0
  98. data/spec/functional/index_spec.rb +82 -0
  99. data/spec/functional/new_spec.rb +14 -0
  100. data/spec/functional/show_spec.rb +20 -0
  101. data/spec/functional/update_spec.rb +47 -0
  102. data/spec/spec_helper.rb +34 -0
  103. data/spec/support/database_cleaner.rb +20 -0
  104. data/spec/support/helpers.rb +31 -0
  105. data/spec/unit/lib/instant_api/controller/build_create_spec.rb +38 -0
  106. data/spec/unit/lib/instant_api/controller/build_destroy_spec.rb +25 -0
  107. data/spec/unit/lib/instant_api/controller/build_edit_spec.rb +24 -0
  108. data/spec/unit/lib/instant_api/controller/build_index_spec.rb +49 -0
  109. data/spec/unit/lib/instant_api/controller/build_new_spec.rb +18 -0
  110. data/spec/unit/lib/instant_api/controller/build_resource_spec.rb +27 -0
  111. data/spec/unit/lib/instant_api/controller/build_show_spec.rb +24 -0
  112. data/spec/unit/lib/instant_api/controller/build_update_spec.rb +66 -0
  113. data/spec/unit/lib/instant_api/controller/builder_spec.rb +18 -0
  114. data/spec/unit/lib/instant_api/controller/parameters_spec.rb +53 -0
  115. data/spec/unit/lib/instant_api/model/active_record_query_builder_spec.rb +133 -0
  116. data/spec/unit/lib/instant_api/model/builder_spec.rb +237 -0
  117. data/spec/unit/lib/instant_api/model/join_calculator_spec.rb +27 -0
  118. metadata +202 -0
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Create', type: :controller do
4
+ let(:user_params) { attributes_for(:user) }
5
+
6
+ before do
7
+ @controller = InstantApi::Controller::Builder.new('users', [:create]).build_class.new
8
+ end
9
+
10
+ it 'successful' do
11
+ post(:create, user: user_params)
12
+
13
+ validate_user(JSON.parse(response.body).symbolize_keys, user_params)
14
+ end
15
+
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Destroy', type: :controller do
4
+ let!(:user) { create(:user) }
5
+
6
+ before do
7
+ @controller = InstantApi::Controller::Builder.new('users', [:destroy]).build_class.new
8
+ end
9
+
10
+ it 'successful' do
11
+ delete(:destroy, id: user.id)
12
+
13
+ response.should be_successful
14
+ User.where(id: user.id).should be_empty
15
+ end
16
+
17
+ it 'returns 404 when the resource does not exists' do
18
+ delete(:destroy, id: user.id + 1)
19
+ response.status.should eq(404)
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Edit', type: :controller do
4
+ let!(:user) { create(:user) }
5
+
6
+ before do
7
+ @controller = InstantApi::Controller::Builder.new('users', [:edit]).build_class.new
8
+ end
9
+
10
+ it 'successful' do
11
+ get(:edit, id: user.id)
12
+ response.should be_successful
13
+ end
14
+
15
+ it 'returns 404 when the resource does not exists' do
16
+ get(:edit, id: user.id + 1)
17
+ response.status.should eq(404)
18
+ end
19
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Index', type: :controller do
4
+ before do
5
+ @controller = InstantApi::Controller::Builder.new('users', [:index]).build_class.new
6
+ end
7
+
8
+ context 'returns empty data' do
9
+ it 'successful' do
10
+ get(:index)
11
+
12
+ json = JSON.parse(response.body).symbolize_keys
13
+ validate_pagination(json[:pagination], 0, 1, 10)
14
+ json[:collection].should be_empty
15
+ end
16
+ end
17
+
18
+ context 'pagination' do
19
+ let!(:user1) { create(:user) }
20
+ let!(:user2) { create(:user) }
21
+
22
+ context 'all in same page' do
23
+ let(:expected_collection) { [user1, user2] }
24
+
25
+ it 'return first user' do
26
+ get(:index)
27
+
28
+ json = JSON.parse(response.body).symbolize_keys
29
+ validate_pagination(json[:pagination], 2, 1, 10)
30
+ validate_user_collection(json[:collection], expected_collection)
31
+ end
32
+ end
33
+
34
+ context 'first page' do
35
+ let(:expected_collection) { [user1] }
36
+
37
+ it 'return first user' do
38
+ get(:index, page: 1, per_page: 1)
39
+
40
+ json = JSON.parse(response.body).symbolize_keys
41
+ validate_pagination(json[:pagination], 2, 1, 1)
42
+ validate_user_collection(json[:collection], expected_collection)
43
+ end
44
+ end
45
+
46
+ context 'second page' do
47
+ let(:expected_collection) { [user2] }
48
+
49
+ it 'record second user' do
50
+ get(:index, page: 2, per_page: 1)
51
+
52
+ json = JSON.parse(response.body).symbolize_keys
53
+ validate_pagination(json[:pagination], 2, 2, 1)
54
+ validate_user_collection(json[:collection], expected_collection)
55
+ end
56
+ end
57
+
58
+ context 'a page that does not exists' do
59
+ let(:expected_collection) { [] }
60
+
61
+ it 'returns nothing' do
62
+ get(:index, page: 2)
63
+
64
+ json = JSON.parse(response.body).symbolize_keys
65
+ validate_pagination(json[:pagination], 2, 2, 10)
66
+ validate_user_collection(json[:collection], expected_collection)
67
+ end
68
+ end
69
+
70
+ context 'invalid page number' do
71
+ let(:expected_collection) { [user1, user2] }
72
+
73
+ it 'returns first page' do
74
+ get(:index, page: 'a')
75
+
76
+ json = JSON.parse(response.body).symbolize_keys
77
+ validate_pagination(json[:pagination], 2, 1, 10)
78
+ validate_user_collection(json[:collection], expected_collection)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'New', type: :controller do
4
+ let!(:user) { create(:user) }
5
+
6
+ before do
7
+ @controller = InstantApi::Controller::Builder.new('users', [:new]).build_class.new
8
+ end
9
+
10
+ it 'successful' do
11
+ get(:new)
12
+ response.should be_successful
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Show', type: :controller do
4
+ let(:user) { create(:user) }
5
+
6
+ before do
7
+ @controller = InstantApi::Controller::Builder.new('users', [:show]).build_class.new
8
+ end
9
+
10
+ it 'successful' do
11
+ get(:show, id: user.id)
12
+
13
+ validate_user(JSON.parse(response.body).symbolize_keys, user)
14
+ end
15
+
16
+ it 'returns 404 when the resource does not exists' do
17
+ get(:show, id: user.id + 1)
18
+ response.status.should eq(404)
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Update', type: :controller do
4
+ let(:user) { create(:user) }
5
+
6
+ before do
7
+ @controller = InstantApi::Controller::Builder.new('users', [:update]).build_class.new
8
+ end
9
+
10
+ it 'successful' do
11
+ put(:update, id: user.id, user: {email: 'hello@at.com'})
12
+
13
+ user[:email] = 'hello@at.com'
14
+ validate_user(JSON.parse(response.body).symbolize_keys, user)
15
+ response.should be_successful
16
+ end
17
+
18
+ it 'returns 404 when the resource does not exists' do
19
+ put(:update, id: user.id + 1, user: {email: 'hello@at.com'})
20
+ response.status.should eq(404)
21
+ end
22
+
23
+ context 'returns error' do
24
+ after { User.reset_callbacks(:validate) }
25
+
26
+ before { User.class_eval { validates :email, length: { minimum: 2 } } }
27
+
28
+ it 'error in email' do
29
+ put(:update, id: user.id, user: {email: 'a'})
30
+ validate_errors(response.body,
31
+ [['email', ['is too short (minimum is 2 characters)']]])
32
+ end
33
+
34
+ context 'returns several errors' do
35
+ before do
36
+ User.class_eval { validates :age, numericality: { greater_than: 10 } }
37
+ end
38
+
39
+ it 'error in email and age' do
40
+ put(:update, id: user.id, user: {email: 'a', age: 3})
41
+ validate_errors(response.body,
42
+ [['email', ['is too short (minimum is 2 characters)']],
43
+ ['age', ['must be greater than 10']]])
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,34 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+ require File.expand_path('../dummy/config/environment', __FILE__)
3
+
4
+ require 'rspec'
5
+ require 'rspec/rails'
6
+ require 'ffaker'
7
+ require 'factory_girl'
8
+ require 'webmock/rspec'
9
+ require 'instant_api'
10
+
11
+ require 'support/database_cleaner'
12
+ require 'support/helpers'
13
+
14
+ ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
15
+
16
+ FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
17
+ FactoryGirl.find_definitions
18
+
19
+ RSpec.configure do |config|
20
+ # If true, the base class of anonymous controllers will be inferred
21
+ # automatically. This will be the default behavior in future versions of
22
+ # rspec-rails.
23
+ config.infer_base_class_for_anonymous_controllers = false
24
+ config.render_views = true
25
+
26
+ config.include RSpec::Rails::ControllerExampleGroup,
27
+ type: :controller,
28
+ example_group: {
29
+ file_path: config.escaped_path(%w[spec unit controllers])
30
+ }
31
+
32
+ config.include(FactoryGirl::Syntax::Methods)
33
+ config.include(Helpers)
34
+ end
@@ -0,0 +1,20 @@
1
+ RSpec.configure do |config|
2
+ # disable rspec-rails wrapping of tests in a database transaction
3
+ config.use_transactional_fixtures = false
4
+
5
+ config.before(:suite) do
6
+ DatabaseCleaner.clean_with(:truncation)
7
+ end
8
+
9
+ config.before(:each) do
10
+ DatabaseCleaner.strategy = :transaction
11
+ end
12
+
13
+ config.before(:each) do
14
+ DatabaseCleaner.start
15
+ end
16
+
17
+ config.after(:each) do
18
+ DatabaseCleaner.clean
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module Helpers
2
+ def validate_user(user, expected_user)
3
+ user[:id].should_not be_nil
4
+ user[:age].should eq(expected_user[:age])
5
+ Date.parse(user[:born_at]).should eq(expected_user[:born_at].to_date)
6
+ DateTime.parse(user[:registered_at]).to_i.should eq(expected_user[:registered_at].to_i)
7
+ user[:terms_accepted].should eq(expected_user[:terms_accepted])
8
+ user[:email].should eq(expected_user[:email])
9
+ BigDecimal.new(user[:money]).should eq(expected_user[:money])
10
+ user[:created_at].should_not be_nil
11
+ user[:updated_at].should_not be_nil
12
+ end
13
+
14
+ def validate_pagination(pagination, count, page, per_page)
15
+ pagination = pagination.symbolize_keys
16
+ pagination[:count].should eq(count)
17
+ pagination[:page].should eq(page)
18
+ pagination[:per_page].should eq(per_page)
19
+ end
20
+
21
+ def validate_user_collection(collection, expected_collection)
22
+ collection.each_with_index do |user, index|
23
+ validate_user(user.symbolize_keys, expected_collection[index])
24
+ end
25
+ end
26
+
27
+ def validate_errors(body, expected_errors)
28
+ json = JSON.parse(body).symbolize_keys
29
+ json[:errors].should eq(expected_errors)
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe InstantApi::Controller::BuildCreate do
4
+ let(:controller) { Object.new }
5
+ let(:model_class_name) { 'Aclass' }
6
+
7
+ subject { InstantApi::Controller::BuildCreate.new(controller, model_class_name) }
8
+
9
+ describe '#build' do
10
+ before { subject.build }
11
+
12
+ it { controller.respond_to?(:create).should be_true }
13
+
14
+ context 'call to create' do
15
+ let(:resource) { double('resource', valid?: true, invalid?: false) }
16
+ let(:params) { Object.new }
17
+ let(:parameters) { Object.new }
18
+ let(:builder) { double(:builder, build: resource) }
19
+ class Aclass; end
20
+
21
+ before do
22
+ controller.should_receive(:params).and_return(params)
23
+ controller.stub_chain(:request, :path).and_return('/path')
24
+
25
+ InstantApi::Controller::Parameters.should_receive(:new).
26
+ with(params, '/path').
27
+ and_return(parameters)
28
+ InstantApi::Model::Builder.should_receive(:new).
29
+ with(parameters, Aclass, true).
30
+ and_return(builder)
31
+
32
+ controller.should_receive(:render).with({json: resource}).and_return(true)
33
+ end
34
+
35
+ it { controller.create.should be_true }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe InstantApi::Controller::BuildDestroy do
4
+ let(:controller) { Object.new }
5
+
6
+ subject { InstantApi::Controller::BuildDestroy.new(controller) }
7
+
8
+ describe '#build' do
9
+ before { subject.build }
10
+
11
+ it { controller.respond_to?(:destroy).should be_true }
12
+
13
+ context 'call to destroy' do
14
+ let(:resource) { double(:resource, destroy!: true) }
15
+
16
+ before do
17
+ controller.should_receive(:resource).and_return(resource)
18
+ controller.should_receive(:render).with({status: 200, nothing: true}).
19
+ and_return(true)
20
+ end
21
+
22
+ it { controller.destroy.should be_true }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe InstantApi::Controller::BuildEdit do
4
+ let(:controller) { Object.new }
5
+
6
+ subject { InstantApi::Controller::BuildEdit.new(controller) }
7
+
8
+ describe '#build' do
9
+ before { subject.build }
10
+
11
+ it { controller.respond_to?(:edit).should be_true }
12
+
13
+ context 'call to edit' 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.edit.should be_true }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe InstantApi::Controller::BuildIndex do
4
+ let(:controller) { Object.new }
5
+ let(:model_class_name) { 'Aclass' }
6
+
7
+ subject { InstantApi::Controller::BuildIndex.new(controller, model_class_name) }
8
+
9
+ describe '#build' do
10
+ before { subject.build }
11
+
12
+ it { controller.respond_to?(:index).should be_true }
13
+
14
+ context 'call to index' do
15
+ let(:paginated_collection) { Object.new }
16
+ let(:params) { Object.new }
17
+ let(:parameters) { Object.new }
18
+ let(:request) { double(:request, path: '/a') }
19
+ let(:response) do
20
+ {
21
+ collection: paginated_collection,
22
+ pagination: {
23
+ count: 7,
24
+ page: 3,
25
+ per_page: 5
26
+ }
27
+ }
28
+ end
29
+
30
+ before do
31
+ model_collection = InstantApi::Model::Collection.any_instance
32
+ model_collection.should_receive(:page).and_return(3)
33
+ model_collection.should_receive(:per_page).and_return(5)
34
+ model_collection.should_receive(:paginated_collection).and_return(paginated_collection)
35
+ model_collection.should_receive(:count).and_return(7)
36
+
37
+ InstantApi::Controller::Parameters.should_receive(:new).
38
+ with(params, request.path).
39
+ and_return(parameters)
40
+
41
+ controller.should_receive(:request).and_return(request)
42
+ controller.should_receive(:params).and_return(params)
43
+ controller.should_receive(:render).with(json: response).and_return(true)
44
+ end
45
+
46
+ it { controller.index.should be_true }
47
+ end
48
+ end
49
+ end