115 lines
3.2 KiB
Ruby
115 lines
3.2 KiB
Ruby
require 'active_support/core_ext/module/attr_internal'
|
|
|
|
module AbstractController
|
|
class Error < StandardError; end
|
|
|
|
class DoubleRenderError < Error
|
|
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
|
|
|
|
def initialize(message = nil)
|
|
super(message || DEFAULT_MESSAGE)
|
|
end
|
|
end
|
|
|
|
class Base
|
|
|
|
attr_internal :response_body
|
|
attr_internal :response_obj
|
|
attr_internal :action_name
|
|
|
|
class << self
|
|
attr_reader :abstract
|
|
|
|
def abstract!
|
|
@abstract = true
|
|
end
|
|
|
|
alias_method :abstract?, :abstract
|
|
|
|
def inherited(klass)
|
|
::AbstractController::Base.subclasses << klass.to_s
|
|
super
|
|
end
|
|
|
|
def subclasses
|
|
@subclasses ||= []
|
|
end
|
|
|
|
def internal_methods
|
|
controller = self
|
|
controller = controller.superclass until controller.abstract?
|
|
controller.public_instance_methods(true)
|
|
end
|
|
|
|
def process(action)
|
|
new.process(action.to_s)
|
|
end
|
|
|
|
def hidden_actions
|
|
[]
|
|
end
|
|
|
|
def action_methods
|
|
@action_methods ||=
|
|
# All public instance methods of this class, including ancestors
|
|
public_instance_methods(true).map { |m| m.to_s }.to_set -
|
|
# Except for public instance methods of Base and its ancestors
|
|
internal_methods.map { |m| m.to_s } +
|
|
# Be sure to include shadowed public instance methods of this class
|
|
public_instance_methods(false).map { |m| m.to_s } -
|
|
# And always exclude explicitly hidden actions
|
|
hidden_actions
|
|
end
|
|
end
|
|
|
|
abstract!
|
|
|
|
def initialize
|
|
self.response_obj = {}
|
|
end
|
|
|
|
def process(action)
|
|
@_action_name = action_name = action.to_s
|
|
|
|
unless action_name = method_for_action(action_name)
|
|
raise ActionNotFound, "The action '#{action}' could not be found"
|
|
end
|
|
|
|
process_action(action_name)
|
|
self
|
|
end
|
|
|
|
private
|
|
|
|
def action_methods
|
|
self.class.action_methods
|
|
end
|
|
|
|
def action_method?(action)
|
|
action_methods.include?(action)
|
|
end
|
|
|
|
# It is possible for respond_to?(action_name) to be false and
|
|
# respond_to?(:action_missing) to be false if respond_to_action?
|
|
# is overridden in a subclass. For instance, ActionController::Base
|
|
# overrides it to include the case where a template matching the
|
|
# action_name is found.
|
|
def process_action(method_name)
|
|
send(method_name)
|
|
end
|
|
|
|
def _handle_action_missing
|
|
action_missing(@_action_name)
|
|
end
|
|
|
|
# Override this to change the conditions that will raise an
|
|
# ActionNotFound error. If you accept a difference case,
|
|
# you must handle it by also overriding process_action and
|
|
# handling the case.
|
|
def method_for_action(action_name)
|
|
if action_method?(action_name) then action_name
|
|
elsif respond_to?(:action_missing, true) then "_handle_action_missing"
|
|
end
|
|
end
|
|
end
|
|
end |