Permalink
Browse files

New routes implementation. Simpler, faster, easier to understand. The…

… published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4394 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 74b7bfa commit b20c575ac02373723438468932ceddd97056c9ec @jamis jamis committed Jun 1, 2006
View
@@ -1,5 +1,11 @@
*SVN*
+* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. [Nicholas Seckar, Jamis Buck]
+
+ map.connect '/foo/:id', :controller => '...', :action => '...'
+ map.connect '/foo/:id.:format', :controller => '...', :action => '...'
+ map.connect '/foo/:id', ..., :conditions => { :method => :get }
+
* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper]
* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com]
@@ -37,7 +43,6 @@
All this relies on the fact that you have a route that includes .:format.
-
* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [DHH]
* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [DHH]
@@ -189,7 +189,7 @@ def assert_generates(expected_path, options, defaults={}, extras = {}, message=n
# Load routes.rb if it hasn't been loaded.
ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
- generated_path, extra_keys = ActionController::Routing::Routes.generate(options, extras)
+ generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, extras)
found_extras = options.reject {|k, v| ! extra_keys.include? k}
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
@@ -365,7 +365,8 @@ def recognized_request_for(path, request_method = nil)
request = ActionController::TestRequest.new({}, {}, nil)
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
request.path = path
- ActionController::Routing::Routes.recognize!(request)
+
+ ActionController::Routing::Routes.recognize(request)
request
end
@@ -2,7 +2,6 @@
require 'action_controller/request'
require 'action_controller/response'
require 'action_controller/routing'
-require 'action_controller/code_generation'
require 'action_controller/url_rewriter'
require 'drb'
require 'set'
@@ -1,243 +0,0 @@
-module ActionController
- module CodeGeneration #:nodoc:
- class GenerationError < StandardError #:nodoc:
- end
-
- class Source #:nodoc:
- attr_reader :lines, :indentation_level
- IndentationString = ' '
- def initialize
- @lines, @indentation_level = [], 0
- end
- def line(line)
- @lines << (IndentationString * @indentation_level + line)
- end
- alias :<< :line
-
- def indent
- @indentation_level += 1
- yield
- ensure
- @indentation_level -= 1
- end
-
- def to_s() lines.join("\n") end
- end
-
- class CodeGenerator #:nodoc:
- attr_accessor :source, :locals
- def initialize(source = nil)
- @locals = []
- @source = source || Source.new
- end
-
- BeginKeywords = %w(if unless begin until while def).collect {|kw| kw.to_sym}
- ResumeKeywords = %w(elsif else rescue).collect {|kw| kw.to_sym}
- Keywords = BeginKeywords + ResumeKeywords
-
- def method_missing(keyword, *text)
- if Keywords.include? keyword
- if ResumeKeywords.include? keyword
- raise GenerationError, "Can only resume with #{keyword} immediately after an end" unless source.lines.last =~ /^\s*end\s*$/
- source.lines.pop # Remove the 'end'
- end
-
- line "#{keyword} #{text.join ' '}"
- begin source.indent { yield(self.dup) }
- ensure line 'end'
- end
- else
- super(keyword, *text)
- end
- end
-
- def line(*args) self.source.line(*args) end
- alias :<< :line
- def indent(*args, &block) source(*args, &block) end
- def to_s() source.to_s end
-
- def share_locals_with(other)
- other.locals = self.locals = (other.locals | locals)
- end
-
- FieldsToDuplicate = [:locals]
- def dup
- copy = self.class.new(source)
- self.class::FieldsToDuplicate.each do |sym|
- value = self.send(sym)
- value = value.dup unless value.nil? || value.is_a?(Numeric) || value.is_a?(Symbol)
- copy.send("#{sym}=", value)
- end
- return copy
- end
- end
-
- class RecognitionGenerator < CodeGenerator #:nodoc:
- Attributes = [:after, :before, :current, :results, :constants, :depth, :move_ahead, :finish_statement, :path_name, :base_segment_name, :base_index_name]
- attr_accessor(*Attributes)
- FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes
-
- def initialize(*args)
- super(*args)
- @after, @before = [], []
- @current = nil
- @results, @constants = {}, {}
- @depth = 0
- @move_ahead = nil
- @finish_statement = Proc.new {|hash_expr| hash_expr}
- @path_name = :path
- @base_segment_name = :segment
- @base_index_name = :index
- end
-
- def if_next_matches(string, &block)
- test = Routing.test_condition(next_segment(true), string)
- self.if(test, &block)
- end
-
- def move_forward(places = 1)
- dup = self.dup
- dup.depth += 1
- dup.move_ahead = places
- yield dup
- end
-
- def next_segment(assign_inline = false, default = nil)
- if locals.include?(segment_name)
- code = segment_name
- else
- code = "#{segment_name} = #{path_name}[#{index_name}]"
- if assign_inline
- code = "(#{code})"
- else
- line(code)
- code = segment_name
- end
-
- locals << segment_name
- end
- code = "(#{code} || #{default.inspect})" if default
-
- return code.to_s
- end
-
- def segment_name() "#{base_segment_name}#{depth}".to_sym end
- def index_name
- move_ahead, @move_ahead = @move_ahead, nil
- move_ahead ? "#{base_index_name} += #{move_ahead}" : base_index_name
- end
-
- def continue
- dup = self.dup
- dup.before << dup.current
- dup.current = dup.after.shift
- dup.go
- end
-
- def go
- if current then current.write_recognition(self)
- else self.finish
- end
- end
-
- def result(key, expression, delay = false)
- unless delay
- line "#{key}_value = #{expression}"
- expression = "#{key}_value"
- end
- results[key] = expression
- end
- def constant_result(key, object)
- constants[key] = object
- end
-
- def finish(ensure_traversal_finished = true)
- pairs = []
- (results.keys + constants.keys).uniq.each do |key|
- pairs << "#{key.to_s.inspect} => #{results[key] ? results[key] : constants[key].inspect}"
- end
- hash_expr = "{#{pairs.join(', ')}}"
-
- statement = finish_statement.call(hash_expr)
- if ensure_traversal_finished then self.if("! #{next_segment(true)}") {|gp| gp << statement}
- else self << statement
- end
- end
- end
-
- class GenerationGenerator < CodeGenerator #:nodoc:
- Attributes = [:after, :before, :current, :segments, :subpath_at]
- attr_accessor(*Attributes)
- FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes
-
- def initialize(*args)
- super(*args)
- @after, @before = [], []
- @current = nil
- @segments = []
- @subpath_at = nil
- end
-
- def hash_name() 'hash' end
- def local_name(key) "#{key}_value" end
-
- def hash_value(key, assign = true, default = nil)
- if locals.include?(local_name(key)) then code = local_name(key)
- else
- code = "hash[#{key.to_sym.inspect}]"
- if assign
- code = "(#{local_name(key)} = #{code})"
- locals << local_name(key)
- end
- end
- code = "(#{code} || (#{default.inspect}))" if default
- return code
- end
-
- def expire_for_keys(*keys)
- return if keys.empty?
- conds = keys.collect {|key| "expire_on[#{key.to_sym.inspect}]"}
- line "not_expired, #{hash_name} = false, options if not_expired && #{conds.join(' && ')}"
- end
-
- def add_segment(*segments)
- d = dup
- d.segments.concat segments
- yield d
- end
-
- def go
- if current then current.write_generation(self)
- else self.finish
- end
- end
-
- def continue
- d = dup
- d.before << d.current
- d.current = d.after.shift
- d.go
- end
-
- def start_subpath!
- @subpath_at ||= segments.length
- end
-
- def finish
- segments[subpath_at..-1] = [segments[subpath_at..-1].join(";")] if subpath_at
- line %("/#{segments.join('/')}")
- end
-
- def check_conditions(conditions)
- tests = []
- generator = nil
- conditions.each do |key, condition|
- tests << (generator || self).hash_value(key, true) if condition.is_a? Regexp
- tests << Routing.test_condition((generator || self).hash_value(key, false), condition)
- generator = self.dup unless generator
- end
- return tests.join(' && ')
- end
- end
- end
-end
Oops, something went wrong.

0 comments on commit b20c575

Please sign in to comment.