~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/actionpack/lib/action_controller/routing/optimisations.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
module ActionController
2
 
  module Routing
3
 
    # Much of the slow performance from routes comes from the
4
 
    # complexity of expiry, <tt>:requirements</tt> matching, defaults providing
5
 
    # and figuring out which url pattern to use.  With named routes
6
 
    # we can avoid the expense of finding the right route.  So if
7
 
    # they've provided the right number of arguments, and have no
8
 
    # <tt>:requirements</tt>, we can just build up a string and return it.
9
 
    #
10
 
    # To support building optimisations for other common cases, the
11
 
    # generation code is separated into several classes
12
 
    module Optimisation
13
 
      def generate_optimisation_block(route, kind)
14
 
        return "" unless route.optimise?
15
 
        OPTIMISERS.inject("") do |memo, klazz|
16
 
          memo << klazz.new(route, kind).source_code
17
 
          memo
18
 
        end
19
 
      end
20
 
 
21
 
      class Optimiser
22
 
        attr_reader :route, :kind
23
 
        GLOBAL_GUARD_CONDITIONS = [
24
 
          "(!defined?(default_url_options) || default_url_options.blank?)",
25
 
          "(!defined?(controller.default_url_options) || controller.default_url_options.blank?)",
26
 
          "defined?(request)",
27
 
          "request"
28
 
          ]
29
 
 
30
 
        def initialize(route, kind)
31
 
          @route = route
32
 
          @kind  = kind
33
 
        end
34
 
 
35
 
        def guard_conditions
36
 
          ["false"]
37
 
        end
38
 
 
39
 
        def generation_code
40
 
          'nil'
41
 
        end
42
 
 
43
 
        def source_code
44
 
          if applicable?
45
 
            guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ")
46
 
            "return #{generation_code} if #{guard_condition}\n"
47
 
          else
48
 
            "\n"
49
 
          end
50
 
        end
51
 
 
52
 
        # Temporarily disabled <tt>:url</tt> optimisation pending proper solution to
53
 
        # Issues around request.host etc.
54
 
        def applicable?
55
 
          true
56
 
        end
57
 
      end
58
 
 
59
 
      # Given a route
60
 
      #
61
 
      #   map.person '/people/:id'
62
 
      #
63
 
      # If the user calls <tt>person_url(@person)</tt>, we can simply
64
 
      # return a string like "/people/#{@person.to_param}"
65
 
      # rather than triggering the expensive logic in +url_for+.
66
 
      class PositionalArguments < Optimiser
67
 
        def guard_conditions
68
 
          number_of_arguments = route.required_segment_keys.size
69
 
          # if they're using foo_url(:id=>2) it's one
70
 
          # argument, but we don't want to generate /foos/id2
71
 
          if number_of_arguments == 1
72
 
            ["args.size == 1", "!args.first.is_a?(Hash)"]
73
 
          else
74
 
            ["args.size == #{number_of_arguments}"]
75
 
          end
76
 
        end
77
 
 
78
 
        def generation_code
79
 
          elements = []
80
 
          idx = 0
81
 
 
82
 
          if kind == :url
83
 
            elements << '#{request.protocol}'
84
 
            elements << '#{request.host_with_port}'
85
 
          end
86
 
 
87
 
          elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}'
88
 
 
89
 
          # The last entry in <tt>route.segments</tt> appears to *always* be a
90
 
          # 'divider segment' for '/' but we have assertions to ensure that
91
 
          # we don't include the trailing slashes, so skip them.
92
 
          (route.segments.size == 1 ? route.segments : route.segments[0..-2]).each do |segment|
93
 
            if segment.is_a?(DynamicSegment)
94
 
              elements << segment.interpolation_chunk("args[#{idx}].to_param")
95
 
              idx += 1
96
 
            else
97
 
              elements << segment.interpolation_chunk
98
 
            end
99
 
          end
100
 
          %("#{elements * ''}")
101
 
        end
102
 
      end
103
 
 
104
 
      # This case is mostly the same as the positional arguments case
105
 
      # above, but it supports additional query parameters as the last
106
 
      # argument
107
 
      class PositionalArgumentsWithAdditionalParams < PositionalArguments
108
 
        def guard_conditions
109
 
          ["args.size == #{route.segment_keys.size + 1}"] +
110
 
          UrlRewriter::RESERVED_OPTIONS.collect{ |key| "!args.last.has_key?(:#{key})" }
111
 
        end
112
 
 
113
 
        # This case uses almost the same code as positional arguments,
114
 
        # but add a question mark and args.last.to_query on the end,
115
 
        # unless the last arg is empty
116
 
        def generation_code
117
 
          super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}')
118
 
        end
119
 
 
120
 
        # To avoid generating "http://localhost/?host=foo.example.com" we
121
 
        # can't use this optimisation on routes without any segments
122
 
        def applicable?
123
 
          super && route.segment_keys.size > 0
124
 
        end
125
 
      end
126
 
 
127
 
      OPTIMISERS = [PositionalArguments, PositionalArgumentsWithAdditionalParams]
128
 
    end
129
 
  end
130
 
end