actionpack 1.12.5 → 1.13.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.

Files changed (179) hide show
  1. data/CHANGELOG +517 -15
  2. data/MIT-LICENSE +1 -1
  3. data/README +18 -20
  4. data/Rakefile +7 -4
  5. data/examples/address_book_controller.rb +3 -3
  6. data/examples/blog_controller.cgi +3 -3
  7. data/examples/debate_controller.cgi +5 -5
  8. data/lib/action_controller.rb +2 -2
  9. data/lib/action_controller/assertions.rb +73 -311
  10. data/lib/action_controller/{deprecated_assertions.rb → assertions/deprecated_assertions.rb} +32 -8
  11. data/lib/action_controller/assertions/dom_assertions.rb +25 -0
  12. data/lib/action_controller/assertions/model_assertions.rb +12 -0
  13. data/lib/action_controller/assertions/response_assertions.rb +140 -0
  14. data/lib/action_controller/assertions/routing_assertions.rb +82 -0
  15. data/lib/action_controller/assertions/selector_assertions.rb +571 -0
  16. data/lib/action_controller/assertions/tag_assertions.rb +117 -0
  17. data/lib/action_controller/base.rb +334 -163
  18. data/lib/action_controller/benchmarking.rb +3 -6
  19. data/lib/action_controller/caching.rb +83 -22
  20. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -7
  21. data/lib/action_controller/cgi_ext/cgi_methods.rb +167 -173
  22. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +43 -22
  23. data/lib/action_controller/cgi_process.rb +50 -27
  24. data/lib/action_controller/components.rb +21 -25
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/{dependencies.rb → deprecated_dependencies.rb} +9 -27
  27. data/lib/action_controller/filters.rb +448 -225
  28. data/lib/action_controller/flash.rb +24 -20
  29. data/lib/action_controller/helpers.rb +2 -5
  30. data/lib/action_controller/integration.rb +40 -16
  31. data/lib/action_controller/layout.rb +11 -8
  32. data/lib/action_controller/macros/auto_complete.rb +3 -2
  33. data/lib/action_controller/macros/in_place_editing.rb +3 -2
  34. data/lib/action_controller/mime_responds.rb +41 -29
  35. data/lib/action_controller/mime_type.rb +68 -10
  36. data/lib/action_controller/pagination.rb +4 -3
  37. data/lib/action_controller/request.rb +22 -14
  38. data/lib/action_controller/rescue.rb +25 -22
  39. data/lib/action_controller/resources.rb +302 -0
  40. data/lib/action_controller/response.rb +20 -2
  41. data/lib/action_controller/response.rb.rej +17 -0
  42. data/lib/action_controller/routing.rb +1165 -567
  43. data/lib/action_controller/scaffolding.rb +30 -31
  44. data/lib/action_controller/session/active_record_store.rb +2 -0
  45. data/lib/action_controller/session/drb_store.rb +4 -0
  46. data/lib/action_controller/session/mem_cache_store.rb +4 -0
  47. data/lib/action_controller/session_management.rb +6 -9
  48. data/lib/action_controller/status_codes.rb +89 -0
  49. data/lib/action_controller/streaming.rb +6 -15
  50. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +5 -5
  51. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -2
  52. data/lib/action_controller/templates/rescues/routing_error.rhtml +4 -4
  53. data/lib/action_controller/templates/rescues/template_error.rhtml +1 -1
  54. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  55. data/lib/action_controller/test_process.rb +52 -30
  56. data/lib/action_controller/url_rewriter.rb +63 -29
  57. data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -0
  58. data/lib/action_controller/vendor/html-scanner/html/node.rb +3 -4
  59. data/lib/action_controller/vendor/html-scanner/html/selector.rb +822 -0
  60. data/lib/action_controller/verification.rb +22 -11
  61. data/lib/action_pack.rb +1 -1
  62. data/lib/action_pack/version.rb +2 -2
  63. data/lib/action_view.rb +1 -1
  64. data/lib/action_view/base.rb +46 -43
  65. data/lib/action_view/compiled_templates.rb +1 -1
  66. data/lib/action_view/helpers/active_record_helper.rb +54 -17
  67. data/lib/action_view/helpers/asset_tag_helper.rb +97 -46
  68. data/lib/action_view/helpers/capture_helper.rb +1 -1
  69. data/lib/action_view/helpers/date_helper.rb +258 -136
  70. data/lib/action_view/helpers/debug_helper.rb +1 -1
  71. data/lib/action_view/helpers/deprecated_helper.rb +34 -0
  72. data/lib/action_view/helpers/form_helper.rb +75 -35
  73. data/lib/action_view/helpers/form_options_helper.rb +7 -5
  74. data/lib/action_view/helpers/form_tag_helper.rb +44 -6
  75. data/lib/action_view/helpers/java_script_macros_helper.rb +59 -46
  76. data/lib/action_view/helpers/javascript_helper.rb +71 -10
  77. data/lib/action_view/helpers/javascripts/controls.js +41 -23
  78. data/lib/action_view/helpers/javascripts/dragdrop.js +105 -76
  79. data/lib/action_view/helpers/javascripts/effects.js +293 -163
  80. data/lib/action_view/helpers/javascripts/prototype.js +897 -389
  81. data/lib/action_view/helpers/javascripts/prototype.js.rej +561 -0
  82. data/lib/action_view/helpers/number_helper.rb +111 -65
  83. data/lib/action_view/helpers/prototype_helper.rb +84 -109
  84. data/lib/action_view/helpers/scriptaculous_helper.rb +5 -0
  85. data/lib/action_view/helpers/tag_helper.rb +69 -16
  86. data/lib/action_view/helpers/text_helper.rb +149 -112
  87. data/lib/action_view/helpers/url_helper.rb +200 -107
  88. data/lib/action_view/template_error.rb +66 -42
  89. data/test/abstract_unit.rb +4 -2
  90. data/test/active_record_unit.rb +84 -56
  91. data/test/activerecord/active_record_assertions_test.rb +26 -18
  92. data/test/activerecord/active_record_store_test.rb +4 -36
  93. data/test/activerecord/pagination_test.rb +1 -6
  94. data/test/controller/action_pack_assertions_test.rb +230 -113
  95. data/test/controller/addresses_render_test.rb +2 -6
  96. data/test/controller/assert_select_test.rb +576 -0
  97. data/test/controller/base_test.rb +73 -3
  98. data/test/controller/caching_test.rb +228 -0
  99. data/test/controller/capture_test.rb +12 -10
  100. data/test/controller/cgi_test.rb +89 -12
  101. data/test/controller/components_test.rb +24 -2
  102. data/test/controller/content_type_test.rb +139 -0
  103. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  104. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  105. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  106. data/test/controller/cookie_test.rb +33 -25
  107. data/test/controller/deprecated_instance_variables_test.rb +48 -0
  108. data/test/controller/deprecation/deprecated_base_methods_test.rb +60 -0
  109. data/test/controller/fake_controllers.rb +0 -1
  110. data/test/controller/filters_test.rb +301 -16
  111. data/test/controller/flash_test.rb +19 -2
  112. data/test/controller/helper_test.rb +2 -2
  113. data/test/controller/integration_test.rb +154 -0
  114. data/test/controller/layout_test.rb +115 -1
  115. data/test/controller/mime_responds_test.rb +94 -0
  116. data/test/controller/mime_type_test.rb +9 -0
  117. data/test/controller/new_render_test.rb +161 -11
  118. data/test/controller/raw_post_test.rb +52 -15
  119. data/test/controller/redirect_test.rb +27 -14
  120. data/test/controller/render_test.rb +76 -29
  121. data/test/controller/request_test.rb +55 -4
  122. data/test/controller/resources_test.rb +274 -0
  123. data/test/controller/routing_test.rb +1533 -824
  124. data/test/controller/selector_test.rb +628 -0
  125. data/test/controller/send_file_test.rb +9 -1
  126. data/test/controller/session_management_test.rb +51 -0
  127. data/test/controller/test_test.rb +113 -29
  128. data/test/controller/url_rewriter_test.rb +86 -17
  129. data/test/controller/verification_test.rb +19 -17
  130. data/test/controller/webservice_test.rb +0 -7
  131. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  132. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  133. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  134. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  135. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +1 -0
  136. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +1 -0
  137. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +1 -0
  138. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +1 -0
  139. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +1 -0
  140. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +1 -0
  141. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +1 -0
  142. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +1 -0
  143. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +1 -0
  144. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +1 -0
  145. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +1 -0
  146. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +1 -0
  147. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +1 -0
  148. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +1 -0
  149. data/test/fixtures/multipart/binary_file +0 -0
  150. data/test/fixtures/public/javascripts/application.js +1 -0
  151. data/test/fixtures/test/_hello.rxml +1 -0
  152. data/test/fixtures/test/hello_world_container.rxml +3 -0
  153. data/test/fixtures/topic.rb +2 -2
  154. data/test/template/active_record_helper_test.rb +83 -12
  155. data/test/template/asset_tag_helper_test.rb +75 -95
  156. data/test/template/compiled_templates_test.rb +1 -0
  157. data/test/template/date_helper_test.rb +873 -181
  158. data/test/template/deprecated_helper_test.rb +36 -0
  159. data/test/template/deprecated_instance_variables_test.rb +43 -0
  160. data/test/template/form_helper_test.rb +77 -1
  161. data/test/template/form_options_helper_test.rb +4 -0
  162. data/test/template/form_tag_helper_test.rb +66 -2
  163. data/test/template/java_script_macros_helper_test.rb +4 -1
  164. data/test/template/javascript_helper_test.rb +29 -0
  165. data/test/template/number_helper_test.rb +63 -27
  166. data/test/template/prototype_helper_test.rb +77 -34
  167. data/test/template/tag_helper_test.rb +34 -6
  168. data/test/template/text_helper_test.rb +69 -34
  169. data/test/template/url_helper_test.rb +168 -16
  170. data/test/testing_sandbox.rb +7 -22
  171. metadata +66 -20
  172. data/filler.txt +0 -50
  173. data/lib/action_controller/code_generation.rb +0 -235
  174. data/lib/action_controller/vendor/xml_simple.rb +0 -1019
  175. data/test/controller/caching_filestore.rb +0 -74
  176. data/test/fixtures/application_root/app/controllers/a_class_that_contains_a_controller/poorly_placed_controller.rb +0 -7
  177. data/test/fixtures/application_root/app/controllers/module_that_holds_controllers/nested_controller.rb +0 -3
  178. data/test/fixtures/application_root/app/models/a_class_that_contains_a_controller.rb +0 -7
  179. data/test/fixtures/dont_load.rb +0 -3
