Class | ActionController::Routing::Route |
In: |
vendor/rails/actionpack/lib/action_controller/routing.rb
|
Parent: | Object |
conditions | [RW] | |
requirements | [RW] | |
segments | [RW] |
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 310 310: def initialize 311: @segments = [] 312: @requirements = {} 313: @conditions = {} 314: end
Generate the query string with any extra keys in the hash and append it to the given path, returning the new path.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 422 422: def append_query_string(path, hash, query_keys=nil) 423: return nil unless path 424: query_keys ||= extra_keys(hash) 425: "#{path}#{build_query_string(hash, query_keys)}" 426: end
Build a query string from the keys of the given hash. If only_keys is given (as an array), only the keys indicated will be used to build the query string. The query string will correctly build array parameter values.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 442 442: def build_query_string(hash, only_keys=nil) 443: elements = [] 444: 445: only_keys ||= hash.keys 446: 447: only_keys.each do |key| 448: value = hash[key] or next 449: key = CGI.escape key.to_s 450: if value.class == Array 451: key << '[]' 452: else 453: value = [ value ] 454: end 455: value.each { |val| elements << "#{key}=#{CGI.escape(val.to_param.to_s)}" } 456: end 457: 458: query_string = "?#{elements.join("&")}" unless elements.empty? 459: query_string || "" 460: end
Return a hash of key/value pairs representing the keys in the route that have defaults, or which are specified by non-regexp requirements.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 496 496: def defaults 497: @defaults ||= returning({}) do |hash| 498: segments.each do |segment| 499: next unless segment.respond_to? :default 500: hash[segment.key] = segment.default unless segment.default.nil? 501: end 502: requirements.each do |key,req| 503: next if Regexp === req || req.nil? 504: hash[key] = req 505: end 506: end 507: end
Determine which keys in the given hash are "extra". Extra keys are those that were not used to generate a particular route. The extra keys also do not include those recalled from the prior request, nor do they include any keys that were implied in the route (like a :controller that is required, but not explicitly used in the text of the route.)
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 434 434: def extra_keys(hash, recall={}) 435: (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys 436: end
Write the real generation implementation and then resend the message.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 410 410: def generate(options, hash, expire_on = {}) 411: write_generation 412: generate options, hash, expire_on 413: end
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 415 415: def generate_extras(options, hash, expire_on = {}) 416: write_generation 417: generate_extras options, hash, expire_on 418: 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.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 345 345: def generation_extraction 346: segments.collect do |segment| 347: segment.extraction_code 348: end.compact * "\n" 349: end
Produce a condition expression that will check the requirements of this route upon generation.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 353 353: def generation_requirements 354: requirement_conditions = requirements.collect do |key, req| 355: if req.is_a? Regexp 356: value_regexp = Regexp.new "\\A#{req.source}\\Z" 357: "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]" 358: else 359: "hash[:#{key}] == #{req.inspect}" 360: end 361: end 362: requirement_conditions * ' && ' unless requirement_conditions.empty? 363: end
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 364 364: def generation_structure 365: segments.last.string_structure segments[0..-2] 366: end
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 509 509: def matches_controller_and_action?(controller, action) 510: unless @matching_prepared 511: @controller_requirement = requirement_for(:controller) 512: @action_requirement = requirement_for(:action) 513: @matching_prepared = true 514: end 515: 516: (@controller_requirement.nil? || @controller_requirement === controller) && 517: (@action_requirement.nil? || @action_requirement === action) 518: 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+/
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 475 475: def parameter_shell 476: @parameter_shell ||= returning({}) do |shell| 477: requirements.each do |key, requirement| 478: shell[key] = requirement unless requirement.is_a? Regexp 479: end 480: end 481: end
Plugins may override this method to add other conditions, like checks on host, subdomain, and so forth. Note that changes here only affect route recognition, not generation.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 383 383: def recognition_conditions 384: result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"] 385: result << "conditions[:method] === env[:method]" if conditions[:method] 386: result 387: end
Write the code to extract the parameters from a matched route.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 399 399: def recognition_extraction 400: next_capture = 1 401: extraction = segments.collect do |segment| 402: x = segment.match_extraction next_capture 403: next_capture += Regexp.new(segment.regexp_chunk).number_of_captures 404: x 405: end 406: extraction.compact 407: end
Build the regular expression pattern that will match this route.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 390 390: def recognition_pattern(wrap = true) 391: pattern = '' 392: segments.reverse_each do |segment| 393: pattern = segment.build_pattern pattern 394: end 395: wrap ? ("\\A" + pattern + "\\Z") : pattern 396: end
Write the real recognition implementation and then resend the message.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 463 463: def recognize(path, environment={}) 464: write_recognition 465: recognize path, environment 466: 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.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 486 486: def significant_keys 487: @significant_keys ||= returning [] do |sk| 488: segments.each { |segment| sk << segment.key if segment.respond_to? :key } 489: sk.concat requirements.keys 490: sk.uniq! 491: end 492: end
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 520 520: def to_s 521: @to_s ||= begin 522: segs = segments.inject("") { |str,s| str << s.to_s } 523: "%-6s %-40s %s" % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect] 524: end 525: end
Write and compile a generate method for this Route.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 317 317: def write_generation 318: # Build the main body of the generation 319: body = "not_expired = true\n#{generation_extraction}\n#{generation_structure}" 320: 321: # If we have conditions that must be tested first, nest the body inside an if 322: body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements 323: args = "options, hash, expire_on = {}" 324: 325: # Nest the body inside of a def block, and then compile it. 326: raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend" 327: instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" 328: 329: # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash 330: # are the same as the keys that were recalled from the previous request. Thus, 331: # we can use the expire_on.keys to determine which keys ought to be used to build 332: # the query string. (Never use keys from the recalled request when building the 333: # query string.) 334: 335: method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(hash, expire_on))\nend" 336: instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" 337: 338: method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(hash, expire_on)]\nend" 339: instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" 340: raw_method 341: end
Write and compile a recognize method for this Route.
# File vendor/rails/actionpack/lib/action_controller/routing.rb, line 369 369: def write_recognition 370: # Create an if structure to extract the params from a match if it occurs. 371: body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams" 372: body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend" 373: 374: # Build the method declaration and compile it 375: method_decl = "def recognize(path, env={})\n#{body}\nend" 376: instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" 377: method_decl 378: end