Browse Source

Application controller refactoring: Moving error methods to proper concern.

Thorsten Eckel 8 years ago
parent
commit
f511995ba6
2 changed files with 83 additions and 95 deletions
  1. 2 95
      app/controllers/application_controller.rb
  2. 81 0
      app/controllers/concerns/error_handling.rb

+ 2 - 95
app/controllers/application_controller.rb

@@ -2,6 +2,8 @@
 require 'exceptions'
 
 class ApplicationController < ActionController::Base
+  include ErrorHandling
+
   #  http_basic_authenticate_with :name => "test", :password => "ttt"
 
   helper_method :current_user,
@@ -18,15 +20,6 @@ class ApplicationController < ActionController::Base
   before_action :transaction_begin, :set_user, :session_update, :user_device_check, :cors_preflight_check
   after_action  :transaction_end, :http_log, :set_access_control_headers
 
-  rescue_from StandardError, with: :server_error
-  rescue_from ExecJS::RuntimeError, with: :server_error
-  rescue_from ActiveRecord::RecordNotFound, with: :not_found
-  rescue_from ActiveRecord::StatementInvalid, with: :unprocessable_entity
-  rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
-  rescue_from ArgumentError, with: :unprocessable_entity
-  rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity
-  rescue_from Exceptions::NotAuthorized, with: :unauthorized
-
   # For all responses in this controller, return the CORS access control headers.
   def set_access_control_headers
     headers['Access-Control-Allow-Origin']      = '*'
@@ -658,30 +651,6 @@ class ApplicationController < ActionController::Base
     render json: generic_objects, status: :ok
   end
 
-  def model_match_error(error)
-    data = {
-      error: error
-    }
-    if error =~ /Validation failed: (.+?)(,|$)/i
-      data[:error_human] = $1
-    end
-    if error =~ /(already exists|duplicate key|duplicate entry)/i
-      data[:error_human] = 'Object already exists!'
-    end
-    if error =~ /null value in column "(.+?)" violates not-null constraint/i
-      data[:error_human] = "Attribute '#{$1}' required!"
-    end
-    if error =~ /Field '(.+?)' doesn't have a default value/i
-      data[:error_human] = "Attribute '#{$1}' required!"
-    end
-
-    if Rails.env.production? && !data[:error_human].empty?
-      data[:error] = data[:error_human]
-      data.delete('error_human')
-    end
-    data
-  end
-
   def model_references_check(object, params)
     generic_object = object.find(params[:id])
     result = Models.references(object, generic_object.id)
@@ -691,68 +660,6 @@ class ApplicationController < ActionController::Base
     raise Exceptions::UnprocessableEntity, e
   end
 
-  def not_found(e)
-    logger.error e.message
-    logger.error e.backtrace.inspect
-    respond_to do |format|
-      format.json { render json: model_match_error(e.message), status: :not_found }
-      format.any {
-        @exception = e
-        @traceback = !Rails.env.production?
-        file = File.open(Rails.root.join('public', '404.html'), 'r')
-        render inline: file.read, status: :not_found
-      }
-    end
-  end
-
-  def unprocessable_entity(e)
-    logger.error e.message
-    logger.error e.backtrace.inspect
-    respond_to do |format|
-      format.json { render json: model_match_error(e.message), status: :unprocessable_entity }
-      format.any {
-        @exception = e
-        @traceback = !Rails.env.production?
-        file = File.open(Rails.root.join('public', '422.html'), 'r')
-        render inline: file.read, status: :unprocessable_entity
-      }
-    end
-  end
-
-  def server_error(e)
-    logger.error e.message
-    logger.error e.backtrace.inspect
-    respond_to do |format|
-      format.json { render json: model_match_error(e.message), status: 500 }
-      format.any {
-        @exception = e
-        @traceback = !Rails.env.production?
-        file = File.open(Rails.root.join('public', '500.html'), 'r')
-        render inline: file.read, status: 500
-      }
-    end
-  end
-
-  def unauthorized(e)
-    message = e.message
-    if message == 'Exceptions::NotAuthorized'
-      message = 'Not authorized'
-    end
-    error = model_match_error(message)
-    if error && error[:error]
-      response.headers['X-Failure'] = error[:error_human] || error[:error]
-    end
-    respond_to do |format|
-      format.json { render json: error, status: :unauthorized }
-      format.any {
-        @exception = e
-        @traceback = !Rails.env.production?
-        file = File.open(Rails.root.join('public', '401.html'), 'r')
-        render inline: file.read, status: :unauthorized
-      }
-    end
-  end
-
   # check maintenance mode
   def check_maintenance_only(user)
     return false if Setting.get('maintenance_mode') != true

+ 81 - 0
app/controllers/concerns/error_handling.rb

@@ -0,0 +1,81 @@
+module ErrorHandling
+  extend ActiveSupport::Concern
+
+  included do
+    rescue_from StandardError, with: :internal_server_error
+    rescue_from ExecJS::RuntimeError, with: :internal_server_error
+    rescue_from ActiveRecord::RecordNotFound, with: :not_found
+    rescue_from ActiveRecord::StatementInvalid, with: :unprocessable_entity
+    rescue_from ActiveRecord::RecordInvalid, with: :unprocessable_entity
+    rescue_from ArgumentError, with: :unprocessable_entity
+    rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity
+    rescue_from Exceptions::NotAuthorized, with: :unauthorized
+  end
+
+  def not_found(e)
+    log_error_exception(e)
+    respond_to_exception(e, :not_found)
+  end
+
+  def unprocessable_entity(e)
+    log_error_exception(e)
+    respond_to_exception(e, :unprocessable_entity)
+  end
+
+  def internal_server_error(e)
+    log_error_exception(e)
+    respond_to_exception(e, :internal_server_error)
+  end
+
+  def unauthorized(e)
+    error = humanize_error(e.message)
+    response.headers['X-Failure'] = error.fetch(:error_human, error[:error])
+    respond_to_exception(e, :unauthorized)
+  end
+
+  private
+
+  def log_error_exception(e)
+    logger.error e.message
+    logger.error e.backtrace.inspect
+  end
+
+  def respond_to_exception(e, status)
+    status_code = Rack::Utils.status_code(status)
+
+    respond_to do |format|
+      format.json { render json: humanize_error(e.message), status: status }
+      format.any {
+        @exception = e
+        @traceback = !Rails.env.production?
+        file = File.open(Rails.root.join('public', "#{status_code}.html"), 'r')
+        render inline: file.read, status: status
+      }
+    end
+  end
+
+  def humanize_error(error)
+    data = {
+      error: error
+    }
+
+    case error
+    when /Validation failed: (.+?)(,|$)/i
+      data[:error_human] = $1
+    when /(already exists|duplicate key|duplicate entry)/i
+      data[:error_human] = 'Object already exists!'
+    when /null value in column "(.+?)" violates not-null constraint/i
+      data[:error_human] = "Attribute '#{$1}' required!"
+    when /Field '(.+?)' doesn't have a default value/i
+      data[:error_human] = "Attribute '#{$1}' required!"
+    when 'Exceptions::NotAuthorized'
+      data[:error]       = 'Not authorized'
+      data[:error_human] = data[:error]
+    end
+
+    if Rails.env.production? && !data[:error_human].empty?
+      data[:error] = data.delete(:error_human)
+    end
+    data
+  end
+end