@@ -1,22 +1,21 @@
1
1
  require File.dirname(__FILE__) + '/javascript_helper'
2
2
 
3
3
  module ActionView
4
- module Helpers
5
- # Provides a set of methods for making easy links and getting urls that depend on the controller and action. This means that
6
- # you can use the same format for links in the views that you do in the controller. The different methods are even named
7
- # synchronously, so link_to uses that same url as is generated by url_for, which again is the same url used for
8
- # redirection in redirect_to.
4
+ module Helpers #:nodoc:
5
+ # Provides a set of methods for making easy links and getting urls that
6
+ # depend on the controller and action. This means that you can use the
7
+ # same format for links in the views that you do in the controller.
9
8
  module UrlHelper
10
9
  include JavaScriptHelper
11
10
 
12
- # Returns the URL for the set of +options+ provided. This takes the same options
13
- # as url_for. For a list, see the documentation for ActionController::Base#url_for.
14
- # Note that it'll set :only_path => true so you'll get /controller/action instead of the
15
- # http://example.com/controller/action part (makes it harder to parse httpd log files)
16
- #
17
- # When called from a view, url_for returns an HTML escaped url. If you need an unescaped
18
- # url, pass :escape => false to url_for.
19
- #
11
+ # Returns the URL for the set of +options+ provided. This takes the
12
+ # same options as url_for in action controller. For a list, see the
13
+ # documentation for ActionController::Base#url_for. Note that it'll
14
+ # set :only_path => true so you'll get the relative /controller/action
15
+ # instead of the fully qualified http://example.com/controller/action.
16
+ #
17
+ # When called from a view, url_for returns an HTML escaped url. If you
18
+ # need an unescaped url, pass :escape => false in the +options+.
20
19
  def url_for(options = {}, *parameters_for_method_reference)
