code42_api_rails 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +33 -0
- data/Rakefile +35 -0
- data/features/api_response.feature +22 -0
- data/features/errors.feature +24 -0
- data/features/step_definitions/model_steps.rb +3 -0
- data/features/support/env.rb +72 -0
- data/lib/code42_api/base_controller.rb +5 -0
- data/lib/code42_api/base_responder.rb +19 -0
- data/lib/code42_api/errors.rb +90 -0
- data/lib/code42_api/model.rb +11 -0
- data/lib/code42_api/railtie.rb +7 -0
- data/lib/code42_api/version.rb +3 -0
- data/lib/code42_api_rails.rb +8 -0
- data/lib/tasks/code42_rails_tasks.rake +4 -0
- data/test/code42_api_test.rb +8 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/authors_controller.rb +15 -0
- data/test/dummy/app/controllers/test_controller.rb +7 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/author.rb +4 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cucumber.yml +8 -0
- data/test/dummy/config/database.yml +28 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +82 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +8 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/lib/tasks/cucumber.rake +65 -0
- data/test/dummy/log/development.log +2 -0
- data/test/dummy/log/test.log +1826 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/cucumber +10 -0
- data/test/errors_test.rb +41 -0
- data/test/test_helper.rb +20 -0
- metadata +256 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c45add59110e012980ff808a973e08ecf78daf8c
|
4
|
+
data.tar.gz: eea16008c0938e30ad6adca5a9b702aa6c967395
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9fd9f7e1f9d3112455a18d43b5378c0757c70421096ba5ec75fbc717f7557421a8c5d242ac3c197db3e23d33b71849b2d31f6babfad564955ed91051dd4b320e
|
7
|
+
data.tar.gz: 75f318e48b5176ff308ee0bd3bf0e1ea7d7a803c02f320d550d840f62dfd7029af56e3e0ef3998d0929924e370806ccd8d7434d289d23478fe2cfa557ac3220a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Code42ApiRails
|
2
|
+
|
3
|
+
A gem to enable API spec compliance to rails projects
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'code42_api_rails'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
$ bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Subclass your controllers from RailsApi::BaseController.
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class ApplicationController < RailsApi::BaseController
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
Subclass your models from RailsApi::Model.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class MyModel < RailsApi::Model
|
32
|
+
end
|
33
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Code42Api'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
Bundler::GemHelper.install_tasks
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
require 'cucumber/rake/task'
|
21
|
+
|
22
|
+
task :test => [:minitest, :features]
|
23
|
+
|
24
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
25
|
+
end
|
26
|
+
|
27
|
+
Rake::TestTask.new(:minitest) do |t|
|
28
|
+
t.libs << 'lib'
|
29
|
+
t.libs << 'test'
|
30
|
+
t.pattern = 'test/**/*_test.rb'
|
31
|
+
t.verbose = false
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
task default: :test
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: API Response
|
2
|
+
|
3
|
+
Scenario: PUT returns an object
|
4
|
+
Given I send and accept JSON
|
5
|
+
And the following Author exists:
|
6
|
+
| id | first_name | last_name |
|
7
|
+
| 3 | Stephen | King |
|
8
|
+
When I send a PUT request to "/authors/3" with the following:
|
9
|
+
"""
|
10
|
+
{ "author": { "last_name": "Colbert" } }
|
11
|
+
"""
|
12
|
+
Then the response status should be "200"
|
13
|
+
And the JSON response should be:
|
14
|
+
"""
|
15
|
+
{
|
16
|
+
"id": 3,
|
17
|
+
"first_name": "Stephen",
|
18
|
+
"last_name": "Colbert",
|
19
|
+
"created_at": "2014-01-01T06:00:00.000Z",
|
20
|
+
"updated_at": "2014-01-01T06:00:00.000Z"
|
21
|
+
}
|
22
|
+
"""
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: API Errors
|
2
|
+
|
3
|
+
Scenario: Validation errors are formatted according to spec
|
4
|
+
Given I send and accept JSON
|
5
|
+
And the following Author exists:
|
6
|
+
| id | first_name | last_name |
|
7
|
+
| 3 | Stephen | King |
|
8
|
+
When I send a PUT request to "/authors/3" with the following:
|
9
|
+
"""
|
10
|
+
{ "author": { "last_name": null } }
|
11
|
+
"""
|
12
|
+
Then the response status should be "422"
|
13
|
+
And the JSON response should be:
|
14
|
+
"""
|
15
|
+
{
|
16
|
+
"errors": [
|
17
|
+
{
|
18
|
+
"name": "last_name",
|
19
|
+
"type": "PresenceError",
|
20
|
+
"description": "can't be blank"
|
21
|
+
}
|
22
|
+
]
|
23
|
+
}
|
24
|
+
"""
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
2
|
+
# It is recommended to regenerate this file in the future when you upgrade to a
|
3
|
+
# newer version of cucumber-rails. Consider adding your own code to a new file
|
4
|
+
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
5
|
+
# files.
|
6
|
+
|
7
|
+
ENV['RAILS_ROOT'] = File.expand_path('./test/dummy')
|
8
|
+
|
9
|
+
require 'cucumber/rails'
|
10
|
+
require 'cucumber/api_steps'
|
11
|
+
require 'rspec'
|
12
|
+
require 'cucumber/rspec/doubles'
|
13
|
+
|
14
|
+
# Capybara defaults to CSS3 selectors rather than XPath.
|
15
|
+
# If you'd prefer to use XPath, just uncomment this line and adjust any
|
16
|
+
# selectors in your step definitions to use the XPath syntax.
|
17
|
+
# Capybara.default_selector = :xpath
|
18
|
+
|
19
|
+
# By default, any exception happening in your Rails application will bubble up
|
20
|
+
# to Cucumber so that your scenario will fail. This is a different from how
|
21
|
+
# your application behaves in the production environment, where an error page will
|
22
|
+
# be rendered instead.
|
23
|
+
#
|
24
|
+
# Sometimes we want to override this default behaviour and allow Rails to rescue
|
25
|
+
# exceptions and display an error page (just like when the app is running in production).
|
26
|
+
# Typical scenarios where you want to do this is when you test your error pages.
|
27
|
+
# There are two ways to allow Rails to rescue exceptions:
|
28
|
+
#
|
29
|
+
# 1) Tag your scenario (or feature) with @allow-rescue
|
30
|
+
#
|
31
|
+
# 2) Set the value below to true. Beware that doing this globally is not
|
32
|
+
# recommended as it will mask a lot of errors for you!
|
33
|
+
#
|
34
|
+
ActionController::Base.allow_rescue = false
|
35
|
+
|
36
|
+
# Remove/comment out the lines below if your app doesn't have a database.
|
37
|
+
# For some databases (like MongoDB and CouchDB) you may need to use :truncation instead.
|
38
|
+
begin
|
39
|
+
DatabaseCleaner.strategy = :transaction
|
40
|
+
rescue NameError
|
41
|
+
raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
|
42
|
+
end
|
43
|
+
|
44
|
+
RSpec.configure do |config|
|
45
|
+
config.expect_with :rspec do |c|
|
46
|
+
c.syntax = %i(should expect)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios.
|
51
|
+
# See the DatabaseCleaner documentation for details. Example:
|
52
|
+
#
|
53
|
+
# Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do
|
54
|
+
# # { :except => [:widgets] } may not do what you expect here
|
55
|
+
# # as Cucumber::Rails::Database.javascript_strategy overrides
|
56
|
+
# # this setting.
|
57
|
+
# DatabaseCleaner.strategy = :truncation
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do
|
61
|
+
# DatabaseCleaner.strategy = :transaction
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
|
65
|
+
# Possible values are :truncation and :transaction
|
66
|
+
# The :transaction strategy is faster, but might give you threading problems.
|
67
|
+
# See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature
|
68
|
+
Cucumber::Rails::Database.javascript_strategy = :truncation
|
69
|
+
|
70
|
+
Before do
|
71
|
+
Time.stub(:now).and_return Time.new(2014,1,1)
|
72
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
module Code42Api
|
3
|
+
class BaseResponder < ActionController::Responder
|
4
|
+
def api_behavior(error)
|
5
|
+
raise error unless resourceful?
|
6
|
+
raise MissingRenderer.new(format) unless has_renderer?
|
7
|
+
|
8
|
+
if get?
|
9
|
+
display resource
|
10
|
+
elsif post?
|
11
|
+
display resource, status: :created, location: api_location
|
12
|
+
elsif put? || patch?
|
13
|
+
display resource, status: :ok
|
14
|
+
else
|
15
|
+
head :no_content
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module Code42Api
|
4
|
+
TypeMapping = Struct.new(:type, :description)
|
5
|
+
class Errors < ActiveModel::Errors
|
6
|
+
STANDARD_ERROR = "StandardError"
|
7
|
+
ERROR_MAPPINGS = {
|
8
|
+
'PresenceError' => [
|
9
|
+
:blank,
|
10
|
+
:empty,
|
11
|
+
:present
|
12
|
+
],
|
13
|
+
'LengthError' => [
|
14
|
+
:too_long,
|
15
|
+
:too_short,
|
16
|
+
:wrong_length
|
17
|
+
],
|
18
|
+
'BoundError' => [
|
19
|
+
:greater_than,
|
20
|
+
:greater_than_or_equal_to,
|
21
|
+
:equal_to,
|
22
|
+
:less_than,
|
23
|
+
:less_than_or_equal_to,
|
24
|
+
:other_than,
|
25
|
+
:odd,
|
26
|
+
:even
|
27
|
+
],
|
28
|
+
'TypeError' => [
|
29
|
+
:not_a_number,
|
30
|
+
:not_an_integer
|
31
|
+
],
|
32
|
+
'AcceptanceError' => [:accepted],
|
33
|
+
'FormatError' => [:invalid],
|
34
|
+
'InclusionError' => [
|
35
|
+
:inclusion,
|
36
|
+
:exclusion
|
37
|
+
]
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
def add(attribute, message = :invalid, options = {})
|
42
|
+
type = get_type(message, options)
|
43
|
+
|
44
|
+
message = normalize_message(attribute, message, options)
|
45
|
+
if exception = options[:strict]
|
46
|
+
exception = ActiveModel::StrictValidationFailed if exception == true
|
47
|
+
raise exception, full_message(attribute, message)
|
48
|
+
end
|
49
|
+
|
50
|
+
self[attribute] << ErrorMessage.new(message, type)
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_type(message, options)
|
54
|
+
if options[:error_type]
|
55
|
+
options[:error_type]
|
56
|
+
elsif message.is_a?(Symbol)
|
57
|
+
type_from_symbol(message)
|
58
|
+
else
|
59
|
+
STANDARD_ERROR
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def type_from_symbol(symbol)
|
64
|
+
ERROR_MAPPINGS.keys.detect { |k| ERROR_MAPPINGS[k].include?(symbol) } || STANDARD_ERROR
|
65
|
+
end
|
66
|
+
|
67
|
+
def as_json(options = nil)
|
68
|
+
errors = []
|
69
|
+
to_hash.each do |(attribute_name,values)|
|
70
|
+
values.each do |value|
|
71
|
+
errors << {
|
72
|
+
name: attribute_name,
|
73
|
+
type: value.type,
|
74
|
+
description: value,
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
errors.as_json(options)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class ErrorMessage < SimpleDelegator
|
83
|
+
attr_reader :type
|
84
|
+
|
85
|
+
def initialize(message, type = STANDARD_ERROR)
|
86
|
+
@type = type
|
87
|
+
super(message)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/test/dummy/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|