Froze rails gems
[depot.git] / vendor / rails / actionpack / lib / action_controller / routing / optimisations.rb
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.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