21
20
  if options.kind_of? Hash
22
21
  options = { :only_path => true }.update(options.symbolize_keys)
@@ -24,30 +23,46 @@ module ActionView
24
23
  else
25
24
  escape = true
26
25
  end
26
+
27
27
  url = @controller.send(:url_for, options, *parameters_for_method_reference)
28
28
  escape ? html_escape(url) : url
29
29
  end
30
30
 
31
- # Creates a link tag of the given +name+ using an URL created by the set of +options+. See the valid options in
32
- # the documentation for ActionController::Base#url_for. It's also possible to pass a string instead of an options hash to
33
- # get a link tag that just points without consideration. If nil is passed as a name, the link itself will become the name.
31
+ # Creates a link tag of the given +name+ using a URL created by the set
32
+ # of +options+. See the valid options in the documentation for
33
+ # ActionController::Base#url_for. It's also possible to pass a string instead
34
+ # of an options hash to get a link tag that uses the value of the string as the
35
+ # href for the link. If nil is passed as a name, the link itself will become
36
+ # the name.
34
37
  #
35
- # The html_options has three special features. One for creating javascript confirm alerts where if you pass :confirm => 'Are you sure?',
36
- # the link will be guarded with a JS popup asking that question. If the user accepts, the link is processed, otherwise not.
38
+ # The +html_options+ will accept a hash of html attributes for the link tag.
39
+ # It also accepts 3 modifiers that specialize the link behavior.
37
40
  #
