code42_api_rails 0.0.2

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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +33 -0
  4. data/Rakefile +35 -0
  5. data/features/api_response.feature +22 -0
  6. data/features/errors.feature +24 -0
  7. data/features/step_definitions/model_steps.rb +3 -0
  8. data/features/support/env.rb +72 -0
  9. data/lib/code42_api/base_controller.rb +5 -0
  10. data/lib/code42_api/base_responder.rb +19 -0
  11. data/lib/code42_api/errors.rb +90 -0
  12. data/lib/code42_api/model.rb +11 -0
  13. data/lib/code42_api/railtie.rb +7 -0
  14. data/lib/code42_api/version.rb +3 -0
  15. data/lib/code42_api_rails.rb +8 -0
  16. data/lib/tasks/code42_rails_tasks.rake +4 -0
  17. data/test/code42_api_test.rb +8 -0
  18. data/test/dummy/README.rdoc +28 -0
  19. data/test/dummy/Rakefile +6 -0
  20. data/test/dummy/app/assets/javascripts/application.js +13 -0
  21. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  22. data/test/dummy/app/controllers/application_controller.rb +5 -0
  23. data/test/dummy/app/controllers/authors_controller.rb +15 -0
  24. data/test/dummy/app/controllers/test_controller.rb +7 -0
  25. data/test/dummy/app/helpers/application_helper.rb +2 -0
  26. data/test/dummy/app/models/author.rb +4 -0
  27. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  28. data/test/dummy/bin/bundle +3 -0
  29. data/test/dummy/bin/rails +4 -0
  30. data/test/dummy/bin/rake +4 -0
  31. data/test/dummy/config.ru +4 -0
  32. data/test/dummy/config/application.rb +23 -0
  33. data/test/dummy/config/boot.rb +5 -0
  34. data/test/dummy/config/cucumber.yml +8 -0
  35. data/test/dummy/config/database.yml +28 -0
  36. data/test/dummy/config/environment.rb +5 -0
  37. data/test/dummy/config/environments/development.rb +37 -0
  38. data/test/dummy/config/environments/production.rb +82 -0
  39. data/test/dummy/config/environments/test.rb +39 -0
  40. data/test/dummy/config/initializers/assets.rb +8 -0
  41. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  43. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  44. data/test/dummy/config/initializers/inflections.rb +16 -0
  45. data/test/dummy/config/initializers/mime_types.rb +4 -0
  46. data/test/dummy/config/initializers/session_store.rb +3 -0
  47. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  48. data/test/dummy/config/locales/en.yml +23 -0
  49. data/test/dummy/config/routes.rb +3 -0
  50. data/test/dummy/config/secrets.yml +22 -0
  51. data/test/dummy/db/development.sqlite3 +0 -0
  52. data/test/dummy/db/schema.rb +8 -0
  53. data/test/dummy/db/test.sqlite3 +0 -0
  54. data/test/dummy/lib/tasks/cucumber.rake +65 -0
  55. data/test/dummy/log/development.log +2 -0
  56. data/test/dummy/log/test.log +1826 -0
  57. data/test/dummy/public/404.html +67 -0
  58. data/test/dummy/public/422.html +67 -0
  59. data/test/dummy/public/500.html +66 -0
  60. data/test/dummy/public/favicon.ico +0 -0
  61. data/test/dummy/script/cucumber +10 -0
  62. data/test/errors_test.rb +41 -0
  63. data/test/test_helper.rb +20 -0
  64. 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,3 @@
1
+ Given(/^the following Author(s?) exist(s?):$/) do |_,_,authors|
2
+ Author.create!(authors.hashes)
3
+ end
@@ -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,5 @@
1
+ module Code42Api
2
+ class BaseController < ActionController::Base
3
+ self.responder = BaseResponder
4
+ end
5
+ 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,11 @@
1
+ require 'active_record'
2
+
3
+ module Code42Api
4
+ class Model < ActiveRecord::Base
5
+ self.abstract_class = true
6
+
7
+ def errors
8
+ @errors ||= Code42Api::Errors.new(self)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Code42Api
2
+ class Railtie < Rails::Railtie
3
+ config.before_initialize do
4
+ config.active_support.use_standard_json_time_format = true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Code42Api
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'code42_api/base_responder'
2
+ require 'code42_api/base_controller'
3
+ require 'code42_api/errors'
4
+ require 'code42_api/model'
5
+ require 'code42_api/railtie'
6
+
7
+ module Code42Api
8
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :code42_rails do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class Code42ApiTest < ActiveSupport::TestCase
4
+ test "time as json returns a iso8601 string" do
5
+ time = Time.utc(2014,1,1,12,0,0).in_time_zone
6
+ assert_equal "2014-01-01T12:00:00.000Z", time.as_json
7
+ end
8
+ 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>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -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 .