actionpack 1.13.6 → 2.0.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 +1400 -20
- data/MIT-LICENSE +1 -1
- data/README +5 -5
- data/RUNNING_UNIT_TESTS +4 -5
- data/Rakefile +5 -6
- data/install.rb +2 -2
- data/lib/action_controller.rb +11 -15
- data/lib/action_controller/assertions.rb +12 -25
- data/lib/action_controller/assertions/dom_assertions.rb +18 -4
- data/lib/action_controller/assertions/model_assertions.rb +8 -1
- data/lib/action_controller/assertions/response_assertions.rb +35 -12
- data/lib/action_controller/assertions/routing_assertions.rb +56 -12
- data/lib/action_controller/assertions/selector_assertions.rb +105 -38
- data/lib/action_controller/assertions/tag_assertions.rb +28 -15
- data/lib/action_controller/base.rb +318 -250
- data/lib/action_controller/benchmarking.rb +33 -29
- data/lib/action_controller/caching.rb +130 -64
- data/lib/action_controller/cgi_ext.rb +16 -0
- data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/session.rb +73 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
- data/lib/action_controller/cgi_process.rb +34 -57
- data/lib/action_controller/components.rb +19 -36
- data/lib/action_controller/cookies.rb +10 -9
- data/lib/action_controller/dispatcher.rb +195 -0
- data/lib/action_controller/filters.rb +35 -34
- data/lib/action_controller/flash.rb +30 -35
- data/lib/action_controller/helpers.rb +121 -47
- data/lib/action_controller/http_authentication.rb +126 -0
- data/lib/action_controller/integration.rb +105 -101
- data/lib/action_controller/layout.rb +59 -47
- data/lib/action_controller/mime_responds.rb +57 -68
- data/lib/action_controller/mime_type.rb +43 -80
- data/lib/action_controller/mime_types.rb +20 -0
- data/lib/action_controller/polymorphic_routes.rb +88 -0
- data/lib/action_controller/record_identifier.rb +91 -0
- data/lib/action_controller/request.rb +553 -88
- data/lib/action_controller/request_forgery_protection.rb +126 -0
- data/lib/action_controller/request_profiler.rb +138 -0
- data/lib/action_controller/rescue.rb +185 -69
- data/lib/action_controller/resources.rb +211 -172
- data/lib/action_controller/response.rb +49 -8
- data/lib/action_controller/routing.rb +359 -236
- data/lib/action_controller/routing_optimisation.rb +119 -0
- data/lib/action_controller/session/active_record_store.rb +3 -2
- data/lib/action_controller/session/cookie_store.rb +161 -0
- data/lib/action_controller/session/mem_cache_store.rb +9 -16
- data/lib/action_controller/session_management.rb +17 -8
- data/lib/action_controller/streaming.rb +6 -3
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
- data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
- data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
- data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
- data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
- data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
- data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
- data/lib/action_controller/test_case.rb +53 -0
- data/lib/action_controller/test_process.rb +59 -46
- data/lib/action_controller/url_rewriter.rb +48 -24
- data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
- data/lib/action_controller/verification.rb +27 -21
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/action_view/base.rb +218 -63
- data/lib/action_view/compiled_templates.rb +1 -2
- data/lib/action_view/helpers/active_record_helper.rb +35 -17
- data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
- data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
- data/lib/action_view/helpers/benchmark_helper.rb +12 -5
- data/lib/action_view/helpers/cache_helper.rb +29 -0
- data/lib/action_view/helpers/capture_helper.rb +97 -63
- data/lib/action_view/helpers/date_helper.rb +295 -35
- data/lib/action_view/helpers/debug_helper.rb +6 -2
- data/lib/action_view/helpers/form_helper.rb +354 -111
- data/lib/action_view/helpers/form_options_helper.rb +171 -109
- data/lib/action_view/helpers/form_tag_helper.rb +332 -76
- data/lib/action_view/helpers/javascript_helper.rb +35 -11
- data/lib/action_view/helpers/javascripts/controls.js +484 -354
- data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
- data/lib/action_view/helpers/javascripts/effects.js +396 -364
- data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
- data/lib/action_view/helpers/number_helper.rb +84 -60
- data/lib/action_view/helpers/prototype_helper.rb +419 -43
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +59 -0
- data/lib/action_view/helpers/sanitize_helper.rb +223 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
- data/lib/action_view/helpers/tag_helper.rb +69 -39
- data/lib/action_view/helpers/text_helper.rb +221 -148
- data/lib/action_view/helpers/url_helper.rb +283 -165
- data/lib/action_view/partials.rb +134 -62
- data/lib/action_view/template_error.rb +4 -12
- data/lib/actionpack.rb +1 -0
- data/test/abstract_unit.rb +21 -1
- data/test/action_view_test.rb +26 -0
- data/test/active_record_unit.rb +12 -20
- data/test/activerecord/active_record_store_test.rb +2 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
- data/test/controller/action_pack_assertions_test.rb +21 -152
- data/test/controller/addresses_render_test.rb +2 -7
- data/test/controller/assert_select_test.rb +120 -14
- data/test/controller/base_test.rb +11 -13
- data/test/controller/caching_test.rb +125 -5
- data/test/controller/capture_test.rb +23 -16
- data/test/controller/cgi_test.rb +66 -391
- data/test/controller/components_test.rb +31 -42
- data/test/controller/content_type_test.rb +1 -1
- data/test/controller/cookie_test.rb +42 -14
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
- data/test/controller/dispatcher_test.rb +123 -0
- data/test/controller/fake_models.rb +5 -0
- data/test/controller/filters_test.rb +44 -7
- data/test/controller/flash_test.rb +46 -2
- data/test/controller/fragment_store_setting_test.rb +10 -8
- data/test/controller/helper_test.rb +19 -2
- data/test/controller/html-scanner/document_test.rb +124 -0
- data/test/controller/html-scanner/node_test.rb +69 -0
- data/test/controller/html-scanner/sanitizer_test.rb +250 -0
- data/test/controller/html-scanner/tag_node_test.rb +239 -0
- data/test/controller/html-scanner/text_node_test.rb +51 -0
- data/test/controller/html-scanner/tokenizer_test.rb +125 -0
- data/test/controller/http_authentication_test.rb +54 -0
- data/test/controller/integration_test.rb +12 -26
- data/test/controller/layout_test.rb +64 -12
- data/test/controller/mime_responds_test.rb +193 -38
- data/test/controller/mime_type_test.rb +30 -8
- data/test/controller/new_render_test.rb +104 -22
- data/test/controller/polymorphic_routes_test.rb +98 -0
- data/test/controller/record_identifier_test.rb +103 -0
- data/test/controller/redirect_test.rb +120 -18
- data/test/controller/render_test.rb +195 -45
- data/test/controller/request_forgery_protection_test.rb +217 -0
- data/test/controller/request_test.rb +545 -27
- data/test/controller/rescue_test.rb +501 -0
- data/test/controller/resources_test.rb +258 -132
- data/test/controller/routing_test.rb +502 -106
- data/test/controller/selector_test.rb +5 -5
- data/test/controller/send_file_test.rb +17 -7
- data/test/controller/session/cookie_store_test.rb +246 -0
- data/test/controller/session/mem_cache_store_test.rb +182 -0
- data/test/controller/session_fixation_test.rb +8 -11
- data/test/controller/session_management_test.rb +7 -7
- data/test/controller/test_test.rb +150 -38
- data/test/controller/url_rewriter_test.rb +87 -12
- data/test/controller/verification_test.rb +11 -0
- data/test/controller/view_paths_test.rb +137 -0
- data/test/controller/webservice_test.rb +11 -75
- data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
- data/test/fixtures/db_definitions/sqlite.sql +2 -1
- data/test/fixtures/developer.rb +2 -0
- data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
- data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
- data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
- data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/bracketed_param +5 -0
- data/test/fixtures/override/test/hello_world.erb +1 -0
- data/test/fixtures/override2/layouts/test/sub.erb +1 -0
- data/test/fixtures/post_test/layouts/post.html.erb +1 -0
- data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
- data/test/fixtures/post_test/post/index.html.erb +1 -0
- data/test/fixtures/post_test/post/index.iphone.erb +1 -0
- data/test/fixtures/post_test/super_post/index.html.erb +1 -0
- data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/javascripts/application.js +0 -1
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/replies.yml +2 -0
- data/test/fixtures/reply.rb +2 -1
- data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
- data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
- data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
- data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
- data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
- data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
- data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
- data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_partial.erb +1 -0
- data/test/fixtures/test/_partial.html.erb +1 -0
- data/test/fixtures/test/_partial.js.erb +1 -0
- data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
- data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
- data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
- data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
- data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
- data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
- data/test/fixtures/test/content_for_concatenated.erb +3 -0
- data/test/fixtures/test/content_for_with_parameter.erb +2 -0
- data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
- data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.builder +1 -0
- data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
- data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
- data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
- data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
- data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
- data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
- data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
- data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
- data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
- data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
- data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/topic.rb +1 -1
- data/test/template/active_record_helper_test.rb +67 -20
- data/test/template/asset_tag_helper_test.rb +222 -54
- data/test/template/atom_feed_helper_test.rb +101 -0
- data/test/template/benchmark_helper_test.rb +2 -2
- data/test/template/compiled_templates_test.rb +76 -32
- data/test/template/date_helper_test.rb +125 -9
- data/test/template/form_helper_test.rb +326 -33
- data/test/template/form_options_helper_test.rb +822 -15
- data/test/template/form_tag_helper_test.rb +96 -30
- data/test/template/javascript_helper_test.rb +61 -13
- data/test/template/number_helper_test.rb +12 -11
- data/test/template/prototype_helper_test.rb +185 -24
- data/test/template/sanitize_helper_test.rb +49 -0
- data/test/template/scriptaculous_helper_test.rb +9 -3
- data/test/template/tag_helper_test.rb +13 -2
- data/test/template/text_helper_test.rb +38 -52
- data/test/template/url_helper_test.rb +216 -46
- metadata +144 -116
- data/examples/.htaccess +0 -24
- data/examples/address_book/index.rhtml +0 -33
- data/examples/address_book/layout.rhtml +0 -8
- data/examples/address_book_controller.cgi +0 -9
- data/examples/address_book_controller.fcgi +0 -6
- data/examples/address_book_controller.rb +0 -52
- data/examples/address_book_controller.rbx +0 -4
- data/examples/benchmark.rb +0 -52
- data/examples/benchmark_with_ar.fcgi +0 -89
- data/examples/blog_controller.cgi +0 -53
- data/examples/debate/index.rhtml +0 -14
- data/examples/debate/new_topic.rhtml +0 -22
- data/examples/debate/topic.rhtml +0 -32
- data/examples/debate_controller.cgi +0 -57
- data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
- data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
- data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
- data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
- data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
- data/lib/action_controller/deprecated_dependencies.rb +0 -65
- data/lib/action_controller/deprecated_redirects.rb +0 -17
- data/lib/action_controller/deprecated_request_methods.rb +0 -34
- data/lib/action_controller/macros/auto_complete.rb +0 -53
- data/lib/action_controller/macros/in_place_editing.rb +0 -33
- data/lib/action_controller/pagination.rb +0 -408
- data/lib/action_controller/scaffolding.rb +0 -189
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
- data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
- data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
- data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
- data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
- data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
- data/lib/action_controller/vendor/xml_node.rb +0 -97
- data/lib/action_view/helpers/deprecated_helper.rb +0 -37
- data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
- data/lib/action_view/helpers/pagination_helper.rb +0 -86
- data/test/activerecord/active_record_assertions_test.rb +0 -92
- data/test/activerecord/pagination_test.rb +0 -165
- data/test/controller/deprecated_instance_variables_test.rb +0 -48
- data/test/controller/raw_post_test.rb +0 -68
- data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
- data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
- data/test/fixtures/test/_hash_object.rhtml +0 -1
- data/test/fixtures/test/list.rhtml +0 -1
- data/test/template/deprecated_helper_test.rb +0 -36
- data/test/template/deprecated_instance_variables_test.rb +0 -43
- data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require 'html/tokenizer'
|
2
|
+
require 'html/node'
|
3
|
+
require 'html/selector'
|
4
|
+
require 'html/sanitizer'
|
4
5
|
|
5
6
|
module HTML #:nodoc:
|
6
|
-
|
7
7
|
# A top-level HTMl document. You give it a body of text, and it will parse that
|
8
8
|
# text into a tree of nodes.
|
9
9
|
class Document #:nodoc:
|
@@ -23,6 +23,9 @@ module HTML #:nodoc:
|
|
23
23
|
if node.tag?
|
24
24
|
if node_stack.length > 1 && node.closing == :close
|
25
25
|
if node_stack.last.name == node.name
|
26
|
+
if node_stack.last.children.empty?
|
27
|
+
node_stack.last.children << Text.new(node_stack.last, node.line, node.position, "")
|
28
|
+
end
|
26
29
|
node_stack.pop
|
27
30
|
else
|
28
31
|
open_start = node_stack.last.position - 20
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module HTML
|
2
|
+
class Sanitizer
|
3
|
+
def sanitize(text, options = {})
|
4
|
+
return text unless sanitizeable?(text)
|
5
|
+
tokenize(text, options).join
|
6
|
+
end
|
7
|
+
|
8
|
+
def sanitizeable?(text)
|
9
|
+
!(text.nil? || text.empty? || !text.index("<"))
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
def tokenize(text, options)
|
14
|
+
tokenizer = HTML::Tokenizer.new(text)
|
15
|
+
result = []
|
16
|
+
while token = tokenizer.next
|
17
|
+
node = Node.parse(nil, 0, 0, token, false)
|
18
|
+
process_node node, result, options
|
19
|
+
end
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_node(node, result, options)
|
24
|
+
result << node.to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class FullSanitizer < Sanitizer
|
29
|
+
def sanitize(text, options = {})
|
30
|
+
result = super
|
31
|
+
# strip any comments, and if they have a newline at the end (ie. line with
|
32
|
+
# only a comment) strip that too
|
33
|
+
result.gsub!(/<!--(.*?)-->[\n]?/m, "") if result
|
34
|
+
# Recurse - handle all dirty nested tags
|
35
|
+
result == text ? result : sanitize(result, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def process_node(node, result, options)
|
39
|
+
result << node.to_s if node.class == HTML::Text
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class LinkSanitizer < FullSanitizer
|
44
|
+
cattr_accessor :included_tags, :instance_writer => false
|
45
|
+
self.included_tags = Set.new(%w(a href))
|
46
|
+
|
47
|
+
def sanitizeable?(text)
|
48
|
+
!(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
def process_node(node, result, options)
|
53
|
+
result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class WhiteListSanitizer < Sanitizer
|
58
|
+
[:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
|
59
|
+
:allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
|
60
|
+
class_inheritable_accessor attr, :instance_writer => false
|
61
|
+
end
|
62
|
+
|
63
|
+
# A regular expression of the valid characters used to separate protocols like
|
64
|
+
# the ':' in 'http://foo.com'
|
65
|
+
self.protocol_separator = /:|(�*58)|(p)|(%|%)3A/
|
66
|
+
|
67
|
+
# Specifies a Set of HTML attributes that can have URIs.
|
68
|
+
self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
|
69
|
+
|
70
|
+
# Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
|
71
|
+
# to just escaping harmless tags like <font>
|
72
|
+
self.bad_tags = Set.new(%w(script))
|
73
|
+
|
74
|
+
# Specifies the default Set of tags that the #sanitize helper will allow unscathed.
|
75
|
+
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
|
76
|
+
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr
|
77
|
+
acronym a img blockquote del ins))
|
78
|
+
|
79
|
+
# Specifies the default Set of html attributes that the #sanitize helper will leave
|
80
|
+
# in the allowed tag.
|
81
|
+
self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
|
82
|
+
|
83
|
+
# Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
|
84
|
+
self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
|
85
|
+
feed svn urn aim rsync tag ssh sftp rtsp afs))
|
86
|
+
|
87
|
+
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
|
88
|
+
self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
|
89
|
+
border-color border-left-color border-right-color border-top-color clear color cursor direction display
|
90
|
+
elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
|
91
|
+
overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
|
92
|
+
speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
|
93
|
+
width))
|
94
|
+
|
95
|
+
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
|
96
|
+
self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
|
97
|
+
collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
|
98
|
+
nowrap olive pointer purple red right solid silver teal top transparent underline white yellow))
|
99
|
+
|
100
|
+
# Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
|
101
|
+
self.shorthand_css_properties = Set.new(%w(background border margin padding))
|
102
|
+
|
103
|
+
# Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
|
104
|
+
def sanitize_css(style)
|
105
|
+
# disallow urls
|
106
|
+
style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
|
107
|
+
|
108
|
+
# gauntlet
|
109
|
+
if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
|
110
|
+
style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$))*$/
|
111
|
+
return ''
|
112
|
+
end
|
113
|
+
|
114
|
+
clean = []
|
115
|
+
style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
|
116
|
+
if allowed_css_properties.include?(prop.downcase)
|
117
|
+
clean << prop + ': ' + val + ';'
|
118
|
+
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
|
119
|
+
unless val.split().any? do |keyword|
|
120
|
+
!allowed_css_keywords.include?(keyword) &&
|
121
|
+
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
|
122
|
+
end
|
123
|
+
clean << prop + ': ' + val + ';'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
clean.join(' ')
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
def tokenize(text, options)
|
132
|
+
options[:parent] = []
|
133
|
+
options[:attributes] ||= allowed_attributes
|
134
|
+
options[:tags] ||= allowed_tags
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def process_node(node, result, options)
|
139
|
+
result << case node
|
140
|
+
when HTML::Tag
|
141
|
+
if node.closing == :close
|
142
|
+
options[:parent].shift
|
143
|
+
else
|
144
|
+
options[:parent].unshift node.name
|
145
|
+
end
|
146
|
+
|
147
|
+
process_attributes_for node, options
|
148
|
+
|
149
|
+
options[:tags].include?(node.name) ? node : nil
|
150
|
+
else
|
151
|
+
bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "<")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def process_attributes_for(node, options)
|
156
|
+
return unless node.attributes
|
157
|
+
node.attributes.keys.each do |attr_name|
|
158
|
+
value = node.attributes[attr_name].to_s
|
159
|
+
|
160
|
+
if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
|
161
|
+
node.attributes.delete(attr_name)
|
162
|
+
else
|
163
|
+
node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(value)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def contains_bad_protocols?(attr_name, value)
|
169
|
+
uri_attributes.include?(attr_name) &&
|
170
|
+
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -240,19 +240,24 @@ module HTML
|
|
240
240
|
raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
|
241
241
|
@source = ""
|
242
242
|
values = values[0] if values.size == 1 && values[0].is_a?(Array)
|
243
|
+
|
243
244
|
# We need a copy to determine if we failed to parse, and also
|
244
245
|
# preserve the original pass by-ref statement.
|
245
246
|
statement = selector.strip.dup
|
247
|
+
|
246
248
|
# Create a simple selector, along with negation.
|
247
249
|
simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }
|
248
250
|
|
251
|
+
@alternates = []
|
252
|
+
@depends = nil
|
253
|
+
|
249
254
|
# Alternative selector.
|
250
255
|
if statement.sub!(/^\s*,\s*/, "")
|
251
256
|
second = Selector.new(statement, values)
|
252
|
-
|
257
|
+
@alternates << second
|
253
258
|
# If there are alternate selectors, we group them in the top selector.
|
254
259
|
if alternates = second.instance_variable_get(:@alternates)
|
255
|
-
second.instance_variable_set(:@alternates,
|
260
|
+
second.instance_variable_set(:@alternates, [])
|
256
261
|
@alternates.concat alternates
|
257
262
|
end
|
258
263
|
@source << " , " << second.to_s
|
@@ -412,7 +417,7 @@ module HTML
|
|
412
417
|
|
413
418
|
# If this selector is part of the group, try all the alternative
|
414
419
|
# selectors (unless first_only).
|
415
|
-
if
|
420
|
+
if !first_only || !matches
|
416
421
|
@alternates.each do |alternate|
|
417
422
|
break if matches && first_only
|
418
423
|
if subset = alternate.match(element, first_only)
|
@@ -789,15 +794,15 @@ module HTML
|
|
789
794
|
# eventually, and array of substitution values.
|
790
795
|
#
|
791
796
|
# This method is called from four places, so it helps to put it here
|
792
|
-
# for
|
797
|
+
# for reuse. The only logic deals with the need to detect comma
|
793
798
|
# separators (alternate) and apply them to the selector group of the
|
794
799
|
# top selector.
|
795
800
|
def next_selector(statement, values)
|
796
801
|
second = Selector.new(statement, values)
|
797
802
|
# If there are alternate selectors, we group them in the top selector.
|
798
803
|
if alternates = second.instance_variable_get(:@alternates)
|
799
|
-
second.instance_variable_set(:@alternates,
|
800
|
-
|
804
|
+
second.instance_variable_set(:@alternates, [])
|
805
|
+
@alternates.concat alternates
|
801
806
|
end
|
802
807
|
second
|
803
808
|
end
|
@@ -12,7 +12,8 @@ module ActionController #:nodoc:
|
|
12
12
|
# parameters being set, or without certain session values existing.
|
13
13
|
#
|
14
14
|
# When a verification is violated, values may be inserted into the flash, and
|
15
|
-
# a specified redirection is triggered.
|
15
|
+
# a specified redirection is triggered. If no specific action is configured,
|
16
|
+
# verification failures will by default result in a 400 Bad Request response.
|
16
17
|
#
|
17
18
|
# Usage:
|
18
19
|
#
|
@@ -42,37 +43,37 @@ module ActionController #:nodoc:
|
|
42
43
|
# the user is redirected to a different action. The +options+ parameter
|
43
44
|
# is a hash consisting of the following key/value pairs:
|
44
45
|
#
|
45
|
-
# * <tt>:params</tt
|
46
|
+
# * <tt>:params</tt> - a single key or an array of keys that must
|
46
47
|
# be in the <tt>params</tt> hash in order for the action(s) to be safely
|
47
48
|
# called.
|
48
|
-
# * <tt>:session</tt
|
49
|
+
# * <tt>:session</tt> - a single key or an array of keys that must
|
49
50
|
# be in the <tt>session</tt> in order for the action(s) to be safely called.
|
50
|
-
# * <tt>:flash</tt
|
51
|
+
# * <tt>:flash</tt> - a single key or an array of keys that must
|
51
52
|
# be in the flash in order for the action(s) to be safely called.
|
52
|
-
# * <tt>:method</tt
|
53
|
+
# * <tt>:method</tt> - a single key or an array of keys--any one of which
|
53
54
|
# must match the current request method in order for the action(s) to
|
54
55
|
# be safely called. (The key should be a symbol: <tt>:get</tt> or
|
55
56
|
# <tt>:post</tt>, for example.)
|
56
|
-
# * <tt>:xhr</tt
|
57
|
+
# * <tt>:xhr</tt> - true/false option to ensure that the request is coming
|
57
58
|
# from an Ajax call or not.
|
58
|
-
# * <tt>:add_flash</tt
|
59
|
+
# * <tt>:add_flash</tt> - a hash of name/value pairs that should be merged
|
59
60
|
# into the session's flash if the prerequisites cannot be satisfied.
|
60
|
-
# * <tt>:add_headers</tt
|
61
|
+
# * <tt>:add_headers</tt> - a hash of name/value pairs that should be
|
61
62
|
# merged into the response's headers hash if the prerequisites cannot
|
62
63
|
# be satisfied.
|
63
|
-
# * <tt>:redirect_to</tt
|
64
|
+
# * <tt>:redirect_to</tt> - the redirection parameters to be used when
|
64
65
|
# redirecting if the prerequisites cannot be satisfied. You can
|
65
66
|
# redirect either to named route or to the action in some controller.
|
66
|
-
# * <tt>:render</tt
|
67
|
+
# * <tt>:render</tt> - the render parameters to be used when
|
67
68
|
# the prerequisites cannot be satisfied.
|
68
|
-
# * <tt>:only</tt
|
69
|
+
# * <tt>:only</tt> - only apply this verification to the actions specified
|
69
70
|
# in the associated array (may also be a single value).
|
70
|
-
# * <tt>:except</tt
|
71
|
+
# * <tt>:except</tt> - do not apply this verification to the actions
|
71
72
|
# specified in the associated array (may also be a single value).
|
72
73
|
def verify(options={})
|
73
74
|
filter_opts = { :only => options[:only], :except => options[:except] }
|
74
75
|
before_filter(filter_opts) do |c|
|
75
|
-
c.send :verify_action, options
|
76
|
+
c.send! :verify_action, options
|
76
77
|
end
|
77
78
|
end
|
78
79
|
end
|
@@ -81,7 +82,7 @@ module ActionController #:nodoc:
|
|
81
82
|
prereqs_invalid =
|
82
83
|
[*options[:params] ].find { |v| params[v].nil? } ||
|
83
84
|
[*options[:session]].find { |v| session[v].nil? } ||
|
84
|
-
[*options[:flash] ].find { |v| flash[v].nil?
|
85
|
+
[*options[:flash] ].find { |v| flash[v].nil? }
|
85
86
|
|
86
87
|
if !prereqs_invalid && options[:method]
|
87
88
|
prereqs_invalid ||=
|
@@ -93,16 +94,21 @@ module ActionController #:nodoc:
|
|
93
94
|
if prereqs_invalid
|
94
95
|
flash.update(options[:add_flash]) if options[:add_flash]
|
95
96
|
response.headers.update(options[:add_headers]) if options[:add_headers]
|
97
|
+
|
96
98
|
unless performed?
|
97
|
-
|
98
|
-
|
99
|
-
|
99
|
+
case
|
100
|
+
when options[:render]
|
101
|
+
render(options[:render])
|
102
|
+
when options[:redirect_to]
|
103
|
+
options[:redirect_to] = self.send!(options[:redirect_to]) if options[:redirect_to].is_a?(Symbol)
|
104
|
+
redirect_to(options[:redirect_to])
|
105
|
+
else
|
106
|
+
head(:bad_request)
|
107
|
+
end
|
100
108
|
end
|
101
|
-
return false
|
102
109
|
end
|
103
|
-
|
104
|
-
true
|
105
110
|
end
|
111
|
+
|
106
112
|
private :verify_action
|
107
113
|
end
|
108
|
-
end
|
114
|
+
end
|
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-2007 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
|
@@ -21,7 +21,6 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
|
24
|
-
$:.unshift(File.dirname(__FILE__) + "/action_view/vendor")
|
25
24
|
require 'action_view/base'
|
26
25
|
require 'action_view/partials'
|
27
26
|
|
@@ -29,4 +28,4 @@ ActionView::Base.class_eval do
|
|
29
28
|
include ActionView::Partials
|
30
29
|
end
|
31
30
|
|
32
|
-
ActionView::Base.load_helpers
|
31
|
+
ActionView::Base.load_helpers
|
data/lib/action_view/base.rb
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
require 'erb'
|
2
|
+
require 'builder'
|
3
|
+
|
4
|
+
class ERB
|
5
|
+
module Util
|
6
|
+
HTML_ESCAPE = { '&' => '&', '"' => '"', '>' => '>', '<' => '<' }
|
7
|
+
|
8
|
+
def html_escape(s)
|
9
|
+
s.to_s.gsub(/[&\"><]/) { |special| HTML_ESCAPE[special] }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
2
13
|
|
3
14
|
module ActionView #:nodoc:
|
4
15
|
class ActionViewError < StandardError #:nodoc:
|
5
16
|
end
|
6
17
|
|
7
|
-
# Action View templates can be written in three ways. If the template file has a +.rhtml+ extension then it uses a mixture of ERb
|
8
|
-
# (included in Ruby) and HTML. If the template file has a +.rxml+ extension then Jim Weirich's Builder::XmlMarkup library is used.
|
18
|
+
# Action View templates can be written in three ways. If the template file has a +.erb+ (or +.rhtml+) extension then it uses a mixture of ERb
|
19
|
+
# (included in Ruby) and HTML. If the template file has a +.builder+ (or +.rxml+) extension then Jim Weirich's Builder::XmlMarkup library is used.
|
9
20
|
# If the template file has a +.rjs+ extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
|
10
21
|
#
|
11
22
|
# = ERb
|
@@ -77,12 +88,12 @@ module ActionView #:nodoc:
|
|
77
88
|
# == Builder
|
78
89
|
#
|
79
90
|
# Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An +XmlMarkup+ object
|
80
|
-
# named +xml+ is automatically made available to templates with a +.
|
91
|
+
# named +xml+ is automatically made available to templates with a +.builder+ extension.
|
81
92
|
#
|
82
93
|
# Here are some basic examples:
|
83
94
|
#
|
84
95
|
# xml.em("emphasized") # => <em>emphasized</em>
|
85
|
-
# xml.em { xml.b("
|
96
|
+
# xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em>
|
86
97
|
# xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
|
87
98
|
# xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
|
88
99
|
# # NOTE: order of attributes is not specified.
|
@@ -154,10 +165,12 @@ module ActionView #:nodoc:
|
|
154
165
|
|
155
166
|
attr_reader :first_render
|
156
167
|
attr_accessor :base_path, :assigns, :template_extension
|
157
|
-
attr_accessor :controller
|
168
|
+
attr_accessor :controller, :view_paths
|
158
169
|
|
159
170
|
attr_reader :logger, :response, :headers
|
160
|
-
attr_internal
|
171
|
+
attr_internal :cookies, :flash, :headers, :params, :request, :response, :session
|
172
|
+
|
173
|
+
attr_writer :template_format
|
161
174
|
|
162
175
|
# Specify trim mode for the ERB compiler. Defaults to '-'.
|
163
176
|
# See ERb documentation for suitable values.
|
@@ -168,7 +181,7 @@ module ActionView #:nodoc:
|
|
168
181
|
@@cache_template_loading = false
|
169
182
|
cattr_accessor :cache_template_loading
|
170
183
|
|
171
|
-
# Specify whether file extension lookup should be cached.
|
184
|
+
# Specify whether file extension lookup should be cached, and whether template base path lookup should be cached.
|
172
185
|
# Should be +false+ for development environments. Defaults to +true+.
|
173
186
|
@@cache_template_extensions = true
|
174
187
|
cattr_accessor :cache_template_extensions
|
@@ -183,6 +196,11 @@ module ActionView #:nodoc:
|
|
183
196
|
# that alert()s the caught exception (and then re-raises it).
|
184
197
|
@@debug_rjs = false
|
185
198
|
cattr_accessor :debug_rjs
|
199
|
+
|
200
|
+
@@erb_variable = '_erbout'
|
201
|
+
cattr_accessor :erb_variable
|
202
|
+
|
203
|
+
delegate :request_forgery_protection_token, :to => :controller
|
186
204
|
|
187
205
|
@@template_handlers = HashWithIndifferentAccess.new
|
188
206
|
|
@@ -203,16 +221,34 @@ module ActionView #:nodoc:
|
|
203
221
|
# If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
|
204
222
|
# used by pick_template_extension determines whether ext1 or ext2 will be stored.
|
205
223
|
@@cached_template_extension = {}
|
224
|
+
# Maps template paths / extensions to
|
225
|
+
@@cached_base_paths = {}
|
226
|
+
|
227
|
+
# Cache public asset paths
|
228
|
+
cattr_reader :computed_public_paths
|
229
|
+
@@computed_public_paths = {}
|
230
|
+
|
231
|
+
@@templates_requiring_setup = Set.new(%w(builder rxml rjs))
|
232
|
+
|
233
|
+
# Order of template handers checked by #file_exists? depending on the current #template_format
|
234
|
+
DEFAULT_TEMPLATE_HANDLER_PREFERENCE = [:erb, :rhtml, :builder, :rxml, :javascript, :delegate]
|
235
|
+
TEMPLATE_HANDLER_PREFERENCES = {
|
236
|
+
:js => [:javascript, :erb, :rhtml, :builder, :rxml, :delegate],
|
237
|
+
:xml => [:builder, :rxml, :erb, :rhtml, :javascript, :delegate],
|
238
|
+
:delegate => [:delegate]
|
239
|
+
}
|
206
240
|
|
207
241
|
class ObjectWrapper < Struct.new(:value) #:nodoc:
|
208
242
|
end
|
209
243
|
|
210
|
-
def self.load_helpers
|
211
|
-
Dir.entries(
|
212
|
-
next unless
|
213
|
-
require
|
244
|
+
def self.load_helpers #:nodoc:
|
245
|
+
Dir.entries("#{File.dirname(__FILE__)}/helpers").sort.each do |file|
|
246
|
+
next unless file =~ /^([a-z][a-z_]*_helper).rb$/
|
247
|
+
require "action_view/helpers/#{$1}"
|
214
248
|
helper_module_name = $1.camelize
|
215
|
-
|
249
|
+
if Helpers.const_defined?(helper_module_name)
|
250
|
+
include Helpers.const_get(helper_module_name)
|
251
|
+
end
|
216
252
|
end
|
217
253
|
end
|
218
254
|
|
@@ -224,38 +260,58 @@ module ActionView #:nodoc:
|
|
224
260
|
# local assigns available to the template. The #render method ought to
|
225
261
|
# return the rendered template as a string.
|
226
262
|
def self.register_template_handler(extension, klass)
|
263
|
+
TEMPLATE_HANDLER_PREFERENCES[extension.to_sym] = TEMPLATE_HANDLER_PREFERENCES[:delegate]
|
227
264
|
@@template_handlers[extension] = klass
|
228
265
|
end
|
229
266
|
|
230
|
-
def initialize(
|
231
|
-
@
|
267
|
+
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
|
268
|
+
@view_paths = view_paths.respond_to?(:find) ? view_paths.dup : [*view_paths].compact
|
269
|
+
@assigns = assigns_for_first_render
|
232
270
|
@assigns_added = nil
|
233
271
|
@controller = controller
|
234
272
|
@logger = controller && controller.logger
|
235
273
|
end
|
236
274
|
|
237
275
|
# Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
|
238
|
-
# it's relative to the
|
276
|
+
# it's relative to the view_paths array, otherwise it's absolute. The hash in <tt>local_assigns</tt>
|
239
277
|
# is made available as local variables.
|
240
278
|
def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc:
|
241
|
-
|
279
|
+
if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")
|
280
|
+
raise ActionViewError, <<-END_ERROR
|
281
|
+
Due to changes in ActionMailer, you need to provide the mailer_name along with the template name.
|
242
282
|
|
243
|
-
|
244
|
-
|
283
|
+
render "user_mailer/signup"
|
284
|
+
render :file => "user_mailer/signup"
|
285
|
+
|
286
|
+
If you are rendering a subtemplate, you must now use controller-like partial syntax:
|
245
287
|
|
288
|
+
render :partial => 'signup' # no mailer_name necessary
|
289
|
+
END_ERROR
|
290
|
+
end
|
291
|
+
|
292
|
+
@first_render ||= template_path
|
293
|
+
template_path_without_extension, template_extension = path_and_extension(template_path)
|
294
|
+
if use_full_path
|
246
295
|
if template_extension
|
247
296
|
template_file_name = full_template_path(template_path_without_extension, template_extension)
|
248
297
|
else
|
249
298
|
template_extension = pick_template_extension(template_path).to_s
|
299
|
+
unless template_extension
|
300
|
+
raise ActionViewError, "No #{template_handler_preferences.to_sentence} template found for #{template_path} in #{view_paths.inspect}"
|
301
|
+
end
|
250
302
|
template_file_name = full_template_path(template_path, template_extension)
|
303
|
+
template_extension = template_extension.gsub(/^.+\./, '') # strip off any formats
|
251
304
|
end
|
252
305
|
else
|
253
306
|
template_file_name = template_path
|
254
|
-
template_extension = template_path.split('.').last
|
255
307
|
end
|
256
308
|
|
257
309
|
template_source = nil # Don't read the source until we know that it is required
|
258
310
|
|
311
|
+
if template_file_name.blank?
|
312
|
+
raise ActionViewError, "Couldn't find template file for #{template_path} in #{view_paths.inspect}"
|
313
|
+
end
|
314
|
+
|
259
315
|
begin
|
260
316
|
render_template(template_extension, template_source, template_file_name, local_assigns)
|
261
317
|
rescue Exception => e
|
@@ -263,12 +319,12 @@ module ActionView #:nodoc:
|
|
263
319
|
e.sub_template_of(template_file_name)
|
264
320
|
raise e
|
265
321
|
else
|
266
|
-
raise TemplateError.new(
|
322
|
+
raise TemplateError.new(find_base_path_for("#{template_path_without_extension}.#{template_extension}") || view_paths.first, template_file_name, @assigns, template_source, e)
|
267
323
|
end
|
268
324
|
end
|
269
325
|
end
|
270
|
-
|
271
|
-
# Renders the template present at <tt>template_path</tt> (relative to the
|
326
|
+
|
327
|
+
# Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
|
272
328
|
# The hash in <tt>local_assigns</tt> is made available as local variables.
|
273
329
|
def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
|
274
330
|
if options.is_a?(String)
|
@@ -276,22 +332,31 @@ module ActionView #:nodoc:
|
|
276
332
|
elsif options == :update
|
277
333
|
update_page(&block)
|
278
334
|
elsif options.is_a?(Hash)
|
279
|
-
options
|
280
|
-
options[:use_full_path] = options[:use_full_path].nil? ? true : options[:use_full_path]
|
335
|
+
options = options.reverse_merge(:type => :erb, :locals => {}, :use_full_path => true)
|
281
336
|
|
282
|
-
if options[:
|
337
|
+
if options[:layout]
|
338
|
+
path, partial_name = partial_pieces(options.delete(:layout))
|
339
|
+
|
340
|
+
if block_given?
|
341
|
+
@content_for_layout = capture(&block)
|
342
|
+
concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
|
343
|
+
else
|
344
|
+
@content_for_layout = render(options)
|
345
|
+
render(options.merge(:partial => "#{path}/#{partial_name}"))
|
346
|
+
end
|
347
|
+
elsif options[:file]
|
283
348
|
render_file(options[:file], options[:use_full_path], options[:locals])
|
284
349
|
elsif options[:partial] && options[:collection]
|
285
350
|
render_partial_collection(options[:partial], options[:collection], options[:spacer_template], options[:locals])
|
286
351
|
elsif options[:partial]
|
287
352
|
render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals])
|
288
353
|
elsif options[:inline]
|
289
|
-
render_template(options[:type]
|
354
|
+
render_template(options[:type], options[:inline], nil, options[:locals])
|
290
355
|
end
|
291
356
|
end
|
292
357
|
end
|
293
358
|
|
294
|
-
# Renders the +template+ which is given as a string as either
|
359
|
+
# Renders the +template+ which is given as a string as either erb or builder depending on <tt>template_extension</tt>.
|
295
360
|
# The hash in <tt>local_assigns</tt> is made available as local variables.
|
296
361
|
def render_template(template_extension, template, file_path = nil, local_assigns = {}) #:nodoc:
|
297
362
|
if handler = @@template_handlers[template_extension]
|
@@ -327,23 +392,54 @@ module ActionView #:nodoc:
|
|
327
392
|
end
|
328
393
|
end
|
329
394
|
|
395
|
+
# Gets the full template path with base path for the given template_path and extension.
|
396
|
+
#
|
397
|
+
# full_template_path('users/show', 'html.erb')
|
398
|
+
# # => '~/rails/app/views/users/show.html.erb
|
399
|
+
#
|
400
|
+
def full_template_path(template_path, extension)
|
401
|
+
if @@cache_template_extensions
|
402
|
+
(@@cached_base_paths[template_path] ||= {})[extension.to_s] ||= find_full_template_path(template_path, extension)
|
403
|
+
else
|
404
|
+
find_full_template_path(template_path, extension)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# Gets the extension for an existing template with the given template_path.
|
409
|
+
# Returns the format with the extension if that template exists.
|
410
|
+
#
|
411
|
+
# pick_template_extension('users/show')
|
412
|
+
# # => 'html.erb'
|
413
|
+
#
|
414
|
+
# pick_template_extension('users/legacy')
|
415
|
+
# # => "rhtml"
|
416
|
+
#
|
330
417
|
def pick_template_extension(template_path)#:nodoc:
|
331
418
|
if @@cache_template_extensions
|
332
|
-
@@cached_template_extension[template_path] ||= find_template_extension_for(template_path)
|
419
|
+
(@@cached_template_extension[template_path] ||= {})[template_format] ||= find_template_extension_for(template_path)
|
333
420
|
else
|
334
421
|
find_template_extension_for(template_path)
|
335
422
|
end
|
336
423
|
end
|
337
424
|
|
338
425
|
def delegate_template_exists?(template_path)#:nodoc:
|
339
|
-
@@template_handlers.find { |k,| template_exists?(template_path, k) }
|
426
|
+
delegate = @@template_handlers.find { |k,| template_exists?(template_path, k) }
|
427
|
+
delegate && delegate.first.to_sym
|
340
428
|
end
|
341
429
|
|
342
430
|
def erb_template_exists?(template_path)#:nodoc:
|
343
|
-
template_exists?(template_path, :
|
431
|
+
template_exists?(template_path, :erb)
|
344
432
|
end
|
345
433
|
|
346
434
|
def builder_template_exists?(template_path)#:nodoc:
|
435
|
+
template_exists?(template_path, :builder)
|
436
|
+
end
|
437
|
+
|
438
|
+
def rhtml_template_exists?(template_path)#:nodoc:
|
439
|
+
template_exists?(template_path, :rhtml)
|
440
|
+
end
|
441
|
+
|
442
|
+
def rxml_template_exists?(template_path)#:nodoc:
|
347
443
|
template_exists?(template_path, :rxml)
|
348
444
|
end
|
349
445
|
|
@@ -353,14 +449,10 @@ module ActionView #:nodoc:
|
|
353
449
|
|
354
450
|
def file_exists?(template_path)#:nodoc:
|
355
451
|
template_file_name, template_file_extension = path_and_extension(template_path)
|
356
|
-
|
357
452
|
if template_file_extension
|
358
453
|
template_exists?(template_file_name, template_file_extension)
|
359
454
|
else
|
360
|
-
|
361
|
-
%w(erb builder javascript delegate).any? do |template_type|
|
362
|
-
send("#{template_type}_template_exists?", template_path)
|
363
|
-
end
|
455
|
+
pick_template_extension(template_path)
|
364
456
|
end
|
365
457
|
end
|
366
458
|
|
@@ -369,37 +461,95 @@ module ActionView #:nodoc:
|
|
369
461
|
template_path.split('/').last[0,1] != '_'
|
370
462
|
end
|
371
463
|
|
464
|
+
# symbolized version of the :format parameter of the request, or :html by default.
|
465
|
+
def template_format
|
466
|
+
return @template_format if @template_format
|
467
|
+
format = controller && controller.respond_to?(:request) && controller.request.parameters[:format]
|
468
|
+
@template_format = format.blank? ? :html : format.to_sym
|
469
|
+
end
|
470
|
+
|
471
|
+
def template_handler_preferences
|
472
|
+
TEMPLATE_HANDLER_PREFERENCES[template_format] || DEFAULT_TEMPLATE_HANDLER_PREFERENCE
|
473
|
+
end
|
474
|
+
|
475
|
+
# Adds a view_path to the front of the view_paths array.
|
476
|
+
# This change affects the current request only.
|
477
|
+
#
|
478
|
+
# @template.prepend_view_path("views/default")
|
479
|
+
# @template.prepend_view_path(["views/default", "views/custom"])
|
480
|
+
#
|
481
|
+
def prepend_view_path(path)
|
482
|
+
@view_paths.unshift(*path)
|
483
|
+
end
|
484
|
+
|
485
|
+
# Adds a view_path to the end of the view_paths array.
|
486
|
+
# This change affects the current request only.
|
487
|
+
#
|
488
|
+
# @template.append_view_path("views/default")
|
489
|
+
# @template.append_view_path(["views/default", "views/custom"])
|
490
|
+
#
|
491
|
+
def append_view_path(path)
|
492
|
+
@view_paths.push(*path)
|
493
|
+
end
|
494
|
+
|
372
495
|
private
|
373
|
-
|
374
|
-
|
375
|
-
|
496
|
+
def find_full_template_path(template_path, extension)
|
497
|
+
file_name = "#{template_path}.#{extension}"
|
498
|
+
base_path = find_base_path_for(file_name)
|
499
|
+
base_path.blank? ? "" : "#{base_path}/#{file_name}"
|
376
500
|
end
|
377
501
|
|
378
502
|
# Asserts the existence of a template.
|
379
503
|
def template_exists?(template_path, extension)
|
380
504
|
file_path = full_template_path(template_path, extension)
|
381
|
-
@@method_names.has_key?(file_path) || FileTest.exists?(file_path)
|
505
|
+
!file_path.blank? && @@method_names.has_key?(file_path) || FileTest.exists?(file_path)
|
382
506
|
end
|
383
507
|
|
508
|
+
# Splits the path and extension from the given template_path and returns as an array.
|
384
509
|
def path_and_extension(template_path)
|
385
510
|
template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
|
386
511
|
[ template_path_without_extension, $1 ]
|
387
512
|
end
|
513
|
+
|
514
|
+
# Returns the view path that contains the given relative template path.
|
515
|
+
def find_base_path_for(template_file_name)
|
516
|
+
view_paths.find { |p| File.file?(File.join(p, template_file_name)) }
|
517
|
+
end
|
388
518
|
|
389
|
-
|
390
|
-
|
519
|
+
# Returns the view path that the full path resides in.
|
520
|
+
def extract_base_path_from(full_path)
|
521
|
+
view_paths.find { |p| full_path[0..p.size - 1] == p }
|
391
522
|
end
|
392
523
|
|
393
524
|
# Determines the template's file extension, such as rhtml, rxml, or rjs.
|
394
525
|
def find_template_extension_for(template_path)
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
526
|
+
find_template_extension_from_handler(template_path, true) ||
|
527
|
+
find_template_extension_from_handler(template_path) ||
|
528
|
+
find_template_extension_from_first_render()
|
529
|
+
end
|
530
|
+
|
531
|
+
def find_template_extension_from_handler(template_path, formatted = nil)
|
532
|
+
checked_template_path = formatted ? "#{template_path}.#{template_format}" : template_path
|
533
|
+
template_handler_preferences.each do |template_type|
|
534
|
+
extension =
|
535
|
+
case template_type
|
536
|
+
when :javascript
|
537
|
+
template_exists?(checked_template_path, :rjs) && :rjs
|
538
|
+
when :delegate
|
539
|
+
delegate_template_exists?(checked_template_path)
|
540
|
+
else
|
541
|
+
template_exists?(checked_template_path, template_type) && template_type
|
542
|
+
end
|
543
|
+
if extension
|
544
|
+
return formatted ? "#{template_format}.#{extension}" : extension.to_s
|
545
|
+
end
|
402
546
|
end
|
547
|
+
nil
|
548
|
+
end
|
549
|
+
|
550
|
+
# Determine the template extension from the <tt>@first_render</tt> filename
|
551
|
+
def find_template_extension_from_first_render
|
552
|
+
File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1]
|
403
553
|
end
|
404
554
|
|
405
555
|
# This method reads a template file.
|
@@ -439,27 +589,36 @@ module ActionView #:nodoc:
|
|
439
589
|
method_key = file_name || template
|
440
590
|
render_symbol = @@method_names[method_key]
|
441
591
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
592
|
+
compile_time = @@compile_time[render_symbol]
|
593
|
+
if compile_time && supports_local_assigns?(render_symbol, local_assigns)
|
594
|
+
if file_name && !@@cache_template_loading
|
595
|
+
template_changed_since?(file_name, compile_time)
|
446
596
|
end
|
447
597
|
else
|
448
598
|
true
|
449
599
|
end
|
450
600
|
end
|
451
601
|
|
602
|
+
# Method to handle checking a whether a template has changed since last compile; isolated so that templates
|
603
|
+
# not stored on the file system can hook and extend appropriately.
|
604
|
+
def template_changed_since?(file_name, compile_time)
|
605
|
+
lstat = File.lstat(file_name)
|
606
|
+
compile_time < lstat.mtime ||
|
607
|
+
(lstat.symlink? && compile_time < File.stat(file_name).mtime)
|
608
|
+
end
|
609
|
+
|
452
610
|
# Method to create the source code for a given template.
|
453
611
|
def create_template_source(extension, template, render_symbol, locals)
|
454
612
|
if template_requires_setup?(extension)
|
455
613
|
body = case extension.to_sym
|
456
|
-
when :rxml
|
457
|
-
"controller.response
|
458
|
-
"
|
614
|
+
when :rxml, :builder
|
615
|
+
content_type_handler = (controller.respond_to?(:response) ? "controller.response" : "controller")
|
616
|
+
"#{content_type_handler}.content_type ||= Mime::XML\n" +
|
617
|
+
"xml = Builder::XmlMarkup.new(:indent => 2)\n" +
|
459
618
|
template +
|
460
619
|
"\nxml.target!\n"
|
461
620
|
when :rjs
|
462
|
-
"controller.response.content_type ||=
|
621
|
+
"controller.response.content_type ||= Mime::JS\n" +
|
463
622
|
"update_page do |page|\n#{template}\nend"
|
464
623
|
end
|
465
624
|
else
|
@@ -479,11 +638,7 @@ module ActionView #:nodoc:
|
|
479
638
|
end
|
480
639
|
|
481
640
|
def template_requires_setup?(extension) #:nodoc:
|
482
|
-
templates_requiring_setup.include? extension.to_s
|
483
|
-
end
|
484
|
-
|
485
|
-
def templates_requiring_setup #:nodoc:
|
486
|
-
%w(rxml rjs)
|
641
|
+
@@templates_requiring_setup.include? extension.to_s
|
487
642
|
end
|
488
643
|
|
489
644
|
def assign_method_name(extension, template, file_name)
|
@@ -514,7 +669,7 @@ module ActionView #:nodoc:
|
|
514
669
|
line_offset = @@template_args[render_symbol].size
|
515
670
|
if extension
|
516
671
|
case extension.to_sym
|
517
|
-
when :rxml, :rjs
|
672
|
+
when :builder, :rxml, :rjs
|
518
673
|
line_offset += 2
|
519
674
|
end
|
520
675
|
end
|
@@ -532,7 +687,7 @@ module ActionView #:nodoc:
|
|
532
687
|
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
|
533
688
|
end
|
534
689
|
|
535
|
-
raise TemplateError.new(
|
690
|
+
raise TemplateError.new(extract_base_path_from(file_name) || view_paths.first, file_name || template, @assigns, template, e)
|
536
691
|
end
|
537
692
|
|
538
693
|
@@compile_time[render_symbol] = Time.now
|