rocket_pants 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rocket_pants.rb +36 -1
- data/lib/rocket_pants/active_record.rb +19 -0
- data/lib/rocket_pants/base.rb +1 -1
- data/lib/rocket_pants/client.rb +1 -1
- data/lib/rocket_pants/controller/error_handling.rb +25 -7
- data/lib/rocket_pants/controller/rescuable.rb +2 -1
- data/lib/rocket_pants/error.rb +59 -0
- data/lib/rocket_pants/{exceptions.rb → errors.rb} +24 -55
- data/lib/rocket_pants/locale/en.yml +3 -1
- data/lib/rocket_pants/railtie.rb +15 -7
- metadata +4 -2
data/lib/rocket_pants.rb
CHANGED
@@ -7,7 +7,8 @@ require 'moneta'
|
|
7
7
|
require 'moneta/memory'
|
8
8
|
|
9
9
|
module RocketPants
|
10
|
-
require 'rocket_pants/
|
10
|
+
require 'rocket_pants/error'
|
11
|
+
require 'rocket_pants/errors'
|
11
12
|
|
12
13
|
# Set up the routing in advance.
|
13
14
|
require 'rocket_pants/routing'
|
@@ -39,6 +40,7 @@ module RocketPants
|
|
39
40
|
autoload :UrlFor, 'rocket_pants/controller/url_for'
|
40
41
|
|
41
42
|
mattr_accessor :caching_enabled, :header_metadata
|
43
|
+
|
42
44
|
self.caching_enabled = false
|
43
45
|
self.header_metadata = false
|
44
46
|
|
@@ -51,6 +53,39 @@ module RocketPants
|
|
51
53
|
def cache
|
52
54
|
@@cache ||= Moneta::Memory.new
|
53
55
|
end
|
56
|
+
|
57
|
+
def env
|
58
|
+
@@env ||= default_env
|
59
|
+
end
|
60
|
+
|
61
|
+
def env=(value)
|
62
|
+
value = value.presence && ActiveSupport::StringInquirer.new(value)
|
63
|
+
@@env = value
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_env
|
67
|
+
env = Rails.env.to_s if defined?(Rails.env)
|
68
|
+
env ||= ENV['RAILS_ENV'].presence || ENV['RACK_ENV'].presence || "development"
|
69
|
+
ActiveSupport::StringInquirer.new env
|
70
|
+
end
|
71
|
+
|
72
|
+
def default_pass_through_errors
|
73
|
+
env.development? || env.test?
|
74
|
+
end
|
75
|
+
|
76
|
+
def pass_through_errors
|
77
|
+
if defined?(@@pass_through_errors) && [true, false].include?(@@pass_through_errors)
|
78
|
+
@@pass_through_errors
|
79
|
+
else
|
80
|
+
@@pass_through_errors = default_pass_through_errors
|
81
|
+
end
|
82
|
+
end
|
83
|
+
alias pass_through_errors? pass_through_errors
|
84
|
+
|
85
|
+
def pass_through_errors=(value)
|
86
|
+
@@pass_through_errors = value
|
87
|
+
end
|
88
|
+
|
54
89
|
end
|
55
90
|
|
56
91
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Provides a bunch of default active record integration entry points.
|
2
|
+
module RocketPants
|
3
|
+
module ActiveRecordIntegration
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
map_error! ActiveRecord::RecordNotFound, RocketPants::NotFound
|
8
|
+
map_error!(ActiveRecord::RecordNotSaved) { RocketPants::InvalidResource.new nil }
|
9
|
+
map_error! ActiveRecord::RecordInvalid do |exception|
|
10
|
+
RocketPants::InvalidResource.new exception.record.errors
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
ActiveSupport.on_load :active_record do
|
15
|
+
RocketPants::Base.send :include, RocketPants::ActiveRecordIntegration
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/rocket_pants/base.rb
CHANGED
data/lib/rocket_pants/client.rb
CHANGED
@@ -2,7 +2,7 @@ require 'active_support/core_ext/class/attribute'
|
|
2
2
|
require 'active_support/core_ext/object/blank'
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
4
4
|
|
5
|
-
require 'rocket_pants/
|
5
|
+
require 'rocket_pants/errors'
|
6
6
|
|
7
7
|
require 'api_smith'
|
8
8
|
require 'will_paginate/collection'
|
@@ -13,8 +13,15 @@ module RocketPants
|
|
13
13
|
|
14
14
|
module ClassMethods
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
# Declares that a given error class (e.g. ActiveRecord::RecordNotFound)
|
17
|
+
# should map to a second value - either an Exception class Or, more usefully,
|
18
|
+
# a callable object.
|
19
|
+
# @param [Class] from the exception class to map from.
|
20
|
+
# @param [Class, #call] to the callable to map to, or a callback block.
|
21
|
+
def map_error!(from, to = nil, &blk)
|
22
|
+
to = (to || blk)
|
23
|
+
raise ArgumentError, "Either an option must be provided or a block given." unless to
|
24
|
+
error_mapping[from] = (to || blk)
|
18
25
|
rescue_from from, :with => :render_error
|
19
26
|
end
|
20
27
|
|
@@ -47,7 +54,8 @@ module RocketPants
|
|
47
54
|
# TODO: Add in notification hooks for non-standard exceptions.
|
48
55
|
name = lookup_error_name(exception)
|
49
56
|
message = (exception.message == exception.class.name) ? 'An unknown error has occurred.' : exception.message
|
50
|
-
|
57
|
+
context = lookup_error_context(exception).reverse_merge(:scope => :"rocket_pants.errors", :default => message)
|
58
|
+
I18n.t name, context.except(:metadata)
|
51
59
|
end
|
52
60
|
|
53
61
|
# Lookup error name will automatically handle translating errors to a
|
@@ -77,12 +85,17 @@ module RocketPants
|
|
77
85
|
exception.respond_to?(:context) ? exception.context : {}
|
78
86
|
end
|
79
87
|
|
80
|
-
# Returns extra error details for a given object, making it useable
|
81
|
-
# for hooking in external exceptions.
|
82
88
|
def lookup_error_extras(exception)
|
83
89
|
{}
|
84
90
|
end
|
85
91
|
|
92
|
+
# Returns extra error details for a given object, making it useable
|
93
|
+
# for hooking in external exceptions.
|
94
|
+
def lookup_error_metadata(exception)
|
95
|
+
context = lookup_error_context exception
|
96
|
+
context.fetch(:metadata, {}).merge lookup_error_extras(exception)
|
97
|
+
end
|
98
|
+
|
86
99
|
# Renders an exception as JSON using a nicer version of the error name and
|
87
100
|
# error message, following the typically error standard as laid out in the JSON
|
88
101
|
# api design.
|
@@ -95,13 +108,18 @@ module RocketPants
|
|
95
108
|
klass < StandardError and error_mapping.has_key?(klass)
|
96
109
|
end
|
97
110
|
if normalised_class
|
98
|
-
|
111
|
+
mapped = error_mapping[normalised_class]
|
112
|
+
if mapped.respond_to?(:call)
|
113
|
+
exception = mapped.call(exception)
|
114
|
+
else
|
115
|
+
exception = mapped.new exception.message
|
116
|
+
end
|
99
117
|
end
|
100
118
|
self.status = lookup_error_status(exception)
|
101
119
|
render_json({
|
102
120
|
:error => lookup_error_name(exception).to_s,
|
103
121
|
:error_description => lookup_error_message(exception)
|
104
|
-
}.merge(
|
122
|
+
}.merge(lookup_error_metadata(exception)))
|
105
123
|
end
|
106
124
|
|
107
125
|
end
|
@@ -8,7 +8,7 @@ module RocketPants
|
|
8
8
|
include ActiveSupport::Rescuable
|
9
9
|
|
10
10
|
DEFAULT_NOTIFIER_CALLBACK = lambda do |controller, exception, req|
|
11
|
-
#
|
11
|
+
# Does nothing
|
12
12
|
end
|
13
13
|
|
14
14
|
NAMED_NOTIFIER_CALLBACKS = {
|
@@ -52,6 +52,7 @@ module RocketPants
|
|
52
52
|
def process_action(*args)
|
53
53
|
super
|
54
54
|
rescue Exception => exception
|
55
|
+
raise if RocketPants.pass_through_errors?
|
55
56
|
# Otherwise, use the default built in handler.
|
56
57
|
logger.error "Exception occured: #{exception.class.name} - #{exception.message}"
|
57
58
|
logger.error "Exception backtrace:"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RocketPants
|
2
|
+
|
3
|
+
# Represents the standard error type as defined by the API.
|
4
|
+
# RocketPants::Error instances will be caught and automatically rendered as JSON
|
5
|
+
# by the controller during processing.
|
6
|
+
class Error < StandardError
|
7
|
+
|
8
|
+
# @overload error_name
|
9
|
+
# Returns the error name for this error class, defaulting to
|
10
|
+
# the class name underscorized minus _error.
|
11
|
+
# @return [Symbol] the given errors name.
|
12
|
+
# @overload error_name(value)
|
13
|
+
# Sets the error name for the current class.
|
14
|
+
# @param [#to_sym] the name of this error.
|
15
|
+
def self.error_name(value = nil)
|
16
|
+
if value.nil?
|
17
|
+
@name ||= name.underscore.split("/").last.sub(/_error$/, '').to_sym
|
18
|
+
else
|
19
|
+
@name = (value.presence && value.to_sym)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @overload http_status
|
24
|
+
# Returns the http status code of this error, defaulting to 400 (Bad Request).
|
25
|
+
# @overload http_status(value)
|
26
|
+
# Sets the http status code for this error to a given symbol / integer.
|
27
|
+
# @param value [String, Fixnum] value the new status code.
|
28
|
+
def self.http_status(value = nil)
|
29
|
+
if value.nil?
|
30
|
+
@http_status ||= 400
|
31
|
+
else
|
32
|
+
@http_status = (value.presence && value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Gets the name of this error from the class.
|
37
|
+
def error_name
|
38
|
+
self.class.error_name
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gets the http status of this error from the class.
|
42
|
+
def http_status
|
43
|
+
self.class.http_status
|
44
|
+
end
|
45
|
+
|
46
|
+
# Setter for optional data about this error, used for translation.
|
47
|
+
attr_writer :context
|
48
|
+
|
49
|
+
# Gets the context for this error, defaulting to nil.
|
50
|
+
# @return [Hash] the context for this param.
|
51
|
+
def context
|
52
|
+
@context ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
error_name :unknown
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -1,60 +1,6 @@
|
|
1
1
|
module RocketPants
|
2
|
-
|
3
|
-
# Represents the standard error type as defined by the API.
|
4
|
-
# RocketPants::Error instances will be caught and automatically rendered as JSON
|
5
|
-
# by the controller during processing.
|
6
|
-
class Error < StandardError
|
7
|
-
|
8
|
-
# @overload error_name
|
9
|
-
# Returns the error name for this error class, defaulting to
|
10
|
-
# the class name underscorized minus _error.
|
11
|
-
# @return [Symbol] the given errors name.
|
12
|
-
# @overload error_name(value)
|
13
|
-
# Sets the error name for the current class.
|
14
|
-
# @param [#to_sym] the name of this error.
|
15
|
-
def self.error_name(value = nil)
|
16
|
-
if value.nil?
|
17
|
-
@name ||= name.underscore.split("/").last.sub(/_error$/, '').to_sym
|
18
|
-
else
|
19
|
-
@name = (value.presence && value.to_sym)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# @overload http_status
|
24
|
-
# Returns the http status code of this error, defaulting to 400 (Bad Request).
|
25
|
-
# @overload http_status(value)
|
26
|
-
# Sets the http status code for this error to a given symbol / integer.
|
27
|
-
# @param value [String, Fixnum] value the new status code.
|
28
|
-
def self.http_status(value = nil)
|
29
|
-
if value.nil?
|
30
|
-
@http_status ||= 400
|
31
|
-
else
|
32
|
-
@http_status = (value.presence && value)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# Gets the name of this error from the class.
|
37
|
-
def error_name
|
38
|
-
self.class.error_name
|
39
|
-
end
|
40
|
-
|
41
|
-
# Gets the http status of this error from the class.
|
42
|
-
def http_status
|
43
|
-
self.class.http_status
|
44
|
-
end
|
45
|
-
|
46
|
-
# Setter for optional data about this error, used for translation.
|
47
|
-
attr_writer :context
|
48
|
-
|
49
|
-
# Gets the context for this error, defaulting to nil.
|
50
|
-
# @return [Hash] the context for this param.
|
51
|
-
def context
|
52
|
-
@context ||= {}
|
53
|
-
end
|
54
|
-
|
55
|
-
error_name :unknown
|
56
2
|
|
57
|
-
|
3
|
+
require 'rocket_pants/error'
|
58
4
|
|
59
5
|
# A simple map of data about errors that the rocket pants system can handle.
|
60
6
|
class Errors
|
@@ -111,5 +57,28 @@ module RocketPants
|
|
111
57
|
register! :bad_request, :http_status => :bad_request
|
112
58
|
|
113
59
|
end
|
60
|
+
|
61
|
+
class InvalidResource < RocketPants::Error
|
62
|
+
http_status :unprocessable_entity
|
63
|
+
error_name :invalid_resource
|
64
|
+
|
65
|
+
# Errors are ActiveModel Errors
|
66
|
+
attr_reader :errors
|
67
|
+
|
68
|
+
def initialize(errors, *args)
|
69
|
+
@errors = errors
|
70
|
+
super *args
|
71
|
+
end
|
72
|
+
|
73
|
+
def context
|
74
|
+
super.tap do |ctx|
|
75
|
+
extras = (ctx[:metadata] ||= {})
|
76
|
+
extras[:messages] = errors.to_hash if errors
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Errors.add self
|
81
|
+
|
82
|
+
end
|
114
83
|
|
115
84
|
end
|
@@ -5,4 +5,6 @@ en:
|
|
5
5
|
unauthenticated: "This action requires authentication to continue."
|
6
6
|
invalid_version: "This action is not available in the given version of the api."
|
7
7
|
not_implemented: "The feature you requested has not yet been implemented and hence is currently unavailable."
|
8
|
-
not_found: "The requested resource could not be found."
|
8
|
+
not_found: "The requested resource could not be found."
|
9
|
+
bad_request: "The data given to this server does not meet our criteria."
|
10
|
+
invalid_resource: "The current resource was deemed invalid."
|
data/lib/rocket_pants/railtie.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module RocketPants
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
|
4
|
-
config.rocket_pants
|
5
|
-
config.rocket_pants.use_caching
|
6
|
-
config.rocket_pants.header_metadata
|
4
|
+
config.rocket_pants = ActiveSupport::OrderedOptions.new
|
5
|
+
config.rocket_pants.use_caching = nil
|
6
|
+
config.rocket_pants.header_metadata = nil
|
7
|
+
config.rocket_pants.pass_through_errors = nil
|
7
8
|
|
8
9
|
config.i18n.railties_load_path << File.expand_path('../locale/en.yml', __FILE__)
|
9
10
|
|
@@ -12,10 +13,11 @@ module RocketPants
|
|
12
13
|
end
|
13
14
|
|
14
15
|
initializer "rocket_pants.configuration" do |app|
|
15
|
-
rp_config
|
16
|
-
rp_config.use_caching
|
17
|
-
RocketPants.caching_enabled
|
18
|
-
RocketPants.header_metadata
|
16
|
+
rp_config = app.config.rocket_pants
|
17
|
+
rp_config.use_caching = Rails.env.production? if rp_config.use_caching.nil?
|
18
|
+
RocketPants.caching_enabled = rp_config.use_caching
|
19
|
+
RocketPants.header_metadata = rp_config.header_metadata unless rp_config.header_metadata.nil?
|
20
|
+
RocketPants.pass_through_errors = rp_config.pass_through_errors unless rp_config.pass_through_errors.nil?
|
19
21
|
# Set the rocket pants cache if present.
|
20
22
|
RocketPants.cache = rp_config.cache if rp_config.cache
|
21
23
|
end
|
@@ -38,6 +40,12 @@ module RocketPants
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
43
|
+
initializer "rocket_pants.setup_activerecord" do
|
44
|
+
if defined?(ActiveRecord)
|
45
|
+
require 'rocket_pants/active_record'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
41
49
|
rake_tasks do
|
42
50
|
load "rocket_pants/tasks/rocket_pants.rake"
|
43
51
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rocket_pants
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -243,6 +243,7 @@ executables: []
|
|
243
243
|
extensions: []
|
244
244
|
extra_rdoc_files: []
|
245
245
|
files:
|
246
|
+
- lib/rocket_pants/active_record.rb
|
246
247
|
- lib/rocket_pants/base.rb
|
247
248
|
- lib/rocket_pants/cache_middleware.rb
|
248
249
|
- lib/rocket_pants/cacheable.rb
|
@@ -258,7 +259,8 @@ files:
|
|
258
259
|
- lib/rocket_pants/controller/respondable.rb
|
259
260
|
- lib/rocket_pants/controller/url_for.rb
|
260
261
|
- lib/rocket_pants/controller/versioning.rb
|
261
|
-
- lib/rocket_pants/
|
262
|
+
- lib/rocket_pants/error.rb
|
263
|
+
- lib/rocket_pants/errors.rb
|
262
264
|
- lib/rocket_pants/locale/en.yml
|
263
265
|
- lib/rocket_pants/railtie.rb
|
264
266
|
- lib/rocket_pants/routing.rb
|