Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #10835: 0002-added-optimisation-code.patch

File 0002-added-optimisation-code.patch, 5.2 kB (added by oleganza, 9 months ago)
  • a/actionpack/lib/action_controller/routing.rb

    old new  
    77require 'action_controller/routing/segments' 
    88require 'action_controller/routing/builder' 
    99require 'action_controller/routing/route_set' 
     10require 'action_controller/routing/recognition_optimisation' 
    1011 
    1112module ActionController 
    1213  # == Routing 
  • /dev/null

    old new  
     1module ActionController 
     2  module Routing 
     3    # Route recognition is slow due to one-by-one iterating over 
     4    # a whole routeset (each map.resources generates at least 14 routes) 
     5    # and matching weird regexps on each step. 
     6    # 
     7    # We optimize this by moving through forwarding prefix anchors first, 
     8    # and stupidly iterating thru routes when anchors' prefix is matched. 
     9    # In other words, we're skipping a bunch of routes with a same prefix 
     10    # if that prefix doesn't match. 
     11    # 
     12    # Example. Given the routes: 
     13    # /posts/ 
     14    # /posts/:id 
     15    # /posts/:id/comments 
     16    # /posts/blah 
     17    # /users/ 
     18    # /users/:id 
     19    #  
     20    # request_uri = /users/123 
     21    # 
     22    # There will be only 3 iterations:  
     23    #  1) test for /posts prefix, skip all /posts/* routes 
     24    #  2) test for /users/ 
     25    #  3) test for /users/:id => success 
     26         
     27    class RouteSet 
     28      def recognize_path(path, environment={}) 
     29        result = recognize_optimized(path, environment) and return result 
     30         
     31        # Route was not recognized. Try to find out why (maybe wrong verb). 
     32        allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } } 
     33                   
     34        if environment[:method] && !HTTP_METHODS.include?(environment[:method]) 
     35          raise NotImplemented.new(*allows) 
     36        elsif !allows.empty? 
     37          raise MethodNotAllowed.new(*allows) 
     38        else 
     39          raise RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" 
     40        end 
     41      end 
     42       
     43      def recognize_optimized(path, env) 
     44        (compile_recognize_optimized!; @compiled_recognize_optimized=true) unless @compiled_recognize_optimized 
     45         
     46        anch = @first_anchor 
     47        loop do 
     48          r = anch.recognize(path, env) and return r 
     49          anch = anch.next_anchor 
     50          break unless anch 
     51        end 
     52        nil 
     53      end 
     54       
     55      # one-level version. 
     56      def compile_recognize_optimized! 
     57         
     58        @first_anchor = current_anchor = PrefixAnchor.new(routes, 0) 
     59        i = 0 
     60        routes.each do |route| 
     61          # init linked list 
     62          pfx = prefix_for_anchor(route.segments) 
     63          unless current_anchor.extend(pfx) 
     64            new_anchor = PrefixAnchor.new(routes, current_anchor.last_index+1) 
     65            current_anchor.next_anchor = new_anchor 
     66            new_anchor.extend(pfx) 
     67            current_anchor = new_anchor 
     68          end 
     69        end 
     70      end 
     71       
     72      def prefix_for_anchor(segments) 
     73        return "" if segments.size == 1 && DividerSegment === segments[0] 
     74        return "/"+segments[1].value if StaticSegment === segments[1] 
     75        return :dynamic 
     76      end 
     77    end 
     78       
     79    # Contains a prefix to test for.  
     80    # If the test fails, all the anchor's routes are skipped. 
     81    # Otherwise, all of them are iterated. 
     82    class PrefixAnchor 
     83      attr_accessor :next_anchor 
     84       
     85      def initialize(rs, start = 0) 
     86        @routes = rs 
     87        @range  = (start..start) 
     88        @just_inited   = true 
     89      end 
     90      def extend(pfx) 
     91        return false if @prefix && pfx != @prefix 
     92        @prefix = pfx 
     93        @len = pfx == :dynamic ? -1 : pfx.size 
     94        @range = (@range.first..(@range.last + (@just_inited ? 0 : 1))) 
     95        @just_inited = false 
     96        true 
     97      end 
     98      def last_index 
     99        @range.last 
     100      end 
     101      def recognize(path, env) 
     102        # prefix not matched 
     103        if (@len > 0 && path[0, @len] != @prefix || @len == 0 && path !~ /^\/*$/) 
     104          return nil  
     105        end 
     106        @range.each do |i| 
     107          route = @routes[i] 
     108          result = route.recognize(path, env) and return result 
     109        end 
     110        nil 
     111      end 
     112    end 
     113  end 
     114end 
  • a/actionpack/lib/action_controller/routing/route_set.rb

    old new  
    208208        named_routes.clear 
    209209        @combined_regexp = nil 
    210210        @routes_by_controller = nil 
     211        # This will force routing/recognition_optimization.rb  
     212        # to refresh optimisations. 
     213        @compiled_recognize_optimized = nil 
    211214      end 
    212215 
    213216      def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)