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 CHANGED
@@ -7,7 +7,8 @@ require 'moneta'
7
7
  require 'moneta/memory'
8
8
 
9
9
  module RocketPants
10
- require 'rocket_pants/exceptions'
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
@@ -1,4 +1,4 @@
1
- require 'rocket_pants/exceptions'
1
+ require 'rocket_pants/errors'
2
2
 
3
3
  module RocketPants
4
4
 
@@ -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/exceptions'
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
- def map_error!(from, to)
17
- error_mapping[from] = to
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
- I18n.t name, lookup_error_context(exception).reverse_merge(:scope => :"rocket_pants.errors", :default => message)
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
- exception = error_mapping[normalised_class].new(exception.message)
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(lookup_error_extras(exception)))
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
- # Do nothing by default...
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
- end
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."
@@ -1,9 +1,10 @@
1
1
  module RocketPants
2
2
  class Railtie < Rails::Railtie
3
3
 
4
- config.rocket_pants = ActiveSupport::OrderedOptions.new
5
- config.rocket_pants.use_caching = nil
6
- config.rocket_pants.header_metadata = nil
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 = app.config.rocket_pants
16
- rp_config.use_caching = Rails.env.production? if rp_config.use_caching.nil?
17
- RocketPants.caching_enabled = rp_config.use_caching
18
- RocketPants.header_metadata = rp_config.header_metadata unless rp_config.header_metadata.nil?
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.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/exceptions.rb
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