38
- # Another for creating a popup window, which is done by either passing :popup with true or the options of the window in
39
- # Javascript form.
41
+ # * <tt>:confirm => 'question?'</tt>: This will add a JavaScript confirm
42
+ # prompt with the question specified. If the user accepts, the link is
43
+ # processed normally, otherwise no action is taken.
44
+ # * <tt>:popup => true || array of window options</tt>: This will force the
45
+ # link to open in a popup window. By passing true, a default browser window
46
+ # will be opened with the URL. You can also specify an array of options
47
+ # that are passed-thru to JavaScripts window.open method.
48
+ # * <tt>:method => symbol of HTTP verb</tt>: This modifier will dynamically
49
+ # create an HTML form and immediately submit the form for processing using
50
+ # the HTTP verb specified. Useful for having links perform a POST operation
51
+ # in dangerous actions like deleting a record (which search bots can follow
52
+ # while spidering your site). Supported verbs are :post, :delete and :put.
53
+ # Note that if the user has JavaScript disabled, the request will fall back
54
+ # to using GET. If you are relying on the POST behavior, your should check
55
+ # for it in your controllers action by using the request objects methods
56
+ # for post?, delete? or put?.
40
57
  #
41
- # And a third for making the link do a POST request (instead of the regular GET) through a dynamically added form element that
42
- # is instantly submitted. Note that if the user has turned off Javascript, the request will fall back on the GET. So its
43
- # your responsibility to determine what the action should be once it arrives at the controller. The POST form is turned on by
44
- # passing :post as true. Note, it's not possible to use POST requests and popup targets at the same time (an exception will be thrown).
58
+ # You can mix and match the +html_options+ with the exception of
59
+ # :popup and :method which will raise an ActionView::ActionViewError
60
+ # exception.
45
61
  #
46
- # Examples:
47
- # link_to "Delete this page", { :action => "destroy", :id => @page.id }, :confirm => "Are you sure?"
62
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
48
63
  # link_to "Help", { :action => "help" }, :popup => true
49
- # link_to "Busy loop", { :action => "busy" }, :popup => ['new_window', 'height=300,width=600']
50
- # link_to "Destroy account", { :action => "destroy" }, :confirm => "Are you sure?", :post => true
64
+ # link_to "View Image", { :action => "view" }, :popup => ['new_window_name', 'height=300,width=600']
65
+ # link_to "Delete Image", { :action => "delete", :id => @image.id }, :confirm => "Are you sure?", :method => :delete
51
66
  def link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)
52
67
  if html_options
53
68
  html_options = html_options.stringify_keys
@@ -56,76 +71,77 @@ module ActionView
56
71
  else
57
72
  tag_options = nil
58
73
  end
74
+
59
75
  url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
60
76
  "<a href=\"#{url}\"#{tag_options}>#{name || url}</a>"
61
77
  end
62
78
 
63
- # Generates a form containing a sole button that submits to the
64
- # URL given by _options_. Use this method instead of +link_to+
65
- # for actions that do not have the safe HTTP GET semantics
66
- # implied by using a hypertext link.
79
+ # Generates a form containing a single button that submits to the URL created
80
+ # by the set of +options+. This is the safest method to ensure links that
81
+ # cause changes to your data are not triggered by search bots or accelerators.
82
+ # If the HTML button does not work with your layout, you can also consider
83
+ # using the link_to method with the <tt>:method</tt> modifier as described in
84
+ # the link_to documentation.
67
85
  #
68
- # The parameters are the same as for +link_to+. Any _html_options_
69
- # that you pass will be applied to the inner +input+ element.
70
- # In particular, pass
71
- #
72
- # :disabled => true/false
73
- #
74
- # as part of _html_options_ to control whether the button is
75
- # disabled. The generated form element is given the class
76
- # 'button-to', to which you can attach CSS styles for display
77
- # purposes.
78
- #
79
- # Example 1:
86
+ # The generated FORM element has a class name of <tt>button-to</tt>
87
+ # to allow styling of the form itself and its children. You can control
88
+ # the form submission and input element behavior using +html_options+.
89
+ # This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
90
+ # described in the link_to documentation. If no <tt>:method</tt> modifier
91
+ # is given, it will default to performing a POST operation. You can also
92
+ # disable the button by passing <tt>:disabled => true</tt> in +html_options+.
80
93
  #
81
- # # inside of controller for "feeds"
82
- # button_to "Edit", :action => 'edit', :id => 3
94
+ # button_to "New", :action => "new"
83
95
  #
84
- # Generates the following HTML (sans formatting):
96
+ # Generates the following HTML:
85
97
  #
86
- # <form method="post" action="/feeds/edit/3" class="button-to">
87
- # <div><input value="Edit" type="submit" /></div>
98
+ # <form method="post" action="/controller/new" class="button-to">
99
+ # <div><input value="New" type="submit" /></div>
88
100
  # </form>
