actionpack 2.1.2 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +223 -7
- data/README +6 -12
- data/Rakefile +11 -11
- data/lib/action_controller.rb +9 -9
- data/lib/action_controller/assertions/response_assertions.rb +29 -78
- data/lib/action_controller/assertions/routing_assertions.rb +33 -33
- data/lib/action_controller/assertions/selector_assertions.rb +9 -5
- data/lib/action_controller/base.rb +227 -161
- data/lib/action_controller/benchmarking.rb +37 -24
- data/lib/action_controller/caching/actions.rb +53 -21
- data/lib/action_controller/caching/fragments.rb +10 -36
- data/lib/action_controller/caching/sweeping.rb +3 -3
- data/lib/action_controller/cgi_ext/session.rb +2 -22
- data/lib/action_controller/cgi_process.rb +8 -46
- data/lib/action_controller/components.rb +4 -1
- data/lib/action_controller/cookies.rb +10 -0
- data/lib/action_controller/dispatcher.rb +49 -15
- data/lib/action_controller/filters.rb +48 -10
- data/lib/action_controller/headers.rb +16 -14
- data/lib/action_controller/helpers.rb +2 -2
- data/lib/action_controller/http_authentication.rb +1 -1
- data/lib/action_controller/integration.rb +57 -60
- data/lib/action_controller/layout.rb +27 -53
- data/lib/action_controller/mime_responds.rb +5 -1
- data/lib/action_controller/mime_type.rb +64 -42
- data/lib/action_controller/mime_types.rb +2 -1
- data/lib/action_controller/performance_test.rb +16 -0
- data/lib/action_controller/polymorphic_routes.rb +16 -9
- data/lib/action_controller/rack_process.rb +303 -0
- data/lib/action_controller/request.rb +205 -97
- data/lib/action_controller/request_forgery_protection.rb +2 -2
- data/lib/action_controller/request_profiler.rb +0 -0
- data/lib/action_controller/rescue.rb +20 -115
- data/lib/action_controller/resources.rb +186 -83
- data/lib/action_controller/response.rb +140 -26
- data/lib/action_controller/routing.rb +28 -30
- data/lib/action_controller/routing/builder.rb +45 -54
- data/lib/action_controller/routing/optimisations.rb +31 -21
- data/lib/action_controller/routing/recognition_optimisation.rb +33 -27
- data/lib/action_controller/routing/route.rb +162 -147
- data/lib/action_controller/routing/route_set.rb +8 -7
- data/lib/action_controller/routing/routing_ext.rb +4 -1
- data/lib/action_controller/routing/segments.rb +50 -21
- data/lib/action_controller/session/cookie_store.rb +3 -2
- data/lib/action_controller/session/drb_server.rb +7 -7
- data/lib/action_controller/session_management.rb +6 -2
- data/lib/action_controller/streaming.rb +15 -8
- data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
- data/lib/action_controller/templates/rescues/template_error.erb +2 -2
- data/lib/action_controller/test_case.rb +66 -2
- data/lib/action_controller/test_process.rb +71 -66
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/url_rewriter.rb +90 -13
- data/lib/action_controller/vendor/html-scanner/html/node.rb +9 -2
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +1 -1
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -2
- data/lib/action_controller/verification.rb +2 -2
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view.rb +19 -11
- data/lib/action_view/base.rb +184 -150
- data/lib/action_view/helpers.rb +38 -0
- data/lib/action_view/helpers/active_record_helper.rb +56 -27
- data/lib/action_view/helpers/asset_tag_helper.rb +356 -153
- data/lib/action_view/helpers/atom_feed_helper.rb +74 -19
- data/lib/action_view/helpers/benchmark_helper.rb +3 -3
- data/lib/action_view/helpers/cache_helper.rb +1 -2
- data/lib/action_view/helpers/capture_helper.rb +19 -44
- data/lib/action_view/helpers/date_helper.rb +486 -296
- data/lib/action_view/helpers/debug_helper.rb +20 -13
- data/lib/action_view/helpers/form_helper.rb +71 -30
- data/lib/action_view/helpers/form_options_helper.rb +15 -85
- data/lib/action_view/helpers/form_tag_helper.rb +61 -38
- data/lib/action_view/helpers/javascript_helper.rb +80 -89
- data/lib/action_view/helpers/number_helper.rb +179 -74
- data/lib/action_view/helpers/prototype_helper.rb +216 -201
- data/lib/action_view/helpers/record_tag_helper.rb +4 -5
- data/lib/action_view/helpers/sanitize_helper.rb +65 -33
- data/lib/action_view/helpers/scriptaculous_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +39 -22
- data/lib/action_view/helpers/text_helper.rb +212 -118
- data/lib/action_view/helpers/translation_helper.rb +21 -0
- data/lib/action_view/helpers/url_helper.rb +100 -58
- data/lib/action_view/inline_template.rb +13 -14
- data/lib/action_view/locale/en.yml +91 -0
- data/lib/action_view/partials.rb +100 -55
- data/lib/action_view/paths.rb +125 -0
- data/lib/action_view/renderable.rb +102 -0
- data/lib/action_view/renderable_partial.rb +48 -0
- data/lib/action_view/template.rb +90 -101
- data/lib/action_view/template_error.rb +11 -21
- data/lib/action_view/template_handler.rb +8 -28
- data/lib/action_view/template_handlers.rb +45 -0
- data/lib/action_view/template_handlers/builder.rb +5 -15
- data/lib/action_view/template_handlers/erb.rb +9 -6
- data/lib/action_view/template_handlers/rjs.rb +2 -17
- data/lib/action_view/test_case.rb +7 -4
- data/test/abstract_unit.rb +4 -1
- data/test/active_record_unit.rb +28 -30
- data/test/activerecord/render_partial_with_record_identification_test.rb +25 -12
- data/test/controller/action_pack_assertions_test.rb +8 -37
- data/test/controller/addresses_render_test.rb +0 -3
- data/test/controller/assert_select_test.rb +51 -24
- data/test/controller/base_test.rb +4 -4
- data/test/controller/caching_test.rb +136 -66
- data/test/controller/capture_test.rb +1 -21
- data/test/controller/cgi_test.rb +157 -10
- data/test/controller/components_test.rb +41 -25
- data/test/controller/content_type_test.rb +49 -17
- data/test/controller/cookie_test.rb +1 -1
- data/test/controller/deprecation/deprecated_base_methods_test.rb +0 -3
- data/test/controller/dispatcher_test.rb +9 -1
- data/test/controller/filter_params_test.rb +2 -2
- data/test/controller/filters_test.rb +13 -13
- data/test/controller/html-scanner/cdata_node_test.rb +15 -0
- data/test/controller/html-scanner/node_test.rb +21 -0
- data/test/controller/html-scanner/sanitizer_test.rb +14 -0
- data/test/controller/integration_test.rb +167 -6
- data/test/controller/layout_test.rb +11 -68
- data/test/controller/logging_test.rb +46 -0
- data/test/controller/mime_responds_test.rb +61 -59
- data/test/controller/mime_type_test.rb +6 -6
- data/test/controller/polymorphic_routes_test.rb +37 -2
- data/test/controller/rack_test.rb +323 -0
- data/test/controller/redirect_test.rb +72 -71
- data/test/controller/render_test.rb +1120 -108
- data/test/controller/request_forgery_protection_test.rb +66 -52
- data/test/controller/request_test.rb +103 -146
- data/test/controller/rescue_test.rb +20 -24
- data/test/controller/resources_test.rb +408 -25
- data/test/controller/routing_test.rb +1774 -1774
- data/test/controller/send_file_test.rb +0 -4
- data/test/controller/session/cookie_store_test.rb +53 -1
- data/test/controller/test_test.rb +15 -37
- data/test/controller/translation_test.rb +26 -0
- data/test/controller/url_rewriter_test.rb +27 -28
- data/test/controller/view_paths_test.rb +48 -47
- data/test/fixtures/_top_level_partial.html.erb +1 -0
- data/test/fixtures/_top_level_partial_only.erb +1 -0
- data/test/fixtures/developers/_developer.erb +1 -0
- data/test/fixtures/fun/games/_game.erb +1 -0
- data/test/fixtures/fun/serious/games/_game.erb +1 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
- data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
- data/test/fixtures/layouts/_column.html.erb +2 -0
- data/test/fixtures/projects/_project.erb +1 -0
- data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
- data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
- data/test/fixtures/replies/_reply.erb +1 -0
- data/test/fixtures/test/_counter.html.erb +1 -0
- data/test/fixtures/test/_customer.erb +1 -1
- data/test/fixtures/test/_customer_with_var.erb +1 -0
- data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
- data/test/fixtures/test/_local_inspector.html.erb +1 -0
- data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
- data/test/fixtures/test/hello.builder +1 -1
- data/test/fixtures/test/hyphen-ated.erb +1 -0
- data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
- data/test/fixtures/test/nested_layout.erb +3 -0
- data/test/fixtures/test/non_erb_block_content_for.builder +1 -1
- data/test/fixtures/test/sub_template_raise.html.erb +1 -0
- data/test/fixtures/test/template.erb +1 -0
- data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
- data/test/template/active_record_helper_i18n_test.rb +46 -0
- data/test/template/active_record_helper_test.rb +24 -24
- data/test/template/asset_tag_helper_test.rb +161 -29
- data/test/template/atom_feed_helper_test.rb +114 -5
- data/test/template/compiled_templates_test.rb +59 -0
- data/test/template/date_helper_i18n_test.rb +113 -0
- data/test/template/date_helper_test.rb +403 -109
- data/test/template/form_helper_test.rb +213 -154
- data/test/template/form_options_helper_test.rb +249 -897
- data/test/template/form_tag_helper_test.rb +80 -32
- data/test/template/javascript_helper_test.rb +17 -18
- data/test/template/number_helper_i18n_test.rb +54 -0
- data/test/template/number_helper_test.rb +43 -13
- data/test/template/prototype_helper_test.rb +101 -84
- data/test/template/record_tag_helper_test.rb +24 -20
- data/test/template/render_test.rb +193 -0
- data/test/template/sanitize_helper_test.rb +3 -3
- data/test/template/tag_helper_test.rb +34 -14
- data/test/template/text_helper_test.rb +83 -9
- data/test/template/translation_helper_test.rb +28 -0
- data/test/template/url_helper_test.rb +55 -18
- metadata +57 -18
- data/lib/action_view/helpers/javascripts/controls.js +0 -963
- data/lib/action_view/helpers/javascripts/dragdrop.js +0 -972
- data/lib/action_view/helpers/javascripts/effects.js +0 -1120
- data/lib/action_view/helpers/javascripts/prototype.js +0 -4225
- data/lib/action_view/partial_template.rb +0 -70
- data/lib/action_view/template_finder.rb +0 -177
- data/lib/action_view/template_handlers/compilable.rb +0 -128
- data/test/controller/custom_handler_test.rb +0 -45
- data/test/controller/new_render_test.rb +0 -945
- data/test/fixtures/test/block_content_for.erb +0 -2
- data/test/fixtures/test/erb_content_for.erb +0 -2
- data/test/template/deprecated_erb_variable_test.rb +0 -9
- data/test/template/template_finder_test.rb +0 -73
- data/test/template/template_object_test.rb +0 -95
@@ -1,14 +1,14 @@
|
|
1
1
|
module ActionController
|
2
2
|
module Routing
|
3
|
-
# Much of the slow performance from routes comes from the
|
3
|
+
# Much of the slow performance from routes comes from the
|
4
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
|
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
7
|
# they've provided the right number of arguments, and have no
|
8
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
|
9
|
+
#
|
10
|
+
# To support building optimisations for other common cases, the
|
11
|
+
# generation code is separated into several classes
|
12
12
|
module Optimisation
|
13
13
|
def generate_optimisation_block(route, kind)
|
14
14
|
return "" unless route.optimise?
|
@@ -20,13 +20,20 @@ module ActionController
|
|
20
20
|
|
21
21
|
class Optimiser
|
22
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
|
+
|
23
30
|
def initialize(route, kind)
|
24
31
|
@route = route
|
25
32
|
@kind = kind
|
26
33
|
end
|
27
34
|
|
28
|
-
def
|
29
|
-
|
35
|
+
def guard_conditions
|
36
|
+
["false"]
|
30
37
|
end
|
31
38
|
|
32
39
|
def generation_code
|
@@ -35,6 +42,7 @@ module ActionController
|
|
35
42
|
|
36
43
|
def source_code
|
37
44
|
if applicable?
|
45
|
+
guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ")
|
38
46
|
"return #{generation_code} if #{guard_condition}\n"
|
39
47
|
else
|
40
48
|
"\n"
|
@@ -53,17 +61,17 @@ module ActionController
|
|
53
61
|
# map.person '/people/:id'
|
54
62
|
#
|
55
63
|
# If the user calls <tt>person_url(@person)</tt>, we can simply
|
56
|
-
# return a string like "/people/#{@person.to_param}"
|
64
|
+
# return a string like "/people/#{@person.to_param}"
|
57
65
|
# rather than triggering the expensive logic in +url_for+.
|
58
66
|
class PositionalArguments < Optimiser
|
59
|
-
def
|
67
|
+
def guard_conditions
|
60
68
|
number_of_arguments = route.segment_keys.size
|
61
|
-
# if they're using foo_url(:id=>2) it's one
|
69
|
+
# if they're using foo_url(:id=>2) it's one
|
62
70
|
# argument, but we don't want to generate /foos/id2
|
63
71
|
if number_of_arguments == 1
|
64
|
-
"
|
72
|
+
["args.size == 1", "!args.first.is_a?(Hash)"]
|
65
73
|
else
|
66
|
-
"
|
74
|
+
["args.size == #{number_of_arguments}"]
|
67
75
|
end
|
68
76
|
end
|
69
77
|
|
@@ -76,7 +84,7 @@ module ActionController
|
|
76
84
|
elements << '#{request.host_with_port}'
|
77
85
|
end
|
78
86
|
|
79
|
-
elements << '#{
|
87
|
+
elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}'
|
80
88
|
|
81
89
|
# The last entry in <tt>route.segments</tt> appears to *always* be a
|
82
90
|
# 'divider segment' for '/' but we have assertions to ensure that
|
@@ -94,23 +102,25 @@ module ActionController
|
|
94
102
|
end
|
95
103
|
|
96
104
|
# This case is mostly the same as the positional arguments case
|
97
|
-
# above, but it supports additional query parameters as the last
|
105
|
+
# above, but it supports additional query parameters as the last
|
98
106
|
# argument
|
99
107
|
class PositionalArgumentsWithAdditionalParams < PositionalArguments
|
100
|
-
def
|
101
|
-
"
|
108
|
+
def guard_conditions
|
109
|
+
["args.size == #{route.segment_keys.size + 1}"] +
|
110
|
+
UrlRewriter::RESERVED_OPTIONS.collect{ |key| "!args.last.has_key?(:#{key})" }
|
102
111
|
end
|
103
112
|
|
104
|
-
# This case uses almost the same code as positional arguments,
|
105
|
-
# but add
|
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
|
106
116
|
def generation_code
|
107
|
-
super.insert(-2, '
|
117
|
+
super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}')
|
108
118
|
end
|
109
119
|
|
110
120
|
# To avoid generating "http://localhost/?host=foo.example.com" we
|
111
121
|
# can't use this optimisation on routes without any segments
|
112
122
|
def applicable?
|
113
|
-
super && route.segment_keys.size > 0
|
123
|
+
super && route.segment_keys.size > 0
|
114
124
|
end
|
115
125
|
end
|
116
126
|
|
@@ -51,7 +51,6 @@ module ActionController
|
|
51
51
|
# 3) segm test for /users/:id
|
52
52
|
# (jump to list index = 5)
|
53
53
|
# 4) full test for /users/:id => here we are!
|
54
|
-
|
55
54
|
class RouteSet
|
56
55
|
def recognize_path(path, environment={})
|
57
56
|
result = recognize_optimized(path, environment) and return result
|
@@ -68,32 +67,6 @@ module ActionController
|
|
68
67
|
end
|
69
68
|
end
|
70
69
|
|
71
|
-
def clear_recognize_optimized!
|
72
|
-
instance_eval %{
|
73
|
-
def recognize_optimized(path, env)
|
74
|
-
write_recognize_optimized!
|
75
|
-
recognize_optimized(path, env)
|
76
|
-
end
|
77
|
-
}, __FILE__, __LINE__
|
78
|
-
end
|
79
|
-
|
80
|
-
def write_recognize_optimized!
|
81
|
-
tree = segment_tree(routes)
|
82
|
-
body = generate_code(tree)
|
83
|
-
instance_eval %{
|
84
|
-
def recognize_optimized(path, env)
|
85
|
-
segments = to_plain_segments(path)
|
86
|
-
index = #{body}
|
87
|
-
return nil unless index
|
88
|
-
while index < routes.size
|
89
|
-
result = routes[index].recognize(path, env) and return result
|
90
|
-
index += 1
|
91
|
-
end
|
92
|
-
nil
|
93
|
-
end
|
94
|
-
}, __FILE__, __LINE__
|
95
|
-
end
|
96
|
-
|
97
70
|
def segment_tree(routes)
|
98
71
|
tree = [0]
|
99
72
|
|
@@ -157,6 +130,39 @@ module ActionController
|
|
157
130
|
segments
|
158
131
|
end
|
159
132
|
|
133
|
+
private
|
134
|
+
def write_recognize_optimized!
|
135
|
+
tree = segment_tree(routes)
|
136
|
+
body = generate_code(tree)
|
137
|
+
|
138
|
+
remove_recognize_optimized!
|
139
|
+
|
140
|
+
instance_eval %{
|
141
|
+
def recognize_optimized(path, env)
|
142
|
+
segments = to_plain_segments(path)
|
143
|
+
index = #{body}
|
144
|
+
return nil unless index
|
145
|
+
while index < routes.size
|
146
|
+
result = routes[index].recognize(path, env) and return result
|
147
|
+
index += 1
|
148
|
+
end
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
}, '(recognize_optimized)', 1
|
152
|
+
end
|
153
|
+
|
154
|
+
def clear_recognize_optimized!
|
155
|
+
remove_recognize_optimized!
|
156
|
+
write_recognize_optimized!
|
157
|
+
end
|
158
|
+
|
159
|
+
def remove_recognize_optimized!
|
160
|
+
if respond_to?(:recognize_optimized)
|
161
|
+
class << self
|
162
|
+
remove_method :recognize_optimized
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
160
166
|
end
|
161
167
|
end
|
162
168
|
end
|
@@ -3,11 +3,25 @@ module ActionController
|
|
3
3
|
class Route #:nodoc:
|
4
4
|
attr_accessor :segments, :requirements, :conditions, :optimise
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
@segments =
|
8
|
-
@requirements =
|
9
|
-
@conditions =
|
10
|
-
|
6
|
+
def initialize(segments = [], requirements = {}, conditions = {})
|
7
|
+
@segments = segments
|
8
|
+
@requirements = requirements
|
9
|
+
@conditions = conditions
|
10
|
+
|
11
|
+
if !significant_keys.include?(:action) && !requirements[:action]
|
12
|
+
@requirements[:action] = "index"
|
13
|
+
@significant_keys << :action
|
14
|
+
end
|
15
|
+
|
16
|
+
# Routes cannot use the current string interpolation method
|
17
|
+
# if there are user-supplied <tt>:requirements</tt> as the interpolation
|
18
|
+
# code won't raise RoutingErrors when generating
|
19
|
+
has_requirements = @segments.detect { |segment| segment.respond_to?(:regexp) && segment.regexp }
|
20
|
+
if has_requirements || @requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION
|
21
|
+
@optimise = false
|
22
|
+
else
|
23
|
+
@optimise = true
|
24
|
+
end
|
11
25
|
end
|
12
26
|
|
13
27
|
# Indicates whether the routes should be optimised with the string interpolation
|
@@ -22,129 +36,6 @@ module ActionController
|
|
22
36
|
end.compact
|
23
37
|
end
|
24
38
|
|
25
|
-
# Write and compile a +generate+ method for this Route.
|
26
|
-
def write_generation
|
27
|
-
# Build the main body of the generation
|
28
|
-
body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
|
29
|
-
|
30
|
-
# If we have conditions that must be tested first, nest the body inside an if
|
31
|
-
body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements
|
32
|
-
args = "options, hash, expire_on = {}"
|
33
|
-
|
34
|
-
# Nest the body inside of a def block, and then compile it.
|
35
|
-
raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend"
|
36
|
-
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
37
|
-
|
38
|
-
# expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
|
39
|
-
# are the same as the keys that were recalled from the previous request. Thus,
|
40
|
-
# we can use the expire_on.keys to determine which keys ought to be used to build
|
41
|
-
# the query string. (Never use keys from the recalled request when building the
|
42
|
-
# query string.)
|
43
|
-
|
44
|
-
method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend"
|
45
|
-
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
46
|
-
|
47
|
-
method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
|
48
|
-
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
49
|
-
raw_method
|
50
|
-
end
|
51
|
-
|
52
|
-
# Build several lines of code that extract values from the options hash. If any
|
53
|
-
# of the values are missing or rejected then a return will be executed.
|
54
|
-
def generation_extraction
|
55
|
-
segments.collect do |segment|
|
56
|
-
segment.extraction_code
|
57
|
-
end.compact * "\n"
|
58
|
-
end
|
59
|
-
|
60
|
-
# Produce a condition expression that will check the requirements of this route
|
61
|
-
# upon generation.
|
62
|
-
def generation_requirements
|
63
|
-
requirement_conditions = requirements.collect do |key, req|
|
64
|
-
if req.is_a? Regexp
|
65
|
-
value_regexp = Regexp.new "\\A#{req.to_s}\\Z"
|
66
|
-
"hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]"
|
67
|
-
else
|
68
|
-
"hash[:#{key}] == #{req.inspect}"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
requirement_conditions * ' && ' unless requirement_conditions.empty?
|
72
|
-
end
|
73
|
-
|
74
|
-
def generation_structure
|
75
|
-
segments.last.string_structure segments[0..-2]
|
76
|
-
end
|
77
|
-
|
78
|
-
# Write and compile a +recognize+ method for this Route.
|
79
|
-
def write_recognition
|
80
|
-
# Create an if structure to extract the params from a match if it occurs.
|
81
|
-
body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
|
82
|
-
body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
|
83
|
-
|
84
|
-
# Build the method declaration and compile it
|
85
|
-
method_decl = "def recognize(path, env={})\n#{body}\nend"
|
86
|
-
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
87
|
-
method_decl
|
88
|
-
end
|
89
|
-
|
90
|
-
# Plugins may override this method to add other conditions, like checks on
|
91
|
-
# host, subdomain, and so forth. Note that changes here only affect route
|
92
|
-
# recognition, not generation.
|
93
|
-
def recognition_conditions
|
94
|
-
result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"]
|
95
|
-
result << "conditions[:method] === env[:method]" if conditions[:method]
|
96
|
-
result
|
97
|
-
end
|
98
|
-
|
99
|
-
# Build the regular expression pattern that will match this route.
|
100
|
-
def recognition_pattern(wrap = true)
|
101
|
-
pattern = ''
|
102
|
-
segments.reverse_each do |segment|
|
103
|
-
pattern = segment.build_pattern pattern
|
104
|
-
end
|
105
|
-
wrap ? ("\\A" + pattern + "\\Z") : pattern
|
106
|
-
end
|
107
|
-
|
108
|
-
# Write the code to extract the parameters from a matched route.
|
109
|
-
def recognition_extraction
|
110
|
-
next_capture = 1
|
111
|
-
extraction = segments.collect do |segment|
|
112
|
-
x = segment.match_extraction(next_capture)
|
113
|
-
next_capture += Regexp.new(segment.regexp_chunk).number_of_captures
|
114
|
-
x
|
115
|
-
end
|
116
|
-
extraction.compact
|
117
|
-
end
|
118
|
-
|
119
|
-
# Write the real generation implementation and then resend the message.
|
120
|
-
def generate(options, hash, expire_on = {})
|
121
|
-
write_generation
|
122
|
-
generate options, hash, expire_on
|
123
|
-
end
|
124
|
-
|
125
|
-
def generate_extras(options, hash, expire_on = {})
|
126
|
-
write_generation
|
127
|
-
generate_extras options, hash, expire_on
|
128
|
-
end
|
129
|
-
|
130
|
-
# Generate the query string with any extra keys in the hash and append
|
131
|
-
# it to the given path, returning the new path.
|
132
|
-
def append_query_string(path, hash, query_keys=nil)
|
133
|
-
return nil unless path
|
134
|
-
query_keys ||= extra_keys(hash)
|
135
|
-
"#{path}#{build_query_string(hash, query_keys)}"
|
136
|
-
end
|
137
|
-
|
138
|
-
# Determine which keys in the given hash are "extra". Extra keys are
|
139
|
-
# those that were not used to generate a particular route. The extra
|
140
|
-
# keys also do not include those recalled from the prior request, nor
|
141
|
-
# do they include any keys that were implied in the route (like a
|
142
|
-
# <tt>:controller</tt> that is required, but not explicitly used in the
|
143
|
-
# text of the route.)
|
144
|
-
def extra_keys(hash, recall={})
|
145
|
-
(hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
|
146
|
-
end
|
147
|
-
|
148
39
|
# Build a query string from the keys of the given hash. If +only_keys+
|
149
40
|
# is given (as an array), only the keys indicated will be used to build
|
150
41
|
# the query string. The query string will correctly build array parameter
|
@@ -161,12 +52,6 @@ module ActionController
|
|
161
52
|
elements.empty? ? '' : "?#{elements.sort * '&'}"
|
162
53
|
end
|
163
54
|
|
164
|
-
# Write the real recognition implementation and then resend the message.
|
165
|
-
def recognize(path, environment={})
|
166
|
-
write_recognition
|
167
|
-
recognize path, environment
|
168
|
-
end
|
169
|
-
|
170
55
|
# A route's parameter shell contains parameter values that are not in the
|
171
56
|
# route's path, but should be placed in the recognized hash.
|
172
57
|
#
|
@@ -186,7 +71,7 @@ module ActionController
|
|
186
71
|
# includes keys that appear inside the path, and keys that have requirements
|
187
72
|
# placed upon them.
|
188
73
|
def significant_keys
|
189
|
-
@significant_keys ||= returning
|
74
|
+
@significant_keys ||= returning([]) do |sk|
|
190
75
|
segments.each { |segment| sk << segment.key if segment.respond_to? :key }
|
191
76
|
sk.concat requirements.keys
|
192
77
|
sk.uniq!
|
@@ -209,12 +94,7 @@ module ActionController
|
|
209
94
|
end
|
210
95
|
|
211
96
|
def matches_controller_and_action?(controller, action)
|
212
|
-
|
213
|
-
@controller_requirement = requirement_for(:controller)
|
214
|
-
@action_requirement = requirement_for(:action)
|
215
|
-
@matching_prepared = true
|
216
|
-
end
|
217
|
-
|
97
|
+
prepare_matching!
|
218
98
|
(@controller_requirement.nil? || @controller_requirement === controller) &&
|
219
99
|
(@action_requirement.nil? || @action_requirement === action)
|
220
100
|
end
|
@@ -226,15 +106,150 @@ module ActionController
|
|
226
106
|
end
|
227
107
|
end
|
228
108
|
|
229
|
-
|
230
|
-
def
|
231
|
-
|
232
|
-
|
233
|
-
|
109
|
+
# TODO: Route should be prepared and frozen on initialize
|
110
|
+
def freeze
|
111
|
+
unless frozen?
|
112
|
+
write_generation!
|
113
|
+
write_recognition!
|
114
|
+
prepare_matching!
|
115
|
+
|
116
|
+
parameter_shell
|
117
|
+
significant_keys
|
118
|
+
defaults
|
119
|
+
to_s
|
234
120
|
end
|
235
|
-
|
121
|
+
|
122
|
+
super
|
236
123
|
end
|
237
124
|
|
125
|
+
private
|
126
|
+
def requirement_for(key)
|
127
|
+
return requirements[key] if requirements.key? key
|
128
|
+
segments.each do |segment|
|
129
|
+
return segment.regexp if segment.respond_to?(:key) && segment.key == key
|
130
|
+
end
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Write and compile a +generate+ method for this Route.
|
135
|
+
def write_generation!
|
136
|
+
# Build the main body of the generation
|
137
|
+
body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
|
138
|
+
|
139
|
+
# If we have conditions that must be tested first, nest the body inside an if
|
140
|
+
body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements
|
141
|
+
args = "options, hash, expire_on = {}"
|
142
|
+
|
143
|
+
# Nest the body inside of a def block, and then compile it.
|
144
|
+
raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend"
|
145
|
+
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
146
|
+
|
147
|
+
# expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
|
148
|
+
# are the same as the keys that were recalled from the previous request. Thus,
|
149
|
+
# we can use the expire_on.keys to determine which keys ought to be used to build
|
150
|
+
# the query string. (Never use keys from the recalled request when building the
|
151
|
+
# query string.)
|
152
|
+
|
153
|
+
method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend"
|
154
|
+
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
155
|
+
|
156
|
+
method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
|
157
|
+
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
158
|
+
raw_method
|
159
|
+
end
|
160
|
+
|
161
|
+
# Build several lines of code that extract values from the options hash. If any
|
162
|
+
# of the values are missing or rejected then a return will be executed.
|
163
|
+
def generation_extraction
|
164
|
+
segments.collect do |segment|
|
165
|
+
segment.extraction_code
|
166
|
+
end.compact * "\n"
|
167
|
+
end
|
168
|
+
|
169
|
+
# Produce a condition expression that will check the requirements of this route
|
170
|
+
# upon generation.
|
171
|
+
def generation_requirements
|
172
|
+
requirement_conditions = requirements.collect do |key, req|
|
173
|
+
if req.is_a? Regexp
|
174
|
+
value_regexp = Regexp.new "\\A#{req.to_s}\\Z"
|
175
|
+
"hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]"
|
176
|
+
else
|
177
|
+
"hash[:#{key}] == #{req.inspect}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
requirement_conditions * ' && ' unless requirement_conditions.empty?
|
181
|
+
end
|
182
|
+
|
183
|
+
def generation_structure
|
184
|
+
segments.last.string_structure segments[0..-2]
|
185
|
+
end
|
186
|
+
|
187
|
+
# Write and compile a +recognize+ method for this Route.
|
188
|
+
def write_recognition!
|
189
|
+
# Create an if structure to extract the params from a match if it occurs.
|
190
|
+
body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
|
191
|
+
body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
|
192
|
+
|
193
|
+
# Build the method declaration and compile it
|
194
|
+
method_decl = "def recognize(path, env = {})\n#{body}\nend"
|
195
|
+
instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
|
196
|
+
method_decl
|
197
|
+
end
|
198
|
+
|
199
|
+
# Plugins may override this method to add other conditions, like checks on
|
200
|
+
# host, subdomain, and so forth. Note that changes here only affect route
|
201
|
+
# recognition, not generation.
|
202
|
+
def recognition_conditions
|
203
|
+
result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"]
|
204
|
+
result << "[conditions[:method]].flatten.include?(env[:method])" if conditions[:method]
|
205
|
+
result
|
206
|
+
end
|
207
|
+
|
208
|
+
# Build the regular expression pattern that will match this route.
|
209
|
+
def recognition_pattern(wrap = true)
|
210
|
+
pattern = ''
|
211
|
+
segments.reverse_each do |segment|
|
212
|
+
pattern = segment.build_pattern pattern
|
213
|
+
end
|
214
|
+
wrap ? ("\\A" + pattern + "\\Z") : pattern
|
215
|
+
end
|
216
|
+
|
217
|
+
# Write the code to extract the parameters from a matched route.
|
218
|
+
def recognition_extraction
|
219
|
+
next_capture = 1
|
220
|
+
extraction = segments.collect do |segment|
|
221
|
+
x = segment.match_extraction(next_capture)
|
222
|
+
next_capture += segment.number_of_captures
|
223
|
+
x
|
224
|
+
end
|
225
|
+
extraction.compact
|
226
|
+
end
|
227
|
+
|
228
|
+
# Generate the query string with any extra keys in the hash and append
|
229
|
+
# it to the given path, returning the new path.
|
230
|
+
def append_query_string(path, hash, query_keys = nil)
|
231
|
+
return nil unless path
|
232
|
+
query_keys ||= extra_keys(hash)
|
233
|
+
"#{path}#{build_query_string(hash, query_keys)}"
|
234
|
+
end
|
235
|
+
|
236
|
+
# Determine which keys in the given hash are "extra". Extra keys are
|
237
|
+
# those that were not used to generate a particular route. The extra
|
238
|
+
# keys also do not include those recalled from the prior request, nor
|
239
|
+
# do they include any keys that were implied in the route (like a
|
240
|
+
# <tt>:controller</tt> that is required, but not explicitly used in the
|
241
|
+
# text of the route.)
|
242
|
+
def extra_keys(hash, recall = {})
|
243
|
+
(hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
|
244
|
+
end
|
245
|
+
|
246
|
+
def prepare_matching!
|
247
|
+
unless defined? @matching_prepared
|
248
|
+
@controller_requirement = requirement_for(:controller)
|
249
|
+
@action_requirement = requirement_for(:action)
|
250
|
+
@matching_prepared = true
|
251
|
+
end
|
252
|
+
end
|
238
253
|
end
|
239
254
|
end
|
240
255
|
end
|