aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib
diff options
context:
space:
mode:
authorGenadi Samokovarov <gsamokovarov@gmail.com>2018-12-25 16:35:15 +0200
committerGenadi Samokovarov <gsamokovarov@gmail.com>2019-04-19 14:14:06 +0900
commit45f1c7a3e16437e517baa6606674f7bbb16dba74 (patch)
tree62fb0c46cd72368c2b8806c43612e809b45996a5 /activesupport/lib
parent16dae7684edc480ee3fe65dfff8e19989402c987 (diff)
downloadrails-45f1c7a3e16437e517baa6606674f7bbb16dba74.tar.gz
rails-45f1c7a3e16437e517baa6606674f7bbb16dba74.tar.bz2
rails-45f1c7a3e16437e517baa6606674f7bbb16dba74.zip
Introduce Actionable Errors
Actionable errors let's you dispatch actions from Rails' error pages. This can help you save time if you have a clear action for the resolution of common development errors. The de-facto example are pending migrations. Every time pending migrations are found, a middleware raises an error. With actionable errors, you can run the migrations right from the error page. Other examples include Rails plugins that need to run a rake task to setup themselves. They can now raise actionable errors to run the setup straight from the error pages. Here is how to define an actionable error: ```ruby class PendingMigrationError < MigrationError #:nodoc: include ActiveSupport::ActionableError action "Run pending migrations" do ActiveRecord::Tasks::DatabaseTasks.migrate end end ``` To make an error actionable, include the `ActiveSupport::ActionableError` module and invoke the `action` class macro to define the action. An action needs a name and a procedure to execute. The name is shown as the name of a button on the error pages. Once clicked, it will invoke the given procedure.
Diffstat (limited to 'activesupport/lib')
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/actionable_error.rb53
2 files changed, 54 insertions, 0 deletions
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 5589c71281..40a9181493 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -33,6 +33,7 @@ require "active_support/core_ext/date_and_time/compatibility"
module ActiveSupport
extend ActiveSupport::Autoload
+ autoload :ActionableError
autoload :Concern
autoload :CurrentAttributes
autoload :Dependencies
diff --git a/activesupport/lib/active_support/actionable_error.rb b/activesupport/lib/active_support/actionable_error.rb
new file mode 100644
index 0000000000..aeee2177aa
--- /dev/null
+++ b/activesupport/lib/active_support/actionable_error.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require "active_support/concern"
+
+module ActiveSupport
+ # Actionable errors let's you define actions to resolve an error.
+ #
+ # To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
+ # module and invoke the +action+ class macro to define the action.
+ #
+ # An action needs a name and a procedure to execute. The name can be shown by
+ # the action dispatching mechanism.
+ module ActionableError
+ extend Concern
+
+ NonActionable = Class.new(StandardError)
+
+ included do
+ class_attribute :_actions, default: Hash.new do |_, label|
+ raise NonActionable, "Cannot find action \"#{label}\" for #{self}"
+ end
+ end
+
+ def self.===(other) # :nodoc:
+ super || Module === other && other.ancestors.include?(self)
+ end
+
+ def self.actions(error) # :nodoc:
+ error = error.constantize if String === error
+ raise NonActionable, "#{error.name} is non-actionable" unless self === error
+ error._actions
+ end
+
+ def self.dispatch(error, label) # :nodoc:
+ actions(error)[label].call
+ end
+
+ module ClassMethods
+ # Defines an action that can resolve the error.
+ #
+ # class PendingMigrationError < MigrationError
+ # include ActiveSupport::ActionableError
+ #
+ # action "Run pending migrations" do
+ # ActiveRecord::Tasks::DatabaseTasks.migrate
+ # end
+ # end
+ def action(label, &block)
+ _actions[label] = block
+ end
+ end
+ end
+end