89
101
  #
90
- # Example 2:
102
+ # If you are using RESTful routes, you can pass the <tt>:method</tt>
103
+ # to change the HTTP verb used to submit the form.
91
104
  #
92
- # button_to "Destroy", { :action => 'destroy', :id => 3 },
93
- # :confirm => "Are you sure?"
105
+ # button_to "Delete Image", { :action => "delete", :id => @image.id },
106
+ # :confirm => "Are you sure?", :method => :delete
94
107
  #
95
- # Generates the following HTML (sans formatting):
108
+ # Which generates the following HTML:
96
109
  #
97
- # <form method="post" action="/feeds/destroy/3" class="button-to">
98
- # <div><input onclick="return confirm('Are you sure?');"
99
- # value="Destroy" type="submit" />
110
+ # <form method="post" action="/images/delete/1" class="button-to">
111
+ # <div>
112
+ # <input type="hidden" name="_method" value="delete" />
113
+ # <input onclick="return confirm('Are you sure?');"
114
+ # value="Delete" type="submit" />
100
115
  # </div>
101
116
  # </form>
102
- #
103
- # *NOTE*: This method generates HTML code that represents a form.
104
- # Forms are "block" content, which means that you should not try to
105
- # insert them into your HTML where only inline content is expected.
106
- # For example, you can legally insert a form inside of a +div+ or
107
- # +td+ element or in between +p+ elements, but not in the middle of
108
- # a run of text, nor can you place a form within another form.
109
- # (Bottom line: Always validate your HTML before going public.)
110
- def button_to(name, options = {}, html_options = nil)
111
- html_options = (html_options || {}).stringify_keys
117
+ def button_to(name, options = {}, html_options = {})
118
+ html_options = html_options.stringify_keys
112
119
  convert_boolean_attributes!(html_options, %w( disabled ))
113
-
120
+
121
+ method_tag = ''
122
+ if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
123
+ method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
124
+ end
125
+
126
+ form_method = method.to_s == 'get' ? 'get' : 'post'
127
+
114
128
  if confirm = html_options.delete("confirm")
115
129
  html_options["onclick"] = "return #{confirm_javascript_function(confirm)};"
116
130
  end
117
-
118
- url = options.is_a?(String) ? options : url_for(options)
131
+
132
+ url = options.is_a?(String) ? options : self.url_for(options)
119
133
  name ||= url
120
-
121
- html_options.merge!("type" => "submit", "value" => name)
122
134
 
123
- "<form method=\"post\" action=\"#{h url}\" class=\"button-to\"><div>" +
124
- tag("input", html_options) + "</div></form>"
135
+ html_options.merge!("type" => "submit", "value" => name)
136
+
137
+ "<form method=\"#{form_method}\" action=\"#{escape_once url}\" class=\"button-to\"><div>" +
138
+ method_tag + tag("input", html_options) + "</div></form>"
125
139
  end
126
140
 
127
141
 
128
- # This tag is deprecated. Combine the link_to and AssetTagHelper::image_tag yourself instead, like:
142
+ # DEPRECATED. It is reccommended to use the AssetTagHelper::image_tag within
143
+ # a link_to method to generate a linked image.
144
+ #
129
145
  # link_to(image_tag("rss", :size => "30x45", :border => 0), "http://www.example.com")
130
146
  def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_reference)
131
147
  image_options = { "src" => src.include?("/") ? src : "/images/#{src}" }
@@ -157,18 +173,42 @@ module ActionView
157
173
  link_to(tag("img", image_options), options, html_options, *parameters_for_method_reference)
158
174
  end
159
175
 
160
- alias_method :link_to_image, :link_image_to # deprecated name
176
+ alias_method :link_to_image, :link_image_to
177
+ deprecate :link_to_image => "use link_to(image_tag(...), url)",
178
+ :link_image_to => "use link_to(image_tag(...), url)"
161
179
 
