actionpack 2.0.5 → 2.1.0
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 +149 -7
- data/MIT-LICENSE +1 -1
- data/README +1 -1
- data/Rakefile +5 -6
- data/lib/action_controller.rb +2 -2
- data/lib/action_controller/assertions/model_assertions.rb +2 -1
- data/lib/action_controller/assertions/response_assertions.rb +4 -2
- data/lib/action_controller/assertions/routing_assertions.rb +3 -3
- data/lib/action_controller/assertions/selector_assertions.rb +30 -27
- data/lib/action_controller/assertions/tag_assertions.rb +3 -3
- data/lib/action_controller/base.rb +103 -129
- data/lib/action_controller/benchmarking.rb +3 -3
- data/lib/action_controller/caching.rb +41 -652
- data/lib/action_controller/caching/actions.rb +144 -0
- data/lib/action_controller/caching/fragments.rb +138 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sql_cache.rb +18 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/cgi_ext/cookie.rb +27 -23
- data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
- data/lib/action_controller/cgi_process.rb +6 -4
- data/lib/action_controller/components.rb +7 -6
- data/lib/action_controller/cookies.rb +31 -19
- data/lib/action_controller/dispatcher.rb +51 -84
- data/lib/action_controller/filters.rb +295 -421
- data/lib/action_controller/flash.rb +1 -6
- data/lib/action_controller/headers.rb +31 -0
- data/lib/action_controller/helpers.rb +26 -9
- data/lib/action_controller/http_authentication.rb +1 -1
- data/lib/action_controller/integration.rb +65 -13
- data/lib/action_controller/layout.rb +24 -39
- data/lib/action_controller/mime_responds.rb +7 -3
- data/lib/action_controller/mime_type.rb +25 -9
- data/lib/action_controller/mime_types.rb +1 -1
- data/lib/action_controller/polymorphic_routes.rb +32 -17
- data/lib/action_controller/record_identifier.rb +10 -4
- data/lib/action_controller/request.rb +46 -30
- data/lib/action_controller/request_forgery_protection.rb +10 -9
- data/lib/action_controller/request_profiler.rb +29 -8
- data/lib/action_controller/rescue.rb +24 -24
- data/lib/action_controller/resources.rb +66 -23
- data/lib/action_controller/response.rb +2 -2
- data/lib/action_controller/routing.rb +113 -1229
- data/lib/action_controller/routing/builder.rb +204 -0
- data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
- data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
- data/lib/action_controller/routing/route.rb +240 -0
- data/lib/action_controller/routing/route_set.rb +435 -0
- data/lib/action_controller/routing/routing_ext.rb +46 -0
- data/lib/action_controller/routing/segments.rb +283 -0
- data/lib/action_controller/session/active_record_store.rb +13 -8
- data/lib/action_controller/session/cookie_store.rb +20 -17
- data/lib/action_controller/session_management.rb +10 -3
- data/lib/action_controller/streaming.rb +45 -31
- data/lib/action_controller/test_case.rb +33 -23
- data/lib/action_controller/test_process.rb +39 -35
- data/lib/action_controller/url_rewriter.rb +18 -12
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +2 -2
- data/lib/action_view.rb +11 -3
- data/lib/action_view/base.rb +73 -390
- data/lib/action_view/helpers/active_record_helper.rb +83 -62
- data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
- data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
- data/lib/action_view/helpers/benchmark_helper.rb +5 -3
- data/lib/action_view/helpers/cache_helper.rb +3 -2
- data/lib/action_view/helpers/capture_helper.rb +1 -2
- data/lib/action_view/helpers/date_helper.rb +104 -82
- data/lib/action_view/helpers/form_helper.rb +148 -75
- data/lib/action_view/helpers/form_options_helper.rb +44 -23
- data/lib/action_view/helpers/form_tag_helper.rb +22 -13
- data/lib/action_view/helpers/javascripts/controls.js +1 -1
- data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
- data/lib/action_view/helpers/javascripts/effects.js +1 -1
- data/lib/action_view/helpers/number_helper.rb +10 -3
- data/lib/action_view/helpers/prototype_helper.rb +61 -29
- data/lib/action_view/helpers/record_tag_helper.rb +3 -3
- data/lib/action_view/helpers/sanitize_helper.rb +23 -17
- data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
- data/lib/action_view/helpers/text_helper.rb +153 -125
- data/lib/action_view/helpers/url_helper.rb +83 -28
- data/lib/action_view/inline_template.rb +20 -0
- data/lib/action_view/partial_template.rb +70 -0
- data/lib/action_view/partials.rb +31 -73
- data/lib/action_view/template.rb +127 -0
- data/lib/action_view/template_error.rb +8 -7
- data/lib/action_view/template_finder.rb +177 -0
- data/lib/action_view/template_handler.rb +18 -1
- data/lib/action_view/template_handlers/builder.rb +10 -2
- data/lib/action_view/template_handlers/compilable.rb +128 -0
- data/lib/action_view/template_handlers/erb.rb +37 -2
- data/lib/action_view/template_handlers/rjs.rb +14 -1
- data/lib/action_view/test_case.rb +58 -0
- data/test/abstract_unit.rb +1 -1
- data/test/active_record_unit.rb +3 -6
- data/test/activerecord/active_record_store_test.rb +1 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
- data/test/adv_attr_test.rb +20 -0
- data/test/controller/action_pack_assertions_test.rb +16 -19
- data/test/controller/addresses_render_test.rb +1 -1
- data/test/controller/assert_select_test.rb +13 -6
- data/test/controller/base_test.rb +48 -2
- data/test/controller/benchmark_test.rb +1 -2
- data/test/controller/caching_test.rb +282 -21
- data/test/controller/capture_test.rb +1 -1
- data/test/controller/cgi_test.rb +1 -1
- data/test/controller/components_test.rb +1 -1
- data/test/controller/content_type_test.rb +2 -2
- data/test/controller/cookie_test.rb +13 -2
- data/test/controller/custom_handler_test.rb +14 -10
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
- data/test/controller/dispatcher_test.rb +31 -49
- data/test/controller/fake_controllers.rb +17 -0
- data/test/controller/fake_models.rb +6 -0
- data/test/controller/filter_params_test.rb +14 -8
- data/test/controller/filters_test.rb +44 -16
- data/test/controller/flash_test.rb +2 -2
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +19 -15
- data/test/controller/html-scanner/document_test.rb +1 -2
- data/test/controller/html-scanner/node_test.rb +1 -2
- data/test/controller/html-scanner/sanitizer_test.rb +8 -5
- data/test/controller/html-scanner/tag_node_test.rb +1 -2
- data/test/controller/html-scanner/text_node_test.rb +2 -3
- data/test/controller/html-scanner/tokenizer_test.rb +8 -2
- data/test/controller/http_authentication_test.rb +1 -1
- data/test/controller/integration_test.rb +14 -16
- data/test/controller/integration_upload_test.rb +43 -0
- data/test/controller/layout_test.rb +26 -6
- data/test/controller/mime_responds_test.rb +39 -7
- data/test/controller/mime_type_test.rb +29 -5
- data/test/controller/new_render_test.rb +105 -34
- data/test/controller/polymorphic_routes_test.rb +32 -20
- data/test/controller/record_identifier_test.rb +38 -2
- data/test/controller/redirect_test.rb +21 -1
- data/test/controller/render_test.rb +59 -15
- data/test/controller/request_forgery_protection_test.rb +92 -5
- data/test/controller/request_test.rb +64 -6
- data/test/controller/rescue_test.rb +22 -6
- data/test/controller/resources_test.rb +102 -14
- data/test/controller/routing_test.rb +231 -19
- data/test/controller/selector_test.rb +2 -2
- data/test/controller/send_file_test.rb +14 -3
- data/test/controller/session/cookie_store_test.rb +16 -4
- data/test/controller/session/mem_cache_store_test.rb +3 -4
- data/test/controller/session_fixation_test.rb +1 -1
- data/test/controller/session_management_test.rb +23 -1
- data/test/controller/test_test.rb +39 -18
- data/test/controller/url_rewriter_test.rb +35 -1
- data/test/controller/verification_test.rb +1 -1
- data/test/controller/view_paths_test.rb +15 -12
- data/test/controller/webservice_test.rb +48 -3
- data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
- data/test/fixtures/company.rb +1 -0
- data/test/fixtures/customers/_customer.html.erb +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +6 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
- data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
- data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots.yml +4 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/multipart/boundary_problem_file +10 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/public/javascripts/controls.js +1 -0
- data/test/fixtures/public/javascripts/dragdrop.js +1 -0
- data/test/fixtures/public/javascripts/effects.js +1 -0
- data/test/fixtures/public/javascripts/prototype.js +1 -0
- data/test/fixtures/public/javascripts/version.1.0.js +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/reply.rb +1 -0
- data/test/fixtures/shared.html.erb +1 -0
- data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
- data/test/fixtures/test/_customer_counter.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_labelling_form.erb +1 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/greeting.js.rjs +1 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/template/active_record_helper_test.rb +25 -8
- data/test/template/asset_tag_helper_test.rb +100 -17
- data/test/template/atom_feed_helper_test.rb +29 -1
- data/test/template/benchmark_helper_test.rb +10 -22
- data/test/template/date_helper_test.rb +455 -153
- data/test/template/erb_util_test.rb +10 -42
- data/test/template/form_helper_test.rb +192 -66
- data/test/template/form_options_helper_test.rb +19 -8
- data/test/template/form_tag_helper_test.rb +11 -8
- data/test/template/javascript_helper_test.rb +3 -9
- data/test/template/number_helper_test.rb +6 -3
- data/test/template/prototype_helper_test.rb +27 -40
- data/test/template/record_tag_helper_test.rb +54 -0
- data/test/template/sanitize_helper_test.rb +5 -6
- data/test/template/scriptaculous_helper_test.rb +7 -13
- data/test/template/tag_helper_test.rb +3 -6
- data/test/template/template_finder_test.rb +73 -0
- data/test/template/template_object_test.rb +95 -0
- data/test/template/test_test.rb +56 -0
- data/test/template/text_helper_test.rb +46 -33
- data/test/template/url_helper_test.rb +8 -10
- metadata +65 -12
- data/lib/action_view/compiled_templates.rb +0 -69
- data/test/action_view_test.rb +0 -44
- data/test/activerecord/fixtures_test.rb +0 -24
- data/test/controller/fragment_store_setting_test.rb +0 -47
- data/test/template/compiled_templates_test.rb +0 -197
- data/test/template/deprecate_ivars_test.rb +0 -51
@@ -16,7 +16,30 @@ module ActionController
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class TestCase < ActiveSupport::TestCase
|
19
|
+
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
|
20
|
+
# (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
|
21
|
+
# rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
|
22
|
+
# than 0.0.0.0.
|
23
|
+
#
|
24
|
+
# The exception is stored in the exception accessor for further inspection.
|
25
|
+
module RaiseActionExceptions
|
26
|
+
attr_accessor :exception
|
27
|
+
|
28
|
+
def rescue_action(e)
|
29
|
+
self.exception = e
|
30
|
+
|
31
|
+
if request.remote_addr == "0.0.0.0"
|
32
|
+
raise(e)
|
33
|
+
else
|
34
|
+
super(e)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
setup :setup_controller_request_and_response
|
40
|
+
|
19
41
|
@@controller_class = nil
|
42
|
+
|
20
43
|
class << self
|
21
44
|
def tests(controller_class)
|
22
45
|
self.controller_class = controller_class
|
@@ -31,7 +54,7 @@ module ActionController
|
|
31
54
|
if current_controller_class = read_inheritable_attribute(:controller_class)
|
32
55
|
current_controller_class
|
33
56
|
else
|
34
|
-
self.controller_class= determine_default_controller_class(name)
|
57
|
+
self.controller_class = determine_default_controller_class(name)
|
35
58
|
end
|
36
59
|
end
|
37
60
|
|
@@ -42,32 +65,19 @@ module ActionController
|
|
42
65
|
end
|
43
66
|
|
44
67
|
def prepare_controller_class(new_class)
|
45
|
-
new_class.
|
46
|
-
def rescue_action(e)
|
47
|
-
raise e
|
48
|
-
end
|
49
|
-
end
|
68
|
+
new_class.send :include, RaiseActionExceptions
|
50
69
|
end
|
51
70
|
end
|
52
71
|
|
53
|
-
def
|
72
|
+
def setup_controller_request_and_response
|
54
73
|
@controller = self.class.controller_class.new
|
55
|
-
@request
|
56
|
-
@response
|
74
|
+
@controller.request = @request = TestRequest.new
|
75
|
+
@response = TestResponse.new
|
57
76
|
end
|
58
|
-
|
59
|
-
|
60
|
-
def
|
61
|
-
|
62
|
-
unless method_defined?(:setup_without_controller)
|
63
|
-
alias_method :setup_without_controller, :setup
|
64
|
-
define_method(:setup) do
|
65
|
-
setup_with_fixtures if respond_to?(:setup_with_fixtures)
|
66
|
-
setup_with_controller
|
67
|
-
setup_without_controller
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
77
|
+
|
78
|
+
# Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
|
79
|
+
def rescue_action_in_public!
|
80
|
+
@request.remote_addr = '208.77.188.166' # example.com
|
71
81
|
end
|
72
82
|
end
|
73
|
-
end
|
83
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'action_controller/assertions'
|
2
|
+
require 'action_controller/test_case'
|
2
3
|
|
3
4
|
module ActionController #:nodoc:
|
4
5
|
class Base
|
5
|
-
# Process a test request called with a
|
6
|
+
# Process a test request called with a TestRequest object.
|
6
7
|
def self.process_test(request)
|
7
8
|
new.process_test(request)
|
8
9
|
end
|
@@ -48,7 +49,7 @@ module ActionController #:nodoc:
|
|
48
49
|
# Either the RAW_POST_DATA environment variable or the URL-encoded request
|
49
50
|
# parameters.
|
50
51
|
def raw_post
|
51
|
-
env['RAW_POST_DATA'] ||= url_encoded_request_parameters
|
52
|
+
env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) }
|
52
53
|
end
|
53
54
|
|
54
55
|
def port=(number)
|
@@ -154,12 +155,12 @@ module ActionController #:nodoc:
|
|
154
155
|
# A refactoring of TestResponse to allow the same behavior to be applied
|
155
156
|
# to the "real" CgiResponse class in integration tests.
|
156
157
|
module TestResponseBehavior #:nodoc:
|
157
|
-
#
|
158
|
+
# The response code of the request
|
158
159
|
def response_code
|
159
160
|
headers['Status'][0,3].to_i rescue 0
|
160
161
|
end
|
161
162
|
|
162
|
-
#
|
163
|
+
# Returns a String to ensure compatibility with Net::HTTPResponse
|
163
164
|
def code
|
164
165
|
headers['Status'].to_s.split(' ')[0]
|
165
166
|
end
|
@@ -168,34 +169,34 @@ module ActionController #:nodoc:
|
|
168
169
|
headers['Status'].to_s.split(' ',2)[1]
|
169
170
|
end
|
170
171
|
|
171
|
-
#
|
172
|
+
# Was the response successful?
|
172
173
|
def success?
|
173
174
|
response_code == 200
|
174
175
|
end
|
175
176
|
|
176
|
-
#
|
177
|
+
# Was the URL not found?
|
177
178
|
def missing?
|
178
179
|
response_code == 404
|
179
180
|
end
|
180
181
|
|
181
|
-
#
|
182
|
+
# Were we redirected?
|
182
183
|
def redirect?
|
183
184
|
(300..399).include?(response_code)
|
184
185
|
end
|
185
186
|
|
186
|
-
#
|
187
|
+
# Was there a server-side error?
|
187
188
|
def error?
|
188
189
|
(500..599).include?(response_code)
|
189
190
|
end
|
190
191
|
|
191
192
|
alias_method :server_error?, :error?
|
192
193
|
|
193
|
-
#
|
194
|
+
# Returns the redirection location or nil
|
194
195
|
def redirect_url
|
195
196
|
headers['Location']
|
196
197
|
end
|
197
198
|
|
198
|
-
#
|
199
|
+
# Does the redirect location match this regexp pattern?
|
199
200
|
def redirect_url_match?( pattern )
|
200
201
|
return false if redirect_url.nil?
|
201
202
|
p = Regexp.new(pattern) if pattern.class == String
|
@@ -204,7 +205,7 @@ module ActionController #:nodoc:
|
|
204
205
|
p.match(redirect_url) != nil
|
205
206
|
end
|
206
207
|
|
207
|
-
#
|
208
|
+
# Returns the template path of the file which was used to
|
208
209
|
# render this response (or nil)
|
209
210
|
def rendered_file(with_controller=false)
|
210
211
|
unless template.first_render.nil?
|
@@ -216,50 +217,49 @@ module ActionController #:nodoc:
|
|
216
217
|
end
|
217
218
|
end
|
218
219
|
|
219
|
-
#
|
220
|
+
# Was this template rendered by a file?
|
220
221
|
def rendered_with_file?
|
221
222
|
!rendered_file.nil?
|
222
223
|
end
|
223
224
|
|
224
|
-
#
|
225
|
+
# A shortcut to the flash. Returns an empyt hash if no session flash exists.
|
225
226
|
def flash
|
226
227
|
session['flash'] || {}
|
227
228
|
end
|
228
229
|
|
229
|
-
#
|
230
|
+
# Do we have a flash?
|
230
231
|
def has_flash?
|
231
232
|
!session['flash'].empty?
|
232
233
|
end
|
233
234
|
|
234
|
-
#
|
235
|
+
# Do we have a flash that has contents?
|
235
236
|
def has_flash_with_contents?
|
236
237
|
!flash.empty?
|
237
238
|
end
|
238
239
|
|
239
|
-
#
|
240
|
+
# Does the specified flash object exist?
|
240
241
|
def has_flash_object?(name=nil)
|
241
242
|
!flash[name].nil?
|
242
243
|
end
|
243
244
|
|
244
|
-
#
|
245
|
+
# Does the specified object exist in the session?
|
245
246
|
def has_session_object?(name=nil)
|
246
247
|
!session[name].nil?
|
247
248
|
end
|
248
249
|
|
249
|
-
#
|
250
|
+
# A shortcut to the template.assigns
|
250
251
|
def template_objects
|
251
252
|
template.assigns || {}
|
252
253
|
end
|
253
254
|
|
254
|
-
#
|
255
|
+
# Does the specified template object exist?
|
255
256
|
def has_template_object?(name=nil)
|
256
257
|
!template_objects[name].nil?
|
257
258
|
end
|
258
259
|
|
259
260
|
# Returns the response cookies, converted to a Hash of (name => CGI::Cookie) pairs
|
260
|
-
# Example:
|
261
261
|
#
|
262
|
-
#
|
262
|
+
# assert_equal ['AuthorOfNewPage'], r.cookies['author'].value
|
263
263
|
def cookies
|
264
264
|
headers['cookie'].inject({}) { |hash, cookie| hash[cookie.name] = cookie; hash }
|
265
265
|
end
|
@@ -340,6 +340,7 @@ module ActionController #:nodoc:
|
|
340
340
|
@content_type = content_type
|
341
341
|
@original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
|
342
342
|
@tempfile = Tempfile.new(@original_filename)
|
343
|
+
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
343
344
|
@tempfile.binmode if binary
|
344
345
|
FileUtils.copy_file(path, @tempfile.path)
|
345
346
|
end
|
@@ -357,7 +358,7 @@ module ActionController #:nodoc:
|
|
357
358
|
|
358
359
|
module TestProcess
|
359
360
|
def self.included(base)
|
360
|
-
# execute the request simulating a specific
|
361
|
+
# execute the request simulating a specific HTTP method and set/volley the response
|
361
362
|
%w( get post put delete head ).each do |method|
|
362
363
|
base.class_eval <<-EOV, __FILE__, __LINE__
|
363
364
|
def #{method}(action, parameters = nil, session = nil, flash = nil)
|
@@ -373,7 +374,7 @@ module ActionController #:nodoc:
|
|
373
374
|
# Sanity check for required instance variables so we can give an
|
374
375
|
# understandable error message.
|
375
376
|
%w(@controller @request @response).each do |iv_name|
|
376
|
-
if !(
|
377
|
+
if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
|
377
378
|
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
|
378
379
|
end
|
379
380
|
end
|
@@ -464,10 +465,13 @@ module ActionController #:nodoc:
|
|
464
465
|
return super
|
465
466
|
end
|
466
467
|
|
467
|
-
# Shortcut for ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type)
|
468
|
+
# Shortcut for <tt>ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type)</tt>:
|
469
|
+
#
|
468
470
|
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
|
469
471
|
#
|
470
|
-
# To upload binary files on Windows, pass
|
472
|
+
# To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
|
473
|
+
# This will not affect other platforms:
|
474
|
+
#
|
471
475
|
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
|
472
476
|
def fixture_file_upload(path, mime_type = nil, binary = false)
|
473
477
|
ActionController::TestUploadedFile.new(
|
@@ -482,17 +486,17 @@ module ActionController #:nodoc:
|
|
482
486
|
# with a new RouteSet instance.
|
483
487
|
#
|
484
488
|
# The new instance is yielded to the passed block. Typically the block
|
485
|
-
# will create some routes using map.draw { map.connect ... }
|
489
|
+
# will create some routes using <tt>map.draw { map.connect ... }</tt>:
|
486
490
|
#
|
487
|
-
#
|
488
|
-
#
|
489
|
-
#
|
490
|
-
#
|
491
|
-
#
|
492
|
-
#
|
493
|
-
#
|
494
|
-
#
|
495
|
-
#
|
491
|
+
# with_routing do |set|
|
492
|
+
# set.draw do |map|
|
493
|
+
# map.connect ':controller/:action/:id'
|
494
|
+
# assert_equal(
|
495
|
+
# ['/content/10/show', {}],
|
496
|
+
# map.generate(:controller => 'content', :id => 10, :action => 'show')
|
497
|
+
# end
|
498
|
+
# end
|
499
|
+
# end
|
496
500
|
#
|
497
501
|
def with_routing
|
498
502
|
real_routes = ActionController::Routing::Routes
|
@@ -15,8 +15,8 @@ module ActionController
|
|
15
15
|
# In addition to providing +url_for+, named routes are also accessible after
|
16
16
|
# including UrlWriter.
|
17
17
|
module UrlWriter
|
18
|
-
# The default options for urls written by this writer. Typically a
|
19
|
-
# is provided.
|
18
|
+
# The default options for urls written by this writer. Typically a <tt>:host</tt>
|
19
|
+
# pair is provided.
|
20
20
|
mattr_accessor :default_url_options
|
21
21
|
self.default_url_options = {}
|
22
22
|
|
@@ -29,20 +29,25 @@ module ActionController
|
|
29
29
|
# Generate a url based on the options provided, default_url_options and the
|
30
30
|
# routes defined in routes.rb. The following options are supported:
|
31
31
|
#
|
32
|
-
# * <tt>:only_path</tt> If true, the relative url is returned. Defaults to false
|
33
|
-
# * <tt>:protocol</tt> The protocol to connect to. Defaults to 'http'.
|
34
|
-
# * <tt>:host</tt> Specifies the host the link should be targetted at.
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# * <tt>:
|
38
|
-
# * <tt>:
|
32
|
+
# * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
|
33
|
+
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
|
34
|
+
# * <tt>:host</tt> - Specifies the host the link should be targetted at.
|
35
|
+
# If <tt>:only_path</tt> is false, this option must be
|
36
|
+
# provided either explicitly, or via +default_url_options+.
|
37
|
+
# * <tt>:port</tt> - Optionally specify the port to connect to.
|
38
|
+
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
|
39
|
+
# * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
|
40
|
+
# +relative_url_root+ set in ActionController::AbstractRequest.relative_url_root.
|
41
|
+
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
|
39
42
|
#
|
40
|
-
# Any other key(
|
43
|
+
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
|
44
|
+
# +url_for+ is forwarded to the Routes module.
|
41
45
|
#
|
42
46
|
# Examples:
|
43
47
|
#
|
44
48
|
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
|
45
49
|
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
|
50
|
+
# url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
|
46
51
|
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
|
47
52
|
def url_for(options)
|
48
53
|
options = self.class.default_url_options.merge(options)
|
@@ -61,10 +66,11 @@ module ActionController
|
|
61
66
|
# Delete the unused options to prevent their appearance in the query string.
|
62
67
|
[:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
|
63
68
|
end
|
64
|
-
|
69
|
+
trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
|
65
70
|
url << ActionController::AbstractRequest.relative_url_root.to_s unless options[:skip_relative_url_root]
|
66
71
|
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
|
67
|
-
|
72
|
+
generated = Routing::Routes.generate(options, {})
|
73
|
+
url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
|
68
74
|
url << anchor if anchor
|
69
75
|
|
70
76
|
url
|
@@ -54,7 +54,7 @@ module HTML #:nodoc:
|
|
54
54
|
tag << (@scanner.scan_until(/--\s*>/) || @scanner.scan_until(/\Z/))
|
55
55
|
elsif @scanner.scan(/!\[CDATA\[/)
|
56
56
|
tag << @scanner.matched
|
57
|
-
tag << @scanner.scan_until(/\]\]>/)
|
57
|
+
tag << (@scanner.scan_until(/\]\]>/) || @scanner.scan_until(/\Z/))
|
58
58
|
elsif @scanner.scan(/!/) # doctype
|
59
59
|
tag << @scanner.matched
|
60
60
|
tag << consume_quoted_regions
|
data/lib/action_pack.rb
CHANGED
data/lib/action_pack/version.rb
CHANGED
data/lib/action_view.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2004-
|
2
|
+
# Copyright (c) 2004-2008 David Heinemeier Hansson
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -22,16 +22,24 @@
|
|
22
22
|
#++
|
23
23
|
|
24
24
|
require 'action_view/template_handler'
|
25
|
+
require 'action_view/template_handlers/compilable'
|
25
26
|
require 'action_view/template_handlers/builder'
|
26
27
|
require 'action_view/template_handlers/erb'
|
27
28
|
require 'action_view/template_handlers/rjs'
|
28
29
|
|
30
|
+
require 'action_view/template_finder'
|
31
|
+
require 'action_view/template'
|
32
|
+
require 'action_view/partial_template'
|
33
|
+
require 'action_view/inline_template'
|
34
|
+
|
29
35
|
require 'action_view/base'
|
30
36
|
require 'action_view/partials'
|
31
37
|
require 'action_view/template_error'
|
32
38
|
|
33
39
|
ActionView::Base.class_eval do
|
34
40
|
include ActionView::Partials
|
35
|
-
end
|
36
41
|
|
37
|
-
ActionView::Base.
|
42
|
+
ActionView::Base.helper_modules.each do |helper_module|
|
43
|
+
include helper_module
|
44
|
+
end
|
45
|
+
end
|
data/lib/action_view/base.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
module ActionView #:nodoc:
|
2
2
|
class ActionViewError < StandardError #:nodoc:
|
3
3
|
end
|
4
|
+
|
5
|
+
class MissingTemplate < ActionViewError #:nodoc:
|
6
|
+
end
|
4
7
|
|
5
|
-
# Action View templates can be written in three ways. If the template file has a
|
6
|
-
# (included in Ruby) and HTML. If the template file has a
|
7
|
-
# If the template file has a
|
8
|
+
# Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
|
9
|
+
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
|
10
|
+
# If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
|
8
11
|
#
|
9
12
|
# = ERb
|
10
13
|
#
|
@@ -21,7 +24,7 @@ module ActionView #:nodoc:
|
|
21
24
|
#
|
22
25
|
# Hi, Mr. <% puts "Frodo" %>
|
23
26
|
#
|
24
|
-
# If you absolutely must write from within a function, you can use the TextHelper#concat
|
27
|
+
# If you absolutely must write from within a function, you can use the TextHelper#concat.
|
25
28
|
#
|
26
29
|
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
|
27
30
|
#
|
@@ -43,7 +46,7 @@ module ActionView #:nodoc:
|
|
43
46
|
# <% @page_title = "A Wonderful Hello" %>
|
44
47
|
# <%= render "shared/header" %>
|
45
48
|
#
|
46
|
-
# Now the header can pick up on the
|
49
|
+
# Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
|
47
50
|
#
|
48
51
|
# <title><%= @page_title %></title>
|
49
52
|
#
|
@@ -53,7 +56,7 @@ module ActionView #:nodoc:
|
|
53
56
|
#
|
54
57
|
# <%= render "shared/header", { :headline => "Welcome", :person => person } %>
|
55
58
|
#
|
56
|
-
# These can now be accessed in shared/header with:
|
59
|
+
# These can now be accessed in <tt>shared/header</tt> with:
|
57
60
|
#
|
58
61
|
# Headline: <%= headline %>
|
59
62
|
# First name: <%= person.first_name %>
|
@@ -74,13 +77,13 @@ module ActionView #:nodoc:
|
|
74
77
|
#
|
75
78
|
# == Builder
|
76
79
|
#
|
77
|
-
# Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An
|
78
|
-
# named +xml+ is automatically made available to templates with a
|
80
|
+
# Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
|
81
|
+
# named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
|
79
82
|
#
|
80
83
|
# Here are some basic examples:
|
81
84
|
#
|
82
85
|
# xml.em("emphasized") # => <em>emphasized</em>
|
83
|
-
# xml.em { xml.b("emph & bold") }
|
86
|
+
# xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em>
|
84
87
|
# xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
|
85
88
|
# xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
|
86
89
|
# # NOTE: order of attributes is not specified.
|
@@ -127,18 +130,18 @@ module ActionView #:nodoc:
|
|
127
130
|
#
|
128
131
|
# == JavaScriptGenerator
|
129
132
|
#
|
130
|
-
# JavaScriptGenerator templates end in
|
133
|
+
# JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
|
131
134
|
# render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
|
132
135
|
# modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
|
133
136
|
# and make updates to the page where the request originated from.
|
134
137
|
#
|
135
138
|
# An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
|
136
139
|
#
|
137
|
-
# When an
|
140
|
+
# When an <tt>.rjs</tt> action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
|
138
141
|
#
|
139
142
|
# link_to_remote :url => {:action => 'delete'}
|
140
143
|
#
|
141
|
-
# The subsequently rendered
|
144
|
+
# The subsequently rendered <tt>delete.rjs</tt> might look like:
|
142
145
|
#
|
143
146
|
# page.replace_html 'sidebar', :partial => 'sidebar'
|
144
147
|
# page.remove "person-#{@person.id}"
|
@@ -150,16 +153,12 @@ module ActionView #:nodoc:
|
|
150
153
|
class Base
|
151
154
|
include ERB::Util
|
152
155
|
|
153
|
-
attr_reader :
|
154
|
-
attr_accessor :base_path, :assigns, :template_extension
|
155
|
-
attr_accessor :controller
|
156
|
-
|
157
|
-
attr_reader :logger, :response, :headers
|
158
|
-
attr_internal :cookies, :flash, :headers, :params, :request, :response, :session
|
159
|
-
|
160
|
-
delegate :logger, :action_name, :to => :controller
|
156
|
+
attr_reader :finder
|
157
|
+
attr_accessor :base_path, :assigns, :template_extension, :first_render
|
158
|
+
attr_accessor :controller
|
161
159
|
|
162
160
|
attr_writer :template_format
|
161
|
+
attr_accessor :current_render_extension
|
163
162
|
|
164
163
|
# Specify trim mode for the ERB compiler. Defaults to '-'.
|
165
164
|
# See ERb documentation for suitable values.
|
@@ -169,18 +168,12 @@ module ActionView #:nodoc:
|
|
169
168
|
# Specify whether file modification times should be checked to see if a template needs recompilation
|
170
169
|
@@cache_template_loading = false
|
171
170
|
cattr_accessor :cache_template_loading
|
172
|
-
|
173
|
-
# Specify whether file extension lookup should be cached, and whether template base path lookup should be cached.
|
174
|
-
# Should be +false+ for development environments. Defaults to +true+.
|
175
|
-
@@cache_template_extensions = true
|
176
|
-
cattr_accessor :cache_template_extensions
|
177
|
-
|
178
|
-
# Specify whether local_assigns should be able to use string keys.
|
179
|
-
# Defaults to +true+. String keys are deprecated and will be removed
|
180
|
-
# shortly.
|
181
|
-
@@local_assigns_support_string_keys = true
|
182
|
-
cattr_accessor :local_assigns_support_string_keys
|
183
171
|
|
172
|
+
def self.cache_template_extensions=(*args)
|
173
|
+
ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no affect. " <<
|
174
|
+
"Please remove it from your config files.", caller)
|
175
|
+
end
|
176
|
+
|
184
177
|
# Specify whether RJS responses should be wrapped in a try/catch block
|
185
178
|
# that alert()s the caught exception (and then re-raises it).
|
186
179
|
@@debug_rjs = false
|
@@ -189,89 +182,47 @@ module ActionView #:nodoc:
|
|
189
182
|
@@erb_variable = '_erbout'
|
190
183
|
cattr_accessor :erb_variable
|
191
184
|
|
192
|
-
|
185
|
+
attr_internal :request
|
193
186
|
|
194
|
-
|
187
|
+
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
|
188
|
+
:flash, :logger, :action_name, :to => :controller
|
195
189
|
|
196
190
|
module CompiledTemplates #:nodoc:
|
197
191
|
# holds compiled template code
|
198
192
|
end
|
199
193
|
include CompiledTemplates
|
200
194
|
|
201
|
-
# Maps inline templates to their method names
|
195
|
+
# Maps inline templates to their method names
|
196
|
+
cattr_accessor :method_names
|
202
197
|
@@method_names = {}
|
203
|
-
# Map method names to their compile time
|
204
|
-
@@compile_time = {}
|
205
198
|
# Map method names to the names passed in local assigns so far
|
206
199
|
@@template_args = {}
|
207
|
-
# Count the number of inline templates
|
208
|
-
@@inline_template_count = 0
|
209
|
-
# Maps template paths without extension to their file extension returned by pick_template_extension.
|
210
|
-
# If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
|
211
|
-
# used by pick_template_extension determines whether ext1 or ext2 will be stored.
|
212
|
-
@@cached_template_extension = {}
|
213
|
-
# Maps template paths / extensions to
|
214
|
-
@@cached_base_paths = {}
|
215
200
|
|
216
201
|
# Cache public asset paths
|
217
202
|
cattr_reader :computed_public_paths
|
218
203
|
@@computed_public_paths = {}
|
219
204
|
|
220
|
-
@@template_handlers = {}
|
221
|
-
@@default_template_handlers = nil
|
222
|
-
|
223
205
|
class ObjectWrapper < Struct.new(:value) #:nodoc:
|
224
206
|
end
|
225
207
|
|
226
|
-
def self.
|
227
|
-
|
208
|
+
def self.helper_modules #:nodoc:
|
209
|
+
helpers = []
|
210
|
+
Dir.entries(File.expand_path("#{File.dirname(__FILE__)}/helpers")).sort.each do |file|
|
228
211
|
next unless file =~ /^([a-z][a-z_]*_helper).rb$/
|
229
212
|
require "action_view/helpers/#{$1}"
|
230
213
|
helper_module_name = $1.camelize
|
231
214
|
if Helpers.const_defined?(helper_module_name)
|
232
|
-
|
215
|
+
helpers << Helpers.const_get(helper_module_name)
|
233
216
|
end
|
234
217
|
end
|
218
|
+
return helpers
|
235
219
|
end
|
236
220
|
|
237
|
-
# Register a class that knows how to handle template files with the given
|
238
|
-
# extension. This can be used to implement new template types.
|
239
|
-
# The constructor for the class must take the ActiveView::Base instance
|
240
|
-
# as a parameter, and the class must implement a #render method that
|
241
|
-
# takes the contents of the template to render as well as the Hash of
|
242
|
-
# local assigns available to the template. The #render method ought to
|
243
|
-
# return the rendered template as a string.
|
244
|
-
def self.register_template_handler(extension, klass)
|
245
|
-
@@template_handlers[extension.to_sym] = klass
|
246
|
-
end
|
247
|
-
|
248
|
-
def self.template_handler_extensions
|
249
|
-
@@template_handler_extensions ||= @@template_handlers.keys.map(&:to_s).sort
|
250
|
-
end
|
251
|
-
|
252
|
-
def self.register_default_template_handler(extension, klass)
|
253
|
-
register_template_handler(extension, klass)
|
254
|
-
@@default_template_handlers = klass
|
255
|
-
end
|
256
|
-
|
257
|
-
def self.handler_for_extension(extension)
|
258
|
-
(extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers
|
259
|
-
end
|
260
|
-
|
261
|
-
register_default_template_handler :erb, TemplateHandlers::ERB
|
262
|
-
register_template_handler :rjs, TemplateHandlers::RJS
|
263
|
-
register_template_handler :builder, TemplateHandlers::Builder
|
264
|
-
|
265
|
-
# TODO: Depreciate old template extensions
|
266
|
-
register_template_handler :rhtml, TemplateHandlers::ERB
|
267
|
-
register_template_handler :rxml, TemplateHandlers::Builder
|
268
|
-
|
269
221
|
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
|
270
|
-
@view_paths = view_paths.respond_to?(:find) ? view_paths.dup : [*view_paths].compact
|
271
222
|
@assigns = assigns_for_first_render
|
272
223
|
@assigns_added = nil
|
273
224
|
@controller = controller
|
274
|
-
@
|
225
|
+
@finder = TemplateFinder.new(self, view_paths)
|
275
226
|
end
|
276
227
|
|
277
228
|
# Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
|
@@ -291,61 +242,27 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
|
291
242
|
END_ERROR
|
292
243
|
end
|
293
244
|
|
294
|
-
|
295
|
-
template_path_without_extension, template_extension = path_and_extension(template_path)
|
296
|
-
if use_full_path
|
297
|
-
if template_extension
|
298
|
-
template_file_name = full_template_path(template_path_without_extension, template_extension)
|
299
|
-
else
|
300
|
-
template_extension = pick_template_extension(template_path).to_s
|
301
|
-
unless template_extension
|
302
|
-
raise ActionViewError, "No template found for #{template_path} in #{view_paths.inspect}"
|
303
|
-
end
|
304
|
-
template_file_name = full_template_path(template_path, template_extension)
|
305
|
-
template_extension = template_extension.gsub(/^.+\./, '') # strip off any formats
|
306
|
-
end
|
307
|
-
else
|
308
|
-
template_file_name = template_path
|
309
|
-
end
|
310
|
-
|
311
|
-
template_source = nil # Don't read the source until we know that it is required
|
312
|
-
|
313
|
-
if template_file_name.blank?
|
314
|
-
raise ActionViewError, "Couldn't find template file for #{template_path} in #{view_paths.inspect}"
|
315
|
-
end
|
316
|
-
|
317
|
-
begin
|
318
|
-
render_template(template_extension, template_source, template_file_name, local_assigns)
|
319
|
-
rescue Exception => e
|
320
|
-
if TemplateError === e
|
321
|
-
e.sub_template_of(template_file_name)
|
322
|
-
raise e
|
323
|
-
else
|
324
|
-
raise TemplateError.new(find_base_path_for("#{template_path_without_extension}.#{template_extension}") || view_paths.first, template_file_name, @assigns, template_source, e)
|
325
|
-
end
|
326
|
-
end
|
245
|
+
Template.new(self, template_path, use_full_path, local_assigns).render_template
|
327
246
|
end
|
328
247
|
|
329
248
|
# Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
|
330
249
|
# The hash in <tt>local_assigns</tt> is made available as local variables.
|
331
|
-
def render(options = {},
|
250
|
+
def render(options = {}, local_assigns = {}, &block) #:nodoc:
|
332
251
|
if options.is_a?(String)
|
333
|
-
render_file(options, true,
|
252
|
+
render_file(options, true, local_assigns)
|
334
253
|
elsif options == :update
|
335
254
|
update_page(&block)
|
336
255
|
elsif options.is_a?(Hash)
|
337
256
|
options = options.reverse_merge(:locals => {}, :use_full_path => true)
|
338
257
|
|
339
|
-
if options
|
340
|
-
path, partial_name = partial_pieces(options.delete(:layout))
|
341
|
-
|
258
|
+
if partial_layout = options.delete(:layout)
|
342
259
|
if block_given?
|
343
260
|
wrap_content_for_layout capture(&block) do
|
344
|
-
concat(render(options.merge(:partial =>
|
261
|
+
concat(render(options.merge(:partial => partial_layout)), block.binding)
|
345
262
|
end
|
346
263
|
else
|
347
264
|
wrap_content_for_layout render(options) do
|
348
|
-
render(options.merge(:partial =>
|
265
|
+
render(options.merge(:partial => partial_layout))
|
349
266
|
end
|
350
267
|
end
|
351
268
|
elsif options[:file]
|
@@ -355,61 +272,14 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
|
355
272
|
elsif options[:partial]
|
356
273
|
render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
|
357
274
|
elsif options[:inline]
|
358
|
-
|
275
|
+
template = InlineTemplate.new(self, options[:inline], options[:locals], options[:type])
|
276
|
+
render_template(template)
|
359
277
|
end
|
360
278
|
end
|
361
279
|
end
|
362
280
|
|
363
|
-
|
364
|
-
|
365
|
-
def render_template(template_extension, template, file_path = nil, local_assigns = {}) #:nodoc:
|
366
|
-
handler = self.class.handler_for_extension(template_extension)
|
367
|
-
|
368
|
-
if template_handler_is_compilable?(handler)
|
369
|
-
compile_and_render_template(handler, template, file_path, local_assigns)
|
370
|
-
else
|
371
|
-
template ||= read_template_file(file_path, template_extension) # Make sure that a lazyily-read template is loaded.
|
372
|
-
delegate_render(handler, template, local_assigns)
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
# Gets the full template path with base path for the given template_path and extension.
|
377
|
-
#
|
378
|
-
# full_template_path('users/show', 'html.erb')
|
379
|
-
# # => '~/rails/app/views/users/show.html.erb
|
380
|
-
#
|
381
|
-
def full_template_path(template_path, extension)
|
382
|
-
if @@cache_template_extensions
|
383
|
-
(@@cached_base_paths[template_path] ||= {})[extension.to_s] ||= find_full_template_path(template_path, extension)
|
384
|
-
else
|
385
|
-
find_full_template_path(template_path, extension)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
# Gets the extension for an existing template with the given template_path.
|
390
|
-
# Returns the format with the extension if that template exists.
|
391
|
-
#
|
392
|
-
# pick_template_extension('users/show')
|
393
|
-
# # => 'html.erb'
|
394
|
-
#
|
395
|
-
# pick_template_extension('users/legacy')
|
396
|
-
# # => "rhtml"
|
397
|
-
#
|
398
|
-
def pick_template_extension(template_path)#:nodoc:
|
399
|
-
if @@cache_template_extensions
|
400
|
-
(@@cached_template_extension[template_path] ||= {})[template_format] ||= find_template_extension_for(template_path)
|
401
|
-
else
|
402
|
-
find_template_extension_for(template_path)
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
def file_exists?(template_path)#:nodoc:
|
407
|
-
template_file_name, template_file_extension = path_and_extension(template_path)
|
408
|
-
if template_file_extension
|
409
|
-
template_exists?(template_file_name, template_file_extension)
|
410
|
-
else
|
411
|
-
template_exists?(template_file_name, pick_template_extension(template_path))
|
412
|
-
end
|
281
|
+
def render_template(template) #:nodoc:
|
282
|
+
template.render_template
|
413
283
|
end
|
414
284
|
|
415
285
|
# Returns true is the file may be rendered implicitly.
|
@@ -417,31 +287,31 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
|
417
287
|
template_path.split('/').last[0,1] != '_'
|
418
288
|
end
|
419
289
|
|
420
|
-
# symbolized version of the
|
290
|
+
# Returns a symbolized version of the <tt>:format</tt> parameter of the request,
|
291
|
+
# or <tt>:html</tt> by default.
|
292
|
+
#
|
293
|
+
# EXCEPTION: If the <tt>:format</tt> parameter is not set, the Accept header will be examined for
|
294
|
+
# whether it contains the JavaScript mime type as its first priority. If that's the case,
|
295
|
+
# it will be used. This ensures that Ajax applications can use the same URL to support both
|
296
|
+
# JavaScript and non-JavaScript users.
|
421
297
|
def template_format
|
422
298
|
return @template_format if @template_format
|
423
|
-
format = controller && controller.respond_to?(:request) && controller.request.parameters[:format]
|
424
|
-
@template_format = format.blank? ? :html : format.to_sym
|
425
|
-
end
|
426
299
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
#
|
443
|
-
def append_view_path(path)
|
444
|
-
@view_paths.push(*path)
|
300
|
+
if controller && controller.respond_to?(:request)
|
301
|
+
parameter_format = controller.request.parameters[:format]
|
302
|
+
accept_format = controller.request.accepts.first
|
303
|
+
|
304
|
+
case
|
305
|
+
when parameter_format.blank? && accept_format != :js
|
306
|
+
@template_format = :html
|
307
|
+
when parameter_format.blank? && accept_format == :js
|
308
|
+
@template_format = :js
|
309
|
+
else
|
310
|
+
@template_format = parameter_format.to_sym
|
311
|
+
end
|
312
|
+
else
|
313
|
+
@template_format = :html
|
314
|
+
end
|
445
315
|
end
|
446
316
|
|
447
317
|
private
|
@@ -450,62 +320,6 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
|
450
320
|
@content_for_layout = content
|
451
321
|
returning(yield) { @content_for_layout = original_content_for_layout }
|
452
322
|
end
|
453
|
-
|
454
|
-
def find_full_template_path(template_path, extension)
|
455
|
-
file_name = "#{template_path}.#{extension}"
|
456
|
-
base_path = find_base_path_for(file_name)
|
457
|
-
base_path.blank? ? "" : "#{base_path}/#{file_name}"
|
458
|
-
end
|
459
|
-
|
460
|
-
# Asserts the existence of a template.
|
461
|
-
def template_exists?(template_path, extension)
|
462
|
-
file_path = full_template_path(template_path, extension)
|
463
|
-
!file_path.blank? && @@method_names.has_key?(file_path) || File.exist?(file_path)
|
464
|
-
end
|
465
|
-
|
466
|
-
# Splits the path and extension from the given template_path and returns as an array.
|
467
|
-
def path_and_extension(template_path)
|
468
|
-
template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
|
469
|
-
[ template_path_without_extension, $1 ]
|
470
|
-
end
|
471
|
-
|
472
|
-
# Returns the view path that contains the given relative template path.
|
473
|
-
def find_base_path_for(template_file_name)
|
474
|
-
view_paths.find { |p| File.file?(File.join(p, template_file_name)) }
|
475
|
-
end
|
476
|
-
|
477
|
-
# Returns the view path that the full path resides in.
|
478
|
-
def extract_base_path_from(full_path)
|
479
|
-
view_paths.find { |p| full_path[0..p.size - 1] == p }
|
480
|
-
end
|
481
|
-
|
482
|
-
# Determines the template's file extension, such as rhtml, rxml, or rjs.
|
483
|
-
def find_template_extension_for(template_path)
|
484
|
-
find_template_extension_from_handler(template_path, true) ||
|
485
|
-
find_template_extension_from_handler(template_path) ||
|
486
|
-
find_template_extension_from_first_render()
|
487
|
-
end
|
488
|
-
|
489
|
-
def find_template_extension_from_handler(template_path, formatted = nil)
|
490
|
-
checked_template_path = formatted ? "#{template_path}.#{template_format}" : template_path
|
491
|
-
|
492
|
-
self.class.template_handler_extensions.each do |extension|
|
493
|
-
if template_exists?(checked_template_path, extension)
|
494
|
-
return formatted ? "#{template_format}.#{extension}" : extension.to_s
|
495
|
-
end
|
496
|
-
end
|
497
|
-
nil
|
498
|
-
end
|
499
|
-
|
500
|
-
# Determine the template extension from the <tt>@first_render</tt> filename
|
501
|
-
def find_template_extension_from_first_render
|
502
|
-
File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1]
|
503
|
-
end
|
504
|
-
|
505
|
-
# This method reads a template file.
|
506
|
-
def read_template_file(template_path, extension)
|
507
|
-
File.read(template_path)
|
508
|
-
end
|
509
323
|
|
510
324
|
# Evaluate the local assigns and pushes them to the view.
|
511
325
|
def evaluate_assigns
|
@@ -515,146 +329,15 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
|
|
515
329
|
end
|
516
330
|
end
|
517
331
|
|
518
|
-
def delegate_render(handler, template, local_assigns)
|
519
|
-
handler.new(self).render(template, local_assigns)
|
520
|
-
end
|
521
|
-
|
522
|
-
def delegate_compile(handler, template)
|
523
|
-
handler.new(self).compile(template)
|
524
|
-
end
|
525
|
-
|
526
|
-
def template_handler_is_compilable?(handler)
|
527
|
-
handler.new(self).respond_to?(:compile)
|
528
|
-
end
|
529
|
-
|
530
332
|
# Assigns instance variables from the controller to the view.
|
531
333
|
def assign_variables_from_controller
|
532
|
-
@assigns.each
|
533
|
-
if ['logger'].include?(key)
|
534
|
-
instance_variable_set("@#{key}", ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, key.to_sym))
|
535
|
-
elsif ['action_name'].include?(key)
|
536
|
-
instance_variable_set("@#{key}", ActiveSupport::Deprecation::DeprecatedInstanceVariable.new(value, key))
|
537
|
-
else
|
538
|
-
instance_variable_set("@#{key}", value)
|
539
|
-
end
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
|
544
|
-
# Return true if the given template was compiled for a superset of the keys in local_assigns
|
545
|
-
def supports_local_assigns?(render_symbol, local_assigns)
|
546
|
-
local_assigns.empty? ||
|
547
|
-
((args = @@template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) })
|
334
|
+
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
|
548
335
|
end
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
def compile_template?(template, file_name, local_assigns)
|
555
|
-
method_key = file_name || template
|
556
|
-
render_symbol = @@method_names[method_key]
|
557
|
-
|
558
|
-
compile_time = @@compile_time[render_symbol]
|
559
|
-
if compile_time && supports_local_assigns?(render_symbol, local_assigns)
|
560
|
-
if file_name && !@@cache_template_loading
|
561
|
-
template_changed_since?(file_name, compile_time)
|
562
|
-
end
|
563
|
-
else
|
564
|
-
true
|
565
|
-
end
|
566
|
-
end
|
567
|
-
|
568
|
-
# Method to handle checking a whether a template has changed since last compile; isolated so that templates
|
569
|
-
# not stored on the file system can hook and extend appropriately.
|
570
|
-
def template_changed_since?(file_name, compile_time)
|
571
|
-
lstat = File.lstat(file_name)
|
572
|
-
compile_time < lstat.mtime ||
|
573
|
-
(lstat.symlink? && compile_time < File.stat(file_name).mtime)
|
574
|
-
end
|
575
|
-
|
576
|
-
# Method to create the source code for a given template.
|
577
|
-
def create_template_source(handler, template, render_symbol, locals)
|
578
|
-
body = delegate_compile(handler, template)
|
579
|
-
|
580
|
-
@@template_args[render_symbol] ||= {}
|
581
|
-
locals_keys = @@template_args[render_symbol].keys | locals
|
582
|
-
@@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }
|
583
|
-
|
584
|
-
locals_code = ""
|
585
|
-
locals_keys.each do |key|
|
586
|
-
locals_code << "#{key} = local_assigns[:#{key}]\n"
|
587
|
-
end
|
588
|
-
|
589
|
-
"def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
|
590
|
-
end
|
591
|
-
|
592
|
-
def assign_method_name(handler, template, file_name)
|
593
|
-
method_key = file_name || template
|
594
|
-
@@method_names[method_key] ||= compiled_method_name(handler, template, file_name)
|
595
|
-
end
|
596
|
-
|
597
|
-
def compiled_method_name(handler, template, file_name)
|
598
|
-
['_run', handler.to_s.demodulize.underscore, compiled_method_name_file_path_segment(file_name)].compact.join('_').to_sym
|
599
|
-
end
|
600
|
-
|
601
|
-
def compiled_method_name_file_path_segment(file_name)
|
602
|
-
if file_name
|
603
|
-
s = File.expand_path(file_name)
|
604
|
-
s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
|
605
|
-
s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }
|
606
|
-
s
|
607
|
-
else
|
608
|
-
(@@inline_template_count += 1).to_s
|
609
|
-
end
|
610
|
-
end
|
611
|
-
|
612
|
-
# Compile and evaluate the template's code
|
613
|
-
def compile_template(handler, template, file_name, local_assigns)
|
614
|
-
render_symbol = assign_method_name(handler, template, file_name)
|
615
|
-
render_source = create_template_source(handler, template, render_symbol, local_assigns.keys)
|
616
|
-
line_offset = @@template_args[render_symbol].size + handler.line_offset
|
617
|
-
|
618
|
-
begin
|
619
|
-
file_name = 'compiled-template' if file_name.blank?
|
620
|
-
CompiledTemplates.module_eval(render_source, file_name, -line_offset)
|
621
|
-
rescue Exception => e # errors from template code
|
622
|
-
if logger
|
623
|
-
logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
|
624
|
-
logger.debug "Function body: #{render_source}"
|
625
|
-
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
|
626
|
-
end
|
627
|
-
|
628
|
-
raise TemplateError.new(extract_base_path_from(file_name) || view_paths.first, file_name || template, @assigns, template, e)
|
629
|
-
end
|
630
|
-
|
631
|
-
@@compile_time[render_symbol] = Time.now
|
632
|
-
# logger.debug "Compiled template #{file_name || template}\n ==> #{render_symbol}" if logger
|
633
|
-
end
|
634
|
-
|
635
|
-
# Render the provided template with the given local assigns. If the template has not been rendered with the provided
|
636
|
-
# local assigns yet, or if the template has been updated on disk, then the template will be compiled to a method.
|
637
|
-
#
|
638
|
-
# Either, but not both, of template and file_path may be nil. If file_path is given, the template
|
639
|
-
# will only be read if it has to be compiled.
|
640
|
-
#
|
641
|
-
def compile_and_render_template(handler, template = nil, file_path = nil, local_assigns = {}) #:nodoc:
|
642
|
-
# convert string keys to symbols if requested
|
643
|
-
local_assigns = local_assigns.symbolize_keys if @@local_assigns_support_string_keys
|
644
|
-
|
645
|
-
# compile the given template, if necessary
|
646
|
-
if compile_template?(template, file_path, local_assigns)
|
647
|
-
template ||= read_template_file(file_path, nil)
|
648
|
-
compile_template(handler, template, file_path, local_assigns)
|
649
|
-
end
|
650
|
-
|
651
|
-
# Get the method name for this template and run it
|
652
|
-
method_name = @@method_names[file_path || template]
|
653
|
-
evaluate_assigns
|
654
|
-
|
655
|
-
send(method_name, local_assigns) do |*name|
|
656
|
-
instance_variable_get "@content_for_#{name.first || 'layout'}"
|
657
|
-
end
|
336
|
+
|
337
|
+
def execute(template)
|
338
|
+
send(template.method, template.locals) do |*names|
|
339
|
+
instance_variable_get "@content_for_#{names.first || 'layout'}"
|
340
|
+
end
|
658
341
|
end
|
659
342
|
end
|
660
343
|
end
|