diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing/route_set.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 129 |
1 files changed, 70 insertions, 59 deletions
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 46a68a32ae..ac4dd7d927 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,4 +1,4 @@ -require 'journey/router' +require 'journey' require 'forwardable' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/to_query' @@ -37,7 +37,7 @@ module ActionDispatch # 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 - # an user error. However, if the controller was retrieved through a dynamic + # 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. @@ -83,7 +83,9 @@ module ActionDispatch attr_reader :routes, :helpers, :module def initialize - clear! + @routes = {} + @helpers = [] + @module = Module.new end def helper_names @@ -91,12 +93,8 @@ module ActionDispatch end def clear! - @routes = {} - @helpers = [] - - @module ||= Module.new do - instance_methods.each { |selector| remove_method(selector) } - end + @routes.clear + @helpers.clear end def add(name, route) @@ -125,21 +123,6 @@ module ActionDispatch routes.length end - def reset! - old_routes = routes.dup - clear! - old_routes.each do |name, route| - add(name, route) - end - end - - def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false) - reset! if regenerate - Array(destinations).each do |dest| - dest.__send__(:include, @module) - end - end - private def url_helper_name(name, kind = :url) :"#{name}_#{kind}" @@ -160,21 +143,23 @@ module ActionDispatch def define_hash_access(route, name, kind, options) selector = hash_access_name(name, kind) - # We use module_eval to avoid leaks - @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - remove_possible_method :#{selector} - def #{selector}(*args) - options = args.extract_options! + @module.module_eval do + remove_possible_method selector + + define_method(selector) do |*args| + inner_options = args.extract_options! + result = options.dup if args.any? - options[:_positional_args] = args - options[:_positional_keys] = #{route.segment_keys.inspect} + result[:_positional_args] = args + result[:_positional_keys] = route.segment_keys end - options ? #{options.inspect}.merge(options) : #{options.inspect} + result.merge(inner_options) end - protected :#{selector} - END_EVAL + + protected selector + end helpers << selector end @@ -286,14 +271,15 @@ module ActionDispatch @prepend.each { |blk| eval_block(blk) } end - def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false) - Array(destinations).each { |d| d.module_eval { include Helpers } } - named_routes.install(destinations, regenerate_code) - end - - module MountedHelpers + module MountedHelpers #:nodoc: + extend ActiveSupport::Concern + include UrlFor end + # Contains all the mounted helpers accross different + # engines and the `main_app` helper for the application. + # You can include this in your classes if you want to + # access routes for other engines. def mounted_helpers MountedHelpers end @@ -304,7 +290,7 @@ module ActionDispatch routes = self MountedHelpers.class_eval do define_method "_#{name}" do - RoutesProxy.new(routes, self._routes_context) + RoutesProxy.new(routes, _routes_context) end end @@ -319,28 +305,36 @@ module ActionDispatch @url_helpers ||= begin routes = self - helpers = Module.new do + Module.new do extend ActiveSupport::Concern include UrlFor + # Define url_for in the singleton level so one can do: + # Rails.application.routes.url_helpers.url_for(args) @_routes = routes class << self delegate :url_for, :to => '@_routes' end + + # Make named_routes available in the module singleton + # as well, so one can do: + # Rails.application.routes.url_helpers.posts_path extend routes.named_routes.module - # ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that - # we can include? - # Yes plz - JP + # Any class that includes this module will get all + # named routes... + include routes.named_routes.module + + # plus a singleton class method called _routes ... included do - routes.install_helpers(self) singleton_class.send(:redefine_method, :_routes) { routes } end + # And an instance method _routes. Note that + # UrlFor (included in this module) add extra + # conveniences for working with @_routes. define_method(:_routes) { @_routes || routes } end - - helpers end end @@ -355,7 +349,7 @@ module ActionDispatch conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym }) route = @set.add_route(app, path, conditions, defaults, name) - named_routes[name] = route if name + named_routes[name] = route if name && !named_routes[name] route end @@ -366,7 +360,26 @@ module ActionDispatch SEPARATORS, anchor) - Journey::Path::Pattern.new(strexp) + 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 @@ -393,10 +406,9 @@ module ActionDispatch if name == :controller value elsif value.is_a?(Array) - value.map { |v| Journey::Router::Utils.escape_uri(v.to_param) }.join('/') - else - return nil unless param = value.to_param - param.split('/').map { |v| Journey::Router::Utils.escape_uri(v) }.join("/") + value.map { |v| v.to_param }.join('/') + elsif param = value.to_param + param end end @@ -540,7 +552,6 @@ module ActionDispatch end def url_for(options) - finalize! options = (options || {}).reverse_merge!(default_url_options) handle_positional_args(options) @@ -557,7 +568,7 @@ module ActionDispatch path_addition, params = generate(path_options, path_segments || {}) path << path_addition - ActionDispatch::Http::URL.url_for(options.merge({ + ActionDispatch::Http::URL.url_for(options.merge!({ :path => path, :params => params, :user => user, @@ -566,7 +577,6 @@ module ActionDispatch end def call(env) - finalize! @router.call(env) end @@ -584,11 +594,12 @@ module ActionDispatch @router.recognize(req) do |route, matches, params| params.each do |key, value| if value.is_a?(String) - value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware? + value = value.dup.force_encoding(Encoding::BINARY) params[key] = URI.parser.unescape(value) end end - + old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params) dispatcher = route.app while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do dispatcher = dispatcher.app |