162
- # Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
163
- # request uri is the same as the link's, in which case only the name is returned (or the
164
- # given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
165
- # to the page currently being viewed.
180
+ # Creates a link tag of the given +name+ using a URL created by the set of
181
+ # +options+ unless the current request uri is the same as the links, in
182
+ # which case only the name is returned (or the given block is yielded, if
183
+ # one exists). Refer to the documentation for link_to_unless for block usage.
184
+ #
185
+ # <ul id="navbar">
186
+ # <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
187
+ # <li><%= link_to_unless_current("About Us", { :action => "about" }) %></li>
188
+ # </ul>
189
+ #
190
+ # This will render the following HTML when on the about us page:
191
+ #
192
+ # <ul id="navbar">
193
+ # <li><a href="/controller/index">Home</a></li>
194
+ # <li>About Us</li>
195
+ # </ul>
166
196
  def link_to_unless_current(name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
167
197
  link_to_unless current_page?(options), name, options, html_options, *parameters_for_method_reference, &block
168
198
  end
169
199
 
170
- # Create a link tag of the given +name+ using an URL created by the set of +options+, unless +condition+
171
- # is true, in which case only the name is returned (or the given block is yielded, if one exists).
200
+ # Creates a link tag of the given +name+ using a URL created by the set of
201
+ # +options+ unless +condition+ is true, in which case only the name is
202
+ # returned. To specialize the default behavior, you can pass a block that
203
+ # accepts the name or the full argument list for link_to_unless (see the example).
204
+ #
205
+ # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
206
+ #
207
+ # This example uses a block to modify the link if the condition isn't met.
208
+ #
209
+ # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
210
+ # link_to(name, { :controller => "accounts", :action => "signup" })
211
+ # end %>
172
212
  def link_to_unless(condition, name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
173
213
  if condition
174
214
  if block_given?
@@ -181,30 +221,56 @@ module ActionView
181
221
  end
182
222
  end
183
223
 
184
- # Create a link tag of the given +name+ using an URL created by the set of +options+, if +condition+
185
- # is true, in which case only the name is returned (or the given block is yielded, if one exists).
224
+ # Creates a link tag of the given +name+ using a URL created by the set of
225
+ # +options+ if +condition+ is true, in which case only the name is
226
+ # returned. To specialize the default behavior, you can pass a block that
227
+ # accepts the name or the full argument list for link_to_unless (see the examples
228
+ # in link_to_unless).
186
229
  def link_to_if(condition, name, options = {}, html_options = {}, *parameters_for_method_reference, &block)
187
230
  link_to_unless !condition, name, options, html_options, *parameters_for_method_reference, &block
188
231
  end
189
232
 
190
- # Creates a link tag for starting an email to the specified <tt>email_address</tt>, which is also used as the name of the
191
- # link unless +name+ is specified. Additional HTML options, such as class or id, can be passed in the <tt>html_options</tt> hash.
233
+ # Creates a mailto link tag to the specified +email_address+, which is
234
+ # also used as the name of the link unless +name+ is specified. Additional
235
+ # html attributes for the link can be passed in +html_options+.
236
+ #
237
+ # mail_to has several methods for hindering email harvestors and customizing
238
+ # the email itself by passing special keys to +html_options+.
239
+ #
240
+ # Special HTML Options:
241
+ #
242
+ # * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex".
243
+ # Passing "javascript" will dynamically create and encode the mailto: link then
244
+ # eval it into the DOM of the page. This method will not show the link on
245
+ # the page if the user has JavaScript disabled. Passing "hex" will hex
246
+ # encode the +email_address+ before outputting the mailto: link.
247
+ # * <tt>:replace_at</tt> - When the link +name+ isn't provided, the
248
+ # +email_address+ is used for the link label. You can use this option to
249
+ # obfuscate the +email_address+ by substituting the @ sign with the string
250
+ # given as the value.
251
+ # * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the
252
+ # +email_address+ is used for the link label. You can use this option to
253
+ # obfuscate the +email_address+ by substituting the . in the email with the
254
+ # string given as the value.
255
+ # * <tt>:subject</tt> - Preset the subject line of the email.
256
+ # * <tt>:body</tt> - Preset the body of the email.
257
+ # * <tt>:cc</tt> - Carbon Copy addition recipients on the email.
258
+ # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
192
259
  #
193
- # You can also make it difficult for spiders to harvest email address by obfuscating them.
194
260
  # Examples:
261
+ # mail_to "me@domain.com" # => <a href="mailto:me@domain.com">me@domain.com</a>
195
262
  # mail_to "me@domain.com", "My email", :encode => "javascript" # =>
196
- # <script type="text/javascript" language="javascript">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>
263
+ # <script type="text/javascript">eval(unescape('%64%6f%63...%6d%65%6e'))</script>
197
264
  #
198
265
  # mail_to "me@domain.com", "My email", :encode => "hex" # =>
199
266
  # <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
200
267
  #
201
- # You can also specify the cc address, bcc address, subject, and body parts of the message header to create a complex e-mail using the
202
- # corresponding +cc+, +bcc+, +subject+, and +body+ <tt>html_options</tt> keys. Each of these options are URI escaped and then appended to
203
- # the <tt>email_address</tt> before being output. <b>Be aware that javascript keywords will not be escaped and may break this feature
204
- # when encoding with javascript.</b>
205
- # Examples:
206
- # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com", :bcc => "bccaddress@domain.com", :subject => "This is an example email", :body => "This is the body of the message." # =>
207
- # <a href="mailto:me@domain.com?cc="ccaddress@domain.com"&bcc="bccaddress@domain.com"&body="This%20is%20the%20body%20of%20the%20message."&subject="This%20is%20an%20example%20email">My email</a>
268
+ # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email" # =>
269
+ # <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
270
+ #
271
+ # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
272
+ # :subject => "This is an example email" # =>
273
+ # <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
208
274
  def mail_to(email_address, name = nil, html_options = {})
209
275
  html_options = html_options.stringify_keys
210
276
  encode = html_options.delete("encode")
@@ -218,17 +284,19 @@ module ActionView
218
284
  extras << "subject=#{CGI.escape(subject).gsub("+", "%20")}&" unless subject.nil?
219
285
  extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
220
286
 
287
+ email_address = email_address.to_s
288
+
221
289
  email_address_obfuscated = email_address.dup
222
290
  email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
223
291
  email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
224
292
 
225
- if encode == 'javascript'
226
- tmp = "document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address.to_s+extras }))}');"
293
+ if encode == "javascript"
294
+ tmp = "document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');"
227
295
  for i in 0...tmp.length
228
296
  string << sprintf("%%%x",tmp[i])
229
297
  end
230
298
  "<script type=\"text/javascript\">eval(unescape('#{string}'))</script>"
231
- elsif encode == 'hex'
299
+ elsif encode == "hex"
232
300
  for i in 0...email_address.length
233
301
  if email_address[i,1] =~ /\w/
234
302
  string << sprintf("%%%x",email_address[i])
@@ -242,26 +310,42 @@ module ActionView
242
310
  end
243
311
  end
244
312
 
245
- # Returns true if the current page uri is generated by the options passed (in url_for format).
313
+ # True if the current request uri was generated by the given +options+.
246
314
  def current_page?(options)
247
- CGI.escapeHTML(url_for(options)) == @controller.request.request_uri
315
+ url_string = CGI.escapeHTML(url_for(options))
316
+ request = @controller.request
317
+ if url_string =~ /^\w+:\/\//
318
+ url_string == "#{request.protocol}#{request.host_with_port}#{request.request_uri}"
319
+ else
320
+ url_string == request.request_uri
321
+ end
248
322
  end
249
323
 
250
324
  private
251
325
  def convert_options_to_javascript!(html_options)
252
- confirm, popup, post = html_options.delete("confirm"), html_options.delete("popup"), html_options.delete("post")
326
+ confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
327
+
328
+ # post is deprecated, but if its specified and method is not, assume that method = :post
329
+ method, post = html_options.delete("method"), html_options.delete("post")
330
+ if !method && post
331
+ ActiveSupport::Deprecation.warn(
332
+ "Passing :post as a link modifier is deprecated. " +
333
+ "Use :method => \"post\" instead. :post will be removed in Rails 2.0."
334
+ )
335
+ method = :post
336
+ end
253
337
 
254
338
  html_options["onclick"] = case
255
- when popup && post
339
+ when popup && method
256
340
  raise ActionView::ActionViewError, "You can't use :popup and :post in the same link"
257
341
  when confirm && popup
258
342
  "if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;"
259
- when confirm && post
260
- "if (#{confirm_javascript_function(confirm)}) { #{post_javascript_function} };return false;"
343
+ when confirm && method
344
+ "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;"
261
345
  when confirm
262
346
  "return #{confirm_javascript_function(confirm)};"
263
- when post
264
- "#{post_javascript_function}return false;"
347
+ when method
348
+ "#{method_javascript_function(method)}return false;"
265
349
  when popup
266
350
  popup_javascript_function(popup) + 'return false;'
267
351
  else
@@ -277,8 +361,17 @@ module ActionView
277
361
  popup.is_a?(Array) ? "window.open(this.href,'#{popup.first}','#{popup.last}');" : "window.open(this.href);"
278
362
  end
279
363
 
280
- def post_javascript_function
281
- "var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit();"
364
+ def method_javascript_function(method)
365
+ submit_function =
366
+ "var f = document.createElement('form'); f.style.display = 'none'; " +
367
+ "this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;"
368
+
369
+ unless method == :post
370
+ submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
371
+ submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);"
372
+ end
373
+
374
+ submit_function << "f.submit();"
282
375
  end
283
376
 
284
377
  # Processes the _html_options_ hash, converting the boolean
@@ -6,16 +6,22 @@ module ActionView
6
6
 
7
7
  attr_reader :original_exception
8
8
 
9
- def initialize(base_path, file_name, assigns, source, original_exception)
10
- @base_path, @assigns, @source, @original_exception =
11
- base_path, assigns, source, original_exception
12
- @file_name = file_name
9
+ def initialize(base_path, file_path, assigns, source, original_exception)
10
+ @base_path, @assigns, @source, @original_exception =
11
+ base_path, assigns.dup, source, original_exception
12
+ @file_path = file_path
13
+
14
+ remove_deprecated_assigns!
13
15
  end
14
-
16
+
15
17
  def message
16
- original_exception.message
18
+ ActiveSupport::Deprecation.silence { original_exception.message }
19
+ end
20
+
21
+ def clean_backtrace
22
+ original_exception.clean_backtrace
17
23
  end
18
-
24
+
19
25
  def sub_template_message
20
26
  if @sub_templates
21
27
  "Trace of template inclusion: " +
@@ -24,63 +30,81 @@ module ActionView
24
30
  ""
25
31
  end
26
32
  end
27
-
28
- def source_extract(indention = 0)
29
- source_code = IO.readlines(@file_name)
30
-
31
- start_on_line = [ line_number - SOURCE_CODE_RADIUS - 1, 0 ].max
32
- end_on_line = [ line_number + SOURCE_CODE_RADIUS - 1, source_code.length].min
33
33
 
34
+ def source_extract(indentation = 0)
35
+ return unless num = line_number
36
+ num = num.to_i
37
+
38
+ source_code = IO.readlines(@file_path)
39
+
40
+ start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
41
+ end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
42
+
43
+ indent = ' ' * indentation
34
44
  line_counter = start_on_line
35
- extract = source_code[start_on_line..end_on_line].collect do |line|
45
+
46
+ source_code[start_on_line..end_on_line].sum do |line|
36
47
  line_counter += 1
37
- "#{' ' * indention}#{line_counter}: " + line
48
+ "#{indent}#{line_counter}: #{line}"
38
49
  end
39
-
40
- extract.join
41
50
  end
42
51
 
43
- def sub_template_of(file_name)
52
+ def sub_template_of(template_path)
44
53
  @sub_templates ||= []
45
- @sub_templates << file_name
54
+ @sub_templates << template_path
46
55
  end
47
-
56
+
48
57
  def line_number
49
- if file_name
50
- regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
51
- [@original_exception.message, @original_exception.clean_backtrace].flatten.each do |line|
52
- return $1.to_i if regexp =~ line
58
+ @line_number ||=
59
+ if file_name
60
+ regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
61
+
62
+ $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
53
63
  end
54
- end
55
- 0
56
64
  end
57
-
65
+
58
66
  def file_name
59
- stripped = strip_base_path(@file_name)
60
- stripped[0] == ?/ ? stripped[1..-1] : stripped
67
+ stripped = strip_base_path(@file_path)
68
+ stripped.slice!(0,1) if stripped[0] == ?/
69
+ stripped
61
70
  end
62
-
71
+
63
72
  def to_s
64
- "\n\n#{self.class} (#{message}) on line ##{line_number} of #{file_name}:\n" +
65
- source_extract + "\n " +
66
- original_exception.clean_backtrace.join("\n ") +
67
- "\n\n"
73
+ "\n\n#{self.class} (#{message}) #{source_location}:\n" +
74
+ "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
68
75
  end
69
76
 
70
77
  def backtrace
71
- [
72
- "On line ##{line_number} of #{file_name}\n\n#{source_extract(4)}\n " +
73
- original_exception.clean_backtrace.join("\n ")
78
+ [
79
+ "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
80
+ clean_backtrace.join("\n ")
74
81
  ]
75
82
  end
76
83
 
77
84
  private
78
- def strip_base_path(file_name)
79
- file_name = File.expand_path(file_name).gsub(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '')
80
- file_name.gsub(@base_path, "")
85
+ def remove_deprecated_assigns!
86
+ ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |ivar|
87
+ @assigns.delete(ivar)
88
+ end
89
+ end
90
+
91
+ def strip_base_path(path)
92
+ File.expand_path(path).
93
+ gsub(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '').
94
+ gsub(@base_path, "")
95
+ end
96
+
97
+ def source_location
98
+ if line_number
99
+ "on line ##{line_number} of "
100
+ else
101
+ 'in '
102
+ end + file_name
81
103
  end
82
104
  end
83
105
  end
84
106
 
85
- Exception::TraceSubstitutions << [/:in\s+`_run_(html|xml).*'\s*$/, ''] if defined?(Exception::TraceSubstitutions)
86
- Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}}, '#{RAILS_ROOT}'] if defined?(RAILS_ROOT)
107
+ if defined?(Exception::TraceSubstitutions)
108
+ Exception::TraceSubstitutions << [/:in\s+`_run_(html|xml).*'\s*$/, '']
109
+ Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}}, '#{RAILS_ROOT}'] if defined?(RAILS_ROOT)
110
+ end