diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing/route_set.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 167 |
1 files changed, 48 insertions, 119 deletions
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index f3d7a234d7..5f54ea130b 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,6 +1,4 @@ require 'action_dispatch/journey' -require 'forwardable' -require 'thread_safe' require 'active_support/concern' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/hash/slice' @@ -21,65 +19,45 @@ module ActionDispatch alias inspect to_s class Dispatcher < Routing::Endpoint - def initialize(defaults) - @defaults = defaults - @controller_class_names = ThreadSafe::Cache.new + def initialize(raise_on_name_error) + @raise_on_name_error = raise_on_name_error end def dispatcher?; true; end def serve(req) - req.check_path_parameters! - params = req.path_parameters - - prepare_params!(params) - - # Just raise undefined constant errors if a controller was specified as default. - unless controller = controller(params, @defaults.key?(:controller)) + params = req.path_parameters + controller = controller req + res = controller.make_response! req + dispatch(controller, params[:action], req, res) + rescue NameError => e + if @raise_on_name_error + raise ActionController::RoutingError, e.message, e.backtrace + else return [404, {'X-Cascade' => 'pass'}, []] end - - dispatch(controller, params[:action], req.env) - end - - def prepare_params!(params) - normalize_controller!(params) - merge_default_action!(params) - end - - # If this is a default_controller (i.e. a controller specified by the user) - # we should raise an error in case it's not found, because it usually means - # a user error. However, if the controller was retrieved through a dynamic - # segment, as in :controller(/:action), we should simply return nil and - # delegate the control back to Rack cascade. Besides, if this is not a default - # controller, it means we should respect the @scope[:module] parameter. - def controller(params, default_controller=true) - if params && params.key?(:controller) - controller_param = params[:controller] - controller_reference(controller_param) - end - rescue NameError => e - raise ActionController::RoutingError, e.message, e.backtrace if default_controller end private - def controller_reference(controller_param) - const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller" - ActiveSupport::Dependencies.constantize(const_name) + def controller(req) + req.controller_class end - def dispatch(controller, action, env) - controller.action(action).call(env) + def dispatch(controller, action, req, res) + controller.dispatch(action, req, res) end + end - def normalize_controller!(params) - params[:controller] = params[:controller].underscore if params.key?(:controller) + class StaticDispatcher < Dispatcher + def initialize(controller_class) + super(false) + @controller_class = controller_class end - def merge_default_action!(params) - params[:action] ||= 'index' - end + private + + def controller(_); @controller_class; end end # A NamedRouteCollection instance is a collection of named routes, and also @@ -88,6 +66,7 @@ module ActionDispatch class NamedRouteCollection include Enumerable attr_reader :routes, :url_helpers_module, :path_helpers_module + private :routes def initialize @routes = {} @@ -142,6 +121,7 @@ module ActionDispatch end def key?(name) + return unless name routes.key? name.to_sym end @@ -199,9 +179,9 @@ module ActionDispatch private def optimized_helper(args) - params = parameterize_args(args) { |k| + params = parameterize_args(args) do raise_generation_error(args) - } + end @route.format params end @@ -267,7 +247,7 @@ module ActionDispatch path_params -= controller_options.keys path_params -= result.keys end - inner_options.each do |key, _| + inner_options.each_key do |key| path_params.delete(key) end @@ -355,7 +335,7 @@ module ActionDispatch @set = Journey::Routes.new @router = Journey::Router.new @set - @formatter = Journey::Formatter.new @set + @formatter = Journey::Formatter.new self end def relative_url_root @@ -370,6 +350,11 @@ module ActionDispatch ActionDispatch::Request end + def make_request(env) + request_class.new env + end + private :make_request + def draw(&block) clear! unless @disable_clear_and_finalize eval_block(block) @@ -413,10 +398,6 @@ module ActionDispatch @prepend.each { |blk| eval_block(blk) } end - def dispatcher(defaults) - Routing::RouteSet::Dispatcher.new(defaults) - end - module MountedHelpers extend ActiveSupport::Concern include UrlFor @@ -512,7 +493,7 @@ module ActionDispatch routes.empty? end - def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true) + def add_route(mapping, path_ast, name, anchor) raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i) if name && named_routes[name] @@ -523,66 +504,11 @@ module ActionDispatch "http://guides.rubyonrails.org/routing.html#restricting-the-routes-created" end - path = conditions.delete :path_info - ast = conditions.delete :parsed_path_info - required_defaults = conditions.delete :required_defaults - path = build_path(path, ast, requirements, anchor) - conditions = build_conditions(conditions) - - route = @set.add_route(app, path, conditions, required_defaults, defaults, name) + route = @set.add_route(name, mapping) named_routes[name] = route if name route end - def build_path(path, ast, requirements, anchor) - strexp = Journey::Router::Strexp.new( - ast, - path, - requirements, - SEPARATORS, - anchor) - - pattern = Journey::Path::Pattern.new(strexp) - - builder = Journey::GTG::Builder.new pattern.spec - - # Get all the symbol nodes followed by literals that are not the - # dummy node. - symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n| - builder.followpos(n).first.literal? - } - - # Get all the symbol nodes preceded by literals. - symbols.concat pattern.spec.find_all(&:literal?).map { |n| - builder.followpos(n).first - }.find_all(&:symbol?) - - symbols.each { |x| - x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/ - } - - pattern - end - private :build_path - - def build_conditions(current_conditions) - conditions = current_conditions.dup - - # Rack-Mount requires that :request_method be a regular expression. - # :request_method represents the HTTP verb that matches this route. - # - # Here we munge values before they get sent on to rack-mount. - verbs = conditions[:request_method] || [] - unless verbs.empty? - conditions[:request_method] = %r[^#{verbs.join('|')}$] - end - - conditions.keep_if do |k, _| - request_class.public_method_defined?(k) - end - end - private :build_conditions - class Generator PARAMETERIZE = lambda do |name, value| if name == :controller @@ -729,14 +655,18 @@ module ActionDispatch RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length, :trailing_slash, :anchor, :params, :only_path, :script_name, - :original_script_name] + :original_script_name, :relative_url_root] def optimize_routes_generation? default_url_options.empty? end def find_script_name(options) - options.delete(:script_name) || relative_url_root || '' + options.delete(:script_name) || find_relative_url_root(options) || '' + end + + def find_relative_url_root(options) + options.delete(:relative_url_root) || relative_url_root end def path_for(options, route_name = nil) @@ -782,7 +712,7 @@ module ActionDispatch end def call(env) - req = request_class.new(env) + req = make_request(env) req.path_info = Journey::Router::Utils.normalize_path(req.path_info) @router.serve(req) end @@ -798,7 +728,7 @@ module ActionDispatch raise ActionController::RoutingError, e.message end - req = request_class.new(env) + req = make_request(env) @router.recognize(req) do |route, params| params.merge!(extras) params.each do |key, value| @@ -811,14 +741,13 @@ module ActionDispatch req.path_parameters = old_params.merge params app = route.app if app.matches?(req) && app.dispatcher? - dispatcher = app.app - - if dispatcher.controller(params, false) - dispatcher.prepare_params!(params) - return params - else + begin + req.controller_class + rescue NameError raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller" end + + return req.path_parameters end end |