From 7464a398e18592010243845edad1fa09b82d16a1 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 6 Nov 2007 08:09:39 +0000 Subject: Minor documentation enhancements and white-space fixes. Closes #9819 [chuyeow] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8082 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/lib/action_controller/routing.rb | 288 ++++++++++++++-------------- 1 file changed, 144 insertions(+), 144 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index 78b1759e3f..a182fb3dd5 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -31,7 +31,7 @@ class Regexp #:nodoc: def number_of_captures Regexp.new("|#{source}").match('').captures.length end - + class << self def optionalize(pattern) case unoptionalize(pattern) @@ -39,7 +39,7 @@ class Regexp #:nodoc: else "(?:#{pattern})?" end end - + def unoptionalize(pattern) [/\A\(\?:(.*)\)\?\Z/, /\A(.|\(.*\))\?\Z/].each do |regexp| return $1 if regexp =~ pattern @@ -50,30 +50,30 @@ class Regexp #:nodoc: end module ActionController - # == Routing + # == Routing # # The routing module provides URL rewriting in native Ruby. It's a way to # redirect incoming requests to controllers and actions. This replaces - # mod_rewrite rules. Best of all Rails' Routing works with any web server. + # mod_rewrite rules. Best of all, Rails' Routing works with any web server. # Routes are defined in routes.rb in your RAILS_ROOT/config directory. # - # Consider the following route, installed by Rails when you generate your + # Consider the following route, installed by Rails when you generate your # application: # # map.connect ':controller/:action/:id' # - # This route states that it expects requests to consist of a - # :controller followed by an :action that in turns is fed by some :id + # This route states that it expects requests to consist of a + # :controller followed by an :action that in turn is fed some :id. # - # Suppose you get an incoming request for /blog/edit/22, you'll end up + # Suppose you get an incoming request for /blog/edit/22, you'll end up # with: # # params = { :controller => 'blog', - # :action => 'edit' + # :action => 'edit', # :id => '22' # } # - # Think of creating routes as drawing a map for your requests. The map tells + # Think of creating routes as drawing a map for your requests. The map tells # them where to go based on some predefined pattern: # # ActionController::Routing::Routes.draw do |map| @@ -86,45 +86,45 @@ module ActionController # # :controller maps to your controller name # :action maps to an action with your controllers - # + # # Other names simply map to a parameter as in the case of +:id+. - # + # # == Route priority # - # Not all routes are created equally. Routes have priority defined by the + # Not all routes are created equally. Routes have priority defined by the # order of appearance of the routes in the routes.rb file. The priority goes # from top to bottom. The last route in that file is at the lowest priority - # will be applied last. If no route matches, 404 is returned. + # and will be applied last. If no route matches, 404 is returned. # - # Within blocks, the empty pattern goes first i.e. is at the highest priority. + # Within blocks, the empty pattern is at the highest priority. # In practice this works out nicely: # - # ActionController::Routing::Routes.draw do |map| + # ActionController::Routing::Routes.draw do |map| # map.with_options :controller => 'blog' do |blog| - # blog.show '', :action => 'list' + # blog.show '', :action => 'list' # end - # map.connect ':controller/:action/:view + # map.connect ':controller/:action/:view' # end # - # In this case, invoking blog controller (with an URL like '/blog/') + # In this case, invoking blog controller (with an URL like '/blog/') # without parameters will activate the 'list' action by default. # # == Defaults routes and default parameters # - # Setting a default route is straightforward in Rails because by appending a - # Hash to the end of your mapping you can set default parameters. + # Setting a default route is straightforward in Rails - you simply append a + # Hash at the end of your mapping to set any default parameters. # # Example: # ActionController::Routing:Routes.draw do |map| # map.connect ':controller/:action/:id', :controller => 'blog' # end # - # This sets up +blog+ as the default controller if no other is specified. + # This sets up +blog+ as the default controller if no other is specified. # This means visiting '/' would invoke the blog controller. # # More formally, you can define defaults in a route with the +:defaults+ key. - # - # map.connect ':controller/:id/:action', :action => 'show', :defaults => { :page => 'Dashboard' } + # + # map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' } # # == Named routes # @@ -143,7 +143,7 @@ module ActionController # # redirect_to show_item_path(:id => 25) # - # Use map.root as a shorthand to name a route for the root path "" + # Use map.root as a shorthand to name a route for the root path "". # # # In routes.rb # map.root :controller => 'blogs' @@ -166,32 +166,32 @@ module ActionController # end # # # provides named routes for show, delete, and edit - # link_to @article.title, show_path(:id => @article.id) + # link_to @article.title, show_path(:id => @article.id) # # == Pretty URLs # # Routes can generate pretty URLs. For example: # # map.connect 'articles/:year/:month/:day', - # :controller => 'articles', + # :controller => 'articles', # :action => 'find_by_date', # :year => /\d{4}/, - # :month => /\d{1,2}/, - # :day => /\d{1,2}/ - # + # :month => /\d{1,2}/, + # :day => /\d{1,2}/ + # # # Using the route above, the url below maps to: # # params = {:year => '2005', :month => '11', :day => '06'} # # http://localhost:3000/articles/2005/11/06 # # == Regular Expressions and parameters - # You can specify a reqular expression to define a format for a parameter. + # You can specify a regular expression to define a format for a parameter. # # map.geocode 'geocode/:postalcode', :controller => 'geocode', # :action => 'show', :postalcode => /\d{5}(-\d{4})?/ # # or, more formally: # - # map.geocode 'geocode/:postalcode', :controller => 'geocode', + # map.geocode 'geocode/:postalcode', :controller => 'geocode', # :action => 'show', :requirements => { :postalcode => /\d{5}(-\d{4})?/ } # # == Route globbing @@ -218,23 +218,23 @@ module ActionController # The two main methods for testing your routes: # # === +assert_routing+ - # + # # def test_movie_route_properly_splits # opts = {:controller => "plugin", :action => "checkout", :id => "2"} # assert_routing "plugin/checkout/2", opts # end - # + # # +assert_routing+ lets you test whether or not the route properly resolves into options. # # === +assert_recognizes+ # # def test_route_has_options # opts = {:controller => "plugin", :action => "show", :id => "12"} - # assert_recognizes opts, "/plugins/show/12" + # assert_recognizes opts, "/plugins/show/12" # end - # + # # Note the subtle difference between the two: +assert_routing+ tests that - # an URL fits options while +assert_recognizes+ tests that an URL + # a URL fits options while +assert_recognizes+ tests that a URL # breaks into parameters properly. # # In tests you can simply pass the URL or named route to +get+ or +post+. @@ -258,12 +258,12 @@ module ActionController # The root paths which may contain controller files mattr_accessor :controller_paths self.controller_paths = [] - + # A helper module to hold URL related helpers. module Helpers include PolymorphicRoutes end - + class << self def with_controllers(names) prior_controllers = @possible_controllers @@ -294,16 +294,16 @@ module ActionController def possible_controllers unless @possible_controllers @possible_controllers = [] - + paths = controller_paths.select { |path| File.directory?(path) && path != "." } seen_paths = Hash.new {|h, k| h[k] = true; false} normalize_paths(paths).each do |load_path| Dir["#{load_path}/**/*_controller.rb"].collect do |path| next if seen_paths[path.gsub(%r{^\.[/\\]}, "")] - + controller_name = path[(load_path.length + 1)..-1] - + controller_name.gsub!(/_controller\.rb\Z/, '') @possible_controllers << controller_name end @@ -324,36 +324,36 @@ module ActionController elsif controller[0] == ?/ then controller[1..-1] elsif %r{^(.*)/} =~ previous then "#{$1}/#{controller}" else controller - end - end + end + end end - + class Route #:nodoc: attr_accessor :segments, :requirements, :conditions, :optimise - + def initialize @segments = [] @requirements = {} @conditions = {} end - + # Indicates whether the routes should be optimised with the string interpolation # version of the named routes methods. def optimise? @optimise && ActionController::Base::optimise_named_routes end - + def segment_keys segments.collect do |segment| segment.key if segment.respond_to? :key end.compact end - + # Write and compile a +generate+ method for this Route. def write_generation # Build the main body of the generation body = "expired = false\n#{generation_extraction}\n#{generation_structure}" - + # If we have conditions that must be tested first, nest the body inside an if body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements args = "options, hash, expire_on = {}" @@ -375,7 +375,7 @@ module ActionController instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" raw_method end - + # Build several lines of code that extract values from the options hash. If any # of the values are missing or rejected then a return will be executed. def generation_extraction @@ -383,7 +383,7 @@ module ActionController segment.extraction_code end.compact * "\n" end - + # Produce a condition expression that will check the requirements of this route # upon generation. def generation_requirements @@ -397,17 +397,17 @@ module ActionController end requirement_conditions * ' && ' unless requirement_conditions.empty? end - + def generation_structure segments.last.string_structure segments[0..-2] end - + # Write and compile a +recognize+ method for this Route. def write_recognition # Create an if structure to extract the params from a match if it occurs. body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams" body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend" - + # Build the method declaration and compile it method_decl = "def recognize(path, env={})\n#{body}\nend" instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" @@ -431,7 +431,7 @@ module ActionController end wrap ? ("\\A" + pattern + "\\Z") : pattern end - + # Write the code to extract the parameters from a matched route. def recognition_extraction next_capture = 1 @@ -442,7 +442,7 @@ module ActionController end extraction.compact end - + # Write the real generation implementation and then resend the message. def generate(options, hash, expire_on = {}) write_generation @@ -493,14 +493,14 @@ module ActionController write_recognition recognize path, environment end - + # A route's parameter shell contains parameter values that are not in the # route's path, but should be placed in the recognized hash. - # + # # For example, +{:controller => 'pages', :action => 'show'} is the shell for the route: - # + # # map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/ - # + # def parameter_shell @parameter_shell ||= returning({}) do |shell| requirements.each do |key, requirement| @@ -508,7 +508,7 @@ module ActionController end end end - + # Return an array containing all the keys that are used in this route. This # includes keys that appear inside the path, and keys that have requirements # placed upon them. @@ -552,7 +552,7 @@ module ActionController "%-6s %-40s %s" % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect] end end - + protected def requirement_for(key) return requirements[key] if requirements.key? key @@ -599,26 +599,26 @@ module ActionController chunks << interpolation_chunk "\"#{chunks * ''}\"#{all_optionals_available_condition(prior_segments)}" end - + def string_structure(prior_segments) optional? ? continue_string_structure(prior_segments) : interpolation_statement(prior_segments) end - + # Return an if condition that is true if all the prior segments can be generated. # If there are no optional segments before this one, then nil is returned. def all_optionals_available_condition(prior_segments) optional_locals = prior_segments.collect { |s| s.local_name if s.optional? && s.respond_to?(:local_name) }.compact optional_locals.empty? ? nil : " if #{optional_locals * ' && '}" end - + # Recognition - + def match_extraction(next_capture) nil end - + # Warning - + # Returns true if this segment is optional? because of a default. If so, then # no warning will be emitted regarding this segment. def optionality_implied? @@ -629,21 +629,21 @@ module ActionController class StaticSegment < Segment #:nodoc: attr_accessor :value, :raw alias_method :raw?, :raw - + def initialize(value = nil) super() self.value = value end - + def interpolation_chunk raw? ? value : super end - + def regexp_chunk chunk = Regexp.escape(value) optional? ? Regexp.optionalize(chunk) : chunk end - + def build_pattern(pattern) escaped = Regexp.escape(value) if optional? && ! pattern.empty? @@ -654,7 +654,7 @@ module ActionController escaped + pattern end end - + def to_s value end @@ -666,7 +666,7 @@ module ActionController self.raw = true self.is_optional = true end - + def optionality_implied? true end @@ -674,23 +674,23 @@ module ActionController class DynamicSegment < Segment #:nodoc: attr_accessor :key, :default, :regexp - + def initialize(key = nil, options = {}) super() self.key = key self.default = options[:default] if options.key? :default self.is_optional = true if options[:optional] || options.key?(:default) end - + def to_s ":#{key}" end - + # The local variable name that the value of this segment will be extracted to. def local_name "#{key}_value" end - + def extract_value "#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}" end @@ -708,39 +708,39 @@ module ActionController def expiry_statement "expired, hash = true, options if !expired && expire_on[:#{key}]" end - + def extraction_code s = extract_value vc = value_check s << "\nreturn [nil,nil] unless #{vc}" if vc s << "\n#{expiry_statement}" end - + def interpolation_chunk(value_code = "#{local_name}") "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}" end - + def string_structure(prior_segments) if optional? # We have a conditional to do... # If we should not appear in the url, just write the code for the prior # segments. This occurs if our value is the default value, or, if we are # optional, if we have nil as our value. - "if #{local_name} == #{default.inspect}\n" + - continue_string_structure(prior_segments) + + "if #{local_name} == #{default.inspect}\n" + + continue_string_structure(prior_segments) + "\nelse\n" + # Otherwise, write the code up to here "#{interpolation_statement(prior_segments)}\nend" else interpolation_statement(prior_segments) end end - + def value_regexp Regexp.new "\\A#{regexp.source}\\Z" if regexp end def regexp_chunk regexp ? "(#{regexp.source})" : "([^#{Routing::SEPARATORS.join}]+)" end - + def build_pattern(pattern) chunk = regexp_chunk chunk = "(#{chunk})" if Regexp.new(chunk).number_of_captures == 0 @@ -760,11 +760,11 @@ module ActionController params[:#{key}] = value if value ] end - + def optionality_implied? [:action, :id].include? key end - + end class ControllerSegment < DynamicSegment #:nodoc: @@ -822,29 +822,29 @@ module ActionController end class Result < ::Array #:nodoc: - def to_s() join '/' end + def to_s() join '/' end def self.new_escaped(strings) new strings.collect {|str| URI.unescape str} - end + end end end class RouteBuilder #:nodoc: attr_accessor :separators, :optional_separators - + def initialize self.separators = Routing::SEPARATORS self.optional_separators = %w( / ) end - + def separator_pattern(inverted = false) "[#{'^' if inverted}#{Regexp.escape(separators.join)}]" end - + def interval_regexp Regexp.new "(.*?)(#{separators.source}|$)" end - + # Accepts a "route path" (a string defining a route), and returns the array # of segments that corresponds to it. Note that the segment array is only # partially initialized--the defaults and requirements, for instance, need @@ -853,7 +853,7 @@ module ActionController # #assign_route_options is called, as well. def segments_for_route_path(path) rest, segments = path, [] - + until rest.empty? segment, rest = segment_for rest segments << segment @@ -884,20 +884,20 @@ module ActionController end [segment, $~.post_match] end - + # Split the given hash of options into requirement and default hashes. The # segments are passed alongside in order to distinguish between default values # and requirements. def divide_route_options(segments, options) options = options.dup - + if options[:namespace] options[:controller] = "#{options[:path_prefix]}/#{options[:controller]}" options.delete(:path_prefix) options.delete(:name_prefix) options.delete(:namespace) - end - + end + requirements = (options.delete(:requirements) || {}).dup defaults = (options.delete(:defaults) || {}).dup conditions = (options.delete(:conditions) || {}).dup @@ -907,20 +907,20 @@ module ActionController hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements hash[key] = value end - + [defaults, requirements, conditions] end - + # Takes a hash of defaults and a hash of requirements, and assigns them to # the segments. Any unused requirements (which do not correspond to a segment) # are returned as a hash. def assign_route_options(segments, defaults, requirements) route_requirements = {} # Requirements that do not belong to a segment - + segment_named = Proc.new do |key| segments.detect { |segment| segment.key == key if segment.respond_to?(:key) } end - + requirements.each do |key, requirement| segment = segment_named[key] if segment @@ -933,19 +933,19 @@ module ActionController route_requirements[key] = requirement end end - + defaults.each do |key, default| segment = segment_named[key] raise ArgumentError, "#{key}: No matching segment exists; cannot assign default" unless segment segment.is_optional = true segment.default = default.to_param if default end - + assign_default_route_options(segments) ensure_required_segments(segments) route_requirements end - + # Assign default options, such as 'index' as a default for :action. This # method must be run *after* user supplied requirements and defaults have # been applied to the segments. @@ -965,7 +965,7 @@ module ActionController end end end - + # Makes sure that there are no optional segments that precede a required # segment. If any are found that precede a required segment, they are # made required. @@ -984,15 +984,15 @@ module ActionController end end end - + # Construct and return a route with the given path and options. def build(path, options) # Wrap the path with slashes path = "/#{path}" unless path[0] == ?/ - path = "#{path}/" unless path[-1] == ?/ - + path = "#{path}/" unless path[-1] == ?/ + path = "/#{options[:path_prefix]}#{path}" if options[:path_prefix] - + segments = segments_for_route_path(path) defaults, requirements, conditions = divide_route_options(segments, options) requirements = assign_route_options(segments, defaults, requirements) @@ -1002,11 +1002,11 @@ module ActionController route.segments = segments route.requirements = requirements route.conditions = conditions - + # Routes cannot use the current string interpolation method # if there are user-supplied :requirements as the interpolation # code won't raise RoutingErrors when generating - route.optimise = !options.key?(:requirements) + route.optimise = !options.key?(:requirements) if !route.significant_keys.include?(:action) && !route.requirements[:action] route.requirements[:action] = "index" route.significant_keys << :action @@ -1023,15 +1023,15 @@ module ActionController class RouteSet #:nodoc: # Mapper instances are used to build routes. The object passed to the draw # block in config/routes.rb is a Mapper instance. - # + # # Mapper instances have relatively few instance methods, in order to avoid # clashes with named routes. class Mapper #:nodoc: def initialize(set) @set = set end - - # Create an unnamed route with the provided +path+ and +options+. See + + # Create an unnamed route with the provided +path+ and +options+. See # SomeHelpfulUrl for an introduction to routes. def connect(path, options = {}) @set.add_route(path, options) @@ -1045,7 +1045,7 @@ module ActionController def named_route(name, path, options = {}) @set.add_named_route(name, path, options) end - + # Enables the use of resources in a module by setting the name_prefix, path_prefix, and namespace for the model. # Example: # @@ -1064,7 +1064,7 @@ module ActionController with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block) end end - + def method_missing(route_name, *args, &proc) super unless args.length >= 1 && proc.nil? @@ -1087,7 +1087,7 @@ module ActionController def clear! @routes = {} @helpers = [] - + @module ||= Module.new @module.instance_methods.each do |selector| @module.class_eval { remove_method selector } @@ -1151,7 +1151,7 @@ module ActionController define_url_helper route, name, kind, hash end end - + def define_hash_access(route, name, kind, options) selector = hash_access_name(name, kind) @module.module_eval <<-end_eval # We use module_eval to avoid leaks @@ -1206,12 +1206,12 @@ module ActionController end attr_accessor :routes, :named_routes - + def initialize self.routes = [] self.named_routes = NamedRouteCollection.new end - + # Subclasses and plugins may override this method to specify a different # RouteBuilder instance, so that other route DSL's can be created. def builder @@ -1223,14 +1223,14 @@ module ActionController yield Mapper.new(self) install_helpers end - + def clear! routes.clear named_routes.clear @combined_regexp = nil @routes_by_controller = nil 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) @@ -1239,7 +1239,7 @@ module ActionController def empty? routes.empty? end - + def load! Routing.use_controllers! nil # Clear the controller cache so we may discover new ones clear! @@ -1249,7 +1249,7 @@ module ActionController # reload! will always force a reload whereas load checks the timestamp first alias reload! load! - + def reload if @routes_last_modified && defined?(RAILS_ROOT) mtime = File.stat("#{RAILS_ROOT}/config/routes.rb").mtime @@ -1260,7 +1260,7 @@ module ActionController end load! end - + def load_routes! if defined?(RAILS_ROOT) && defined?(::ActionController::Routing::Routes) && self == ::ActionController::Routing::Routes load File.join("#{RAILS_ROOT}/config/routes.rb") @@ -1269,19 +1269,19 @@ module ActionController add_route ":controller/:action/:id" end end - + def add_route(path, options = {}) route = builder.build(path, options) routes << route route end - + def add_named_route(name, path, options = {}) # TODO - is options EVER used? name = options[:name_prefix] + name.to_s if options[:name_prefix] named_routes[name.to_sym] = add_route(path, options) end - + def options_as_params(options) # If an explicit :controller was given, always make :action explicit # too, so that action expiry works as expected for things like @@ -1299,7 +1299,7 @@ module ActionController options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action] options_as_params end - + def build_expiry(options, recall) recall.inject({}) do |expiry, (key, recalled_value)| expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param) @@ -1348,7 +1348,7 @@ module ActionController if named_route path = named_route.generate(options, merged, expire_on) - if path.nil? + if path.nil? raise_named_route_error(options, named_route, named_route_name) else return path @@ -1356,19 +1356,19 @@ module ActionController else merged[:action] ||= 'index' options[:action] ||= 'index' - + controller = merged[:controller] action = merged[:action] raise RoutingError, "Need controller and action!" unless controller && action - + if generate_all # Used by caching to expire all paths for a resource return routes.collect do |route| route.send!(method, options, merged, expire_on) end.compact end - + # don't use the recalled keys when determining which routes to check routes = routes_by_controller[controller][action][options.keys.sort_by { |x| x.object_id }] @@ -1377,10 +1377,10 @@ module ActionController return results if results && (!results.is_a?(Array) || results.first) end end - + raise RoutingError, "No route matches #{options.inspect}" end - + # try to give a helpful error message when named route generation fails def raise_named_route_error(options, named_route, named_route_name) diff = named_route.requirements.diff(options) @@ -1392,13 +1392,13 @@ module ActionController raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisfied?" end end - + def recognize(request) params = recognize_path(request.path, extract_request_environment(request)) request.path_parameters = params.with_indifferent_access "#{params[:controller].camelize}Controller".constantize end - + def recognize_path(path, environment={}) routes.each do |route| result = route.recognize(path, environment) and return result @@ -1414,7 +1414,7 @@ module ActionController raise RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" end end - + def routes_by_controller @routes_by_controller ||= Hash.new do |controller_hash, controller| controller_hash[controller] = Hash.new do |action_hash, action| @@ -1424,23 +1424,23 @@ module ActionController end end end - + def routes_for(options, merged, expire_on) raise "Need controller and action!" unless controller && action controller = merged[:controller] merged = options if expire_on[:controller] action = merged[:action] || 'index' - + routes_by_controller[controller][action][merged.keys] end - + def routes_for_controller_and_action(controller, action) selected = routes.select do |route| route.matches_controller_and_action? controller, action end (selected.length == routes.length) ? routes : selected end - + def routes_for_controller_and_action_and_keys(controller, action, keys) selected = routes.select do |route| route.matches_controller_and_action? controller, action -- cgit v1.2.3