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,5 +1,18 @@
1
1
  module Mime
2
- class Type #:nodoc:
2
+ # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
3
+ #
4
+ # class PostsController < ActionController::Base
5
+ # def show
6
+ # @post = Post.find(params[:id])
7
+ #
8
+ # respond_to do |format|
9
+ # format.html
10
+ # format.ics { render :text => post.to_ics, :mime_type => Mime::Type["text/calendar"] }
11
+ # format.xml { render :xml => @people.to_xml }
12
+ # end
13
+ # end
14
+ # end
15
+ class Type
3
16
  # A simple helper class used in parsing the accept header
4
17
  class AcceptItem #:nodoc:
5
18
  attr_accessor :order, :name, :q
@@ -31,14 +44,20 @@ module Mime
31
44
  LOOKUP[string]
32
45
  end
33
46
 
47
+ def register(string, symbol, synonyms = [])
48
+ Mime.send :const_set, symbol.to_s.upcase, Type.new(string, symbol, synonyms)
49
+ SET << Mime.send(:const_get, symbol.to_s.upcase)
50
+ LOOKUP[string] = EXTENSION_LOOKUP[symbol.to_s] = SET.last
51
+ end
52
+
34
53
  def parse(accept_header)
35
54
  # keep track of creation order to keep the subsequent sort stable
36
55
  index = 0
37
- list = accept_header.split(/,/).
38
- map! { |i| AcceptItem.new(index += 1, *i.split(/;\s*q=/)) }.sort!
56
+ list = accept_header.split(/,/).map! do |i|
57
+ AcceptItem.new(index += 1, *i.split(/;\s*q=/))
58
+ end.sort!
39
59
 
40
60
  # Take care of the broken text/xml entry by renaming or deleting it
41
-
42
61
  text_xml = list.index("text/xml")
43
62
  app_xml = list.index("application/xml")
44
63
 
@@ -112,31 +131,70 @@ module Mime
112
131
  end
113
132
 
114
133
  ALL = Type.new "*/*", :all
134
+ TEXT = Type.new "text/plain", :text
115
135
  HTML = Type.new "text/html", :html, %w( application/xhtml+xml )
116
136
  JS = Type.new "text/javascript", :js, %w( application/javascript application/x-javascript )
137
+ ICS = Type.new "text/calendar", :ics
138
+ CSV = Type.new "text/csv", :csv
117
139
  XML = Type.new "application/xml", :xml, %w( text/xml application/x-xml )
118
140
  RSS = Type.new "application/rss+xml", :rss
119
141
  ATOM = Type.new "application/atom+xml", :atom
120
142
  YAML = Type.new "application/x-yaml", :yaml, %w( text/yaml )
143
+ JSON = Type.new "application/json", :json, %w( text/x-json )
144
+
145
+ SET = [ ALL, TEXT, HTML, JS, ICS, XML, RSS, ATOM, YAML, JSON ]
121
146
 
122
- LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) }
147
+ LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k == "" }
123
148
 
124
149
  LOOKUP["*/*"] = ALL
125
150
 
151
+ LOOKUP["text/plain"] = TEXT
152
+
126
153
  LOOKUP["text/html"] = HTML
127
154
  LOOKUP["application/xhtml+xml"] = HTML
128
155
 
129
- LOOKUP["application/xml"] = XML
130
- LOOKUP["text/xml"] = XML
131
- LOOKUP["application/x-xml"] = XML
132
-
133
156
  LOOKUP["text/javascript"] = JS
134
157
  LOOKUP["application/javascript"] = JS
135
158
  LOOKUP["application/x-javascript"] = JS
136
159
 
160
+ LOOKUP["text/calendar"] = ICS
161
+
162
+ LOOKUP["text/csv"] = CSV
163
+
164
+ LOOKUP["application/xml"] = XML
165
+ LOOKUP["text/xml"] = XML
166
+ LOOKUP["application/x-xml"] = XML
167
+
137
168
  LOOKUP["text/yaml"] = YAML
138
169
  LOOKUP["application/x-yaml"] = YAML
139
170
 
140
171
  LOOKUP["application/rss+xml"] = RSS
141
172
  LOOKUP["application/atom+xml"] = ATOM
142
- end
173
+
174
+ LOOKUP["application/json"] = JSON
175
+ LOOKUP["text/x-json"] = JSON
176
+
177
+
178
+ EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k == "" }
179
+
180
+ EXTENSION_LOOKUP["html"] = HTML
181
+ EXTENSION_LOOKUP["xhtml"] = HTML
182
+
183
+ EXTENSION_LOOKUP["txt"] = TEXT
184
+
185
+ EXTENSION_LOOKUP["xml"] = XML
186
+
187
+ EXTENSION_LOOKUP["js"] = JS
188
+
189
+ EXTENSION_LOOKUP["ics"] = ICS
190
+
191
+ EXTENSION_LOOKUP["csv"] = CSV
192
+
193
+ EXTENSION_LOOKUP["yml"] = YAML
194
+ EXTENSION_LOOKUP["yaml"] = YAML
195
+
196
+ EXTENSION_LOOKUP["rss"] = RSS
197
+ EXTENSION_LOOKUP["atom"] = ATOM
198
+
199
+ EXTENSION_LOOKUP["json"] = JSON
200
+ end
@@ -1,6 +1,8 @@
1
1
  module ActionController
2
2
  # === Action Pack pagination for Active Record collections
3
3
  #
4
+ # DEPRECATION WARNING: Pagination will be separated into its own plugin with Rails 2.0.
5
+ #
4
6
  # The Pagination module aids in the process of paging large collections of
5
7
  # Active Record objects. It offers macro-style automatic fetching of your
6
8
  # model for multiple views, or explicit fetching for single actions. And if
@@ -104,8 +106,7 @@ module ActionController
104
106
  # ClassMethods#paginate.
105
107
  #
106
108
  # +options+ are:
107
- # <tt>:singular_name</tt>:: the singular name to use, if it can't be inferred by
108
- # singularizing the collection name
109
+ # <tt>:singular_name</tt>:: the singular name to use, if it can't be inferred by singularizing the collection name
109
110
  # <tt>:class_name</tt>:: the class name to use, if it can't be inferred by
110
111
  # camelizing the singular name
111
112
  # <tt>:per_page</tt>:: the maximum number of items to include in a
@@ -192,7 +193,7 @@ module ActionController
192
193
 
193
194
  def paginator_and_collection_for(collection_id, options) #:nodoc:
194
195
  klass = options[:class_name].constantize
195
- page = @params[options[:parameter]]
196
+ page = params[options[:parameter]]
196
197
  count = count_collection_for_pagination(klass, options)
197
198
  paginator = Paginator.new(self, count, options[:per_page], page)
198
199
  collection = find_collection_for_pagination(klass, options, paginator)
@@ -13,12 +13,18 @@ module ActionController
13
13
  @parameters ||= request_parameters.update(query_parameters).update(path_parameters).with_indifferent_access
14
14
  end
15
15
 
16
- # Returns the HTTP request method as a lowercase symbol (:get, for example)
16
+ # Returns the HTTP request method as a lowercase symbol (:get, for example). Note, HEAD is returned as :get
17
+ # since the two are supposedly to be functionaly equivilent for all purposes except that HEAD won't return a response
18
+ # body (which Rails also takes care of elsewhere).
17
19
  def method
18
- @request_method ||= @env['REQUEST_METHOD'].downcase.to_sym
20
+ @request_method ||= (!parameters[:_method].blank? && @env['REQUEST_METHOD'] == 'POST') ?
21
+ parameters[:_method].to_s.downcase.to_sym :
22
+ @env['REQUEST_METHOD'].downcase.to_sym
23
+
24
+ @request_method == :head ? :get : @request_method
19
25
  end
20
26
 
21
- # Is this a GET request? Equivalent to request.method == :get
27
+ # Is this a GET (or HEAD) request? Equivalent to request.method == :get
22
28
  def get?
23
29
  method == :get
24
30
  end
@@ -38,9 +44,10 @@ module ActionController
38
44
  method == :delete
39
45
  end
40
46
 
41
- # Is this a HEAD request? Equivalent to request.method == :head
47
+ # Is this a HEAD request? HEAD is mapped as :get for request.method, so here we ask the
48
+ # REQUEST_METHOD header directly. Thus, for head, both get? and head? will return true.
42
49
  def head?
43
- method == :head
50
+ @env['REQUEST_METHOD'].downcase.to_sym == :head
44
51
  end
45
52
 
46
53
  # Determine whether the body of a HTTP call is URL-encoded (default)
@@ -128,19 +135,21 @@ module ActionController
128
135
  @env['RAW_POST_DATA']
129
136
  end
130
137
 
131
- # Returns the request URI correctly, taking into account the idiosyncracies
132
- # of the various servers.
138
+ # Return the request URI, accounting for server idiosyncracies.
139
+ # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
133
140
  def request_uri
134
141
  if uri = @env['REQUEST_URI']
135
- (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri # Remove domain, which webrick puts into the request_uri.
136
- else # REQUEST_URI is blank under IIS - get this from PATH_INFO and SCRIPT_NAME
142
+ # Remove domain, which webrick puts into the request_uri.
143
+ (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
144
+ else
145
+ # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
137
146
  script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
138
147
  uri = @env['PATH_INFO']
139
148
  uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
140
149
  unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
141
150
  uri << '?' << env_qs
142
151
  end
143
- uri
152
+ @env['REQUEST_URI'] = uri
144
153
  end
145
154
  end
146
155
 
@@ -159,11 +168,10 @@ module ActionController
159
168
  path = (uri = request_uri) ? uri.split('?').first : ''
160
169
 
161
170
  # Cut off the path to the installation directory if given
162
- root = relative_url_root
163
- path[0, root.length] = '' if root
164
- path || ''
171
+ path.sub!(%r/^#{relative_url_root}/, '')
172
+ path || ''
165
173
  end
166
-
174
+
167
175
  # Returns the path minus the web server relative installation directory.
168
176
  # This can be set with the environment variable RAILS_RELATIVE_URL_ROOT.
169
177
  # It can be automatically extracted for Apache setups. If the server is not
@@ -6,12 +6,10 @@ module ActionController #:nodoc:
6
6
  #
7
7
  # You can tailor the rescuing behavior and appearance by overwriting the following two stub methods.
8
8
  module Rescue
9
- def self.append_features(base) #:nodoc:
10
- super
9
+ def self.included(base) #:nodoc:
11
10
  base.extend(ClassMethods)
12
11
  base.class_eval do
13
- alias_method :perform_action_without_rescue, :perform_action
14
- alias_method :perform_action, :perform_action_with_rescue
12
+ alias_method_chain :perform_action, :rescue
15
13
  end
16
14
  end
17
15
 
@@ -36,23 +34,26 @@ module ActionController #:nodoc:
36
34
 
37
35
  # Overwrite to implement custom logging of errors. By default logs as fatal.
38
36
  def log_error(exception) #:doc:
39
- if ActionView::TemplateError === exception
40
- logger.fatal(exception.to_s)
41
- else
42
- logger.fatal(
43
- "\n\n#{exception.class} (#{exception.message}):\n " +
44
- clean_backtrace(exception).join("\n ") +
45
- "\n\n"
46
- )
37
+ ActiveSupport::Deprecation.silence do
38
+ if ActionView::TemplateError === exception
39
+ logger.fatal(exception.to_s)
40
+ else
41
+ logger.fatal(
42
+ "\n\n#{exception.class} (#{exception.message}):\n " +
43
+ clean_backtrace(exception).join("\n ") +
44
+ "\n\n"
45
+ )
46
+ end
47
47
  end
48
48
  end
49
49
 
50
50
  # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).
51
51
  def rescue_action_in_public(exception) #:doc:
52
52
  case exception
53
- when RoutingError, UnknownAction then
53
+ when RoutingError, UnknownAction
54
54
  render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), "404 Not Found")
55
- else render_text "<html><body><h1>Application error (Rails)</h1></body></html>"
55
+ else
56
+ render_text(IO.read(File.join(RAILS_ROOT, 'public', '500.html')), "500 Internal Error")
56
57
  end
57
58
  end
58
59
 
@@ -60,19 +61,19 @@ module ActionController #:nodoc:
60
61
  # the remote IP being 127.0.0.1. For example, this could include the IP of the developer machine when debugging
61
62
  # remotely.
62
63
  def local_request? #:doc:
63
- [@request.remote_addr, @request.remote_ip] == ["127.0.0.1"] * 2
64
+ [request.remote_addr, request.remote_ip] == ["127.0.0.1"] * 2
64
65
  end
65
66
 
66
67
  # Renders a detailed diagnostics screen on action exceptions.
67
68
  def rescue_action_locally(exception)
68
69
  add_variables_to_assigns
69
70
  @template.instance_variable_set("@exception", exception)
70
- @template.instance_variable_set("@rescues_path", File.dirname(__FILE__) + "/templates/rescues/")
71
+ @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
71
72
  @template.send(:assign_variables_from_controller)
72
73
 
73
74
  @template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
74
75
 
75
- @headers["Content-Type"] = "text/html"
76
+ response.content_type = Mime::HTML
76
77
  render_file(rescues_path("layout"), response_code_for_rescue(exception))
77
78
  end
78
79
 
@@ -80,8 +81,8 @@ module ActionController #:nodoc:
80
81
  def perform_action_with_rescue #:nodoc:
81
82
  begin
82
83
  perform_action_without_rescue
83
- rescue Object => exception
84
- if defined?(Breakpoint) && @params["BP-RETRY"]
84
+ rescue Exception => exception # errors from action performed
85
+ if defined?(Breakpoint) && params["BP-RETRY"]
85
86
  msg = exception.backtrace.first
86
87
  if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
87
88
  origin_file, origin_line = md[1], md[2].to_i
@@ -89,7 +90,7 @@ module ActionController #:nodoc:
89
90
  set_trace_func(lambda do |type, file, line, method, context, klass|
90
91
  if file == origin_file and line == origin_line then
91
92
  set_trace_func(nil)
92
- @params["BP-RETRY"] = false
93
+ params["BP-RETRY"] = false
93
94
 
94
95
  callstack = caller
95
96
  callstack.slice!(0) if callstack.first["rescue.rb"]
@@ -127,8 +128,10 @@ module ActionController #:nodoc:
127
128
 
128
129
  def response_code_for_rescue(exception)
129
130
  case exception
130
- when UnknownAction, RoutingError then "404 Page Not Found"
131
- else "500 Internal Error"
131
+ when UnknownAction, RoutingError
132
+ "404 Page Not Found"
133
+ else
134
+ "500 Internal Error"
132
135
  end
133
136
  end
134
137
 
@@ -0,0 +1,302 @@
1
+ module ActionController
2
+ module Resources
3
+ class Resource #:nodoc:
4
+ attr_reader :collection_methods, :member_methods, :new_methods
5
+ attr_reader :path_prefix, :name_prefix
6
+ attr_reader :plural, :singular
7
+ attr_reader :options
8
+
9
+ def initialize(entities, options)
10
+ @plural = entities
11
+ @singular = options[:singular] || plural.to_s.singularize
12
+
13
+ @options = options
14
+
15
+ arrange_actions
16
+ add_default_actions
17
+ set_prefixes
18
+ end
19
+
20
+ def controller
21
+ (options[:controller] || plural).to_s
22
+ end
23
+
24
+ def path
25
+ "#{path_prefix}/#{plural}"
26
+ end
27
+
28
+ def new_path
29
+ "#{path}/new"
30
+ end
31
+
32
+ def member_path
33
+ "#{path}/:id"
34
+ end
35
+
36
+ def nesting_path_prefix
37
+ "#{path}/:#{singular}_id"
38
+ end
39
+
40
+ private
41
+ def arrange_actions
42
+ @collection_methods = arrange_actions_by_methods(options.delete(:collection))
43
+ @member_methods = arrange_actions_by_methods(options.delete(:member))
44
+ @new_methods = arrange_actions_by_methods(options.delete(:new))
45
+ end
46
+
47
+ def add_default_actions
48
+ add_default_action(member_methods, :get, :edit)
49
+ add_default_action(new_methods, :get, :new)
50
+ end
51
+
52
+ def set_prefixes
53
+ @path_prefix = options.delete(:path_prefix)
54
+ @name_prefix = options.delete(:name_prefix)
55
+ end
56
+
57
+ def arrange_actions_by_methods(actions)
58
+ (actions || {}).inject({}) do |flipped_hash, (key, value)|
59
+ (flipped_hash[value] ||= []) << key
60
+ flipped_hash
61
+ end
62
+ end
63
+
64
+ def add_default_action(collection, method, action)
65
+ (collection[method] ||= []).unshift(action)
66
+ end
67
+ end
68
+
69
+ # Creates named routes for implementing verb-oriented controllers. This is
70
+ # useful for implementing REST API's, where a single resource has different
71
+ # behavior based on the HTTP verb (method) used to access it.
72
+ #
73
+ # Example:
74
+ #
75
+ # map.resources :messages
76
+ #
77
+ # class MessagesController < ActionController::Base
78
+ # # GET messages_url
79
+ # def index
80
+ # # return all messages
81
+ # end
82
+ #
83
+ # # GET new_message_url
84
+ # def new
85
+ # # return an HTML form for describing a new message
86
+ # end
87
+ #
88
+ # # POST messages_url
89
+ # def create
90
+ # # create a new message
91
+ # end
92
+ #
93
+ # # GET message_url(:id => 1)
94
+ # def show
95
+ # # find and return a specific message
96
+ # end
97
+ #
98
+ # # GET edit_message_url(:id => 1)
99
+ # def edit
100
+ # # return an HTML form for editing a specific message
101
+ # end
102
+ #
103
+ # # PUT message_url(:id => 1)
104
+ # def update
105
+ # # find and update a specific message
106
+ # end
107
+ #
108
+ # # DELETE message_url(:id => 1)
109
+ # def destroy
110
+ # # delete a specific message
111
+ # end
112
+ # end
113
+ #
114
+ # The #resources method sets HTTP method restrictions on the routes it generates. For example, making an
115
+ # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
116
+ # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for resource routes.
117
+ #
118
+ # Along with the routes themselves, #resources generates named routes for use in
119
+ # controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
120
+ #
121
+ # Named Route Helpers
122
+ # messages messages_url, hash_for_messages_url,
123
+ # messages_path, hash_for_messages_path
124
+ # message message_url(id), hash_for_message_url(id),
125
+ # message_path(id), hash_for_message_path(id)
126
+ # new_message new_message_url, hash_for_new_message_url,
127
+ # new_message_path, hash_for_new_message_path
128
+ # edit_message edit_message_url(id), hash_for_edit_message_url(id),
129
+ # edit_message_path(id), hash_for_edit_message_path(id)
130
+ #
131
+ # You can use these helpers instead of #url_for or methods that take #url_for parameters:
132
+ #
133
+ # redirect_to :controller => 'messages', :action => 'index'
134
+ # # becomes
135
+ # redirect_to messages_url
136
+ #
137
+ # <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
138
+ # # becomes
139
+ # <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
140
+ #
141
+ # Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
142
+ # form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
143
+ #
144
+ # <%= form_tag message_path(@message), :method => :put %>
145
+ #
146
+ # or
147
+ #
148
+ # <% form_for :message, @message, message_path(@message), :html => {:method => :put} do |f| %>
149
+ #
150
+ # The #resources method accepts various options, too, to customize the resulting
151
+ # routes:
152
+ # * <tt>:controller</tt> -- specify the controller name for the routes.
153
+ # * <tt>:singular</tt> -- specify the singular name used in the member routes.
154
+ # * <tt>:path_prefix</tt> -- set a prefix to the routes with required route variables.
155
+ # Weblog comments usually belong to a post, so you might use resources like:
156
+ #
157
+ # map.resources :articles
158
+ # map.resources :comments, :path_prefix => '/articles/:article_id'
159
+ #
160
+ # You can nest resources calls to set this automatically:
161
+ #
162
+ # map.resources :articles do |article|
163
+ # article.resources :comments
164
+ # end
165
+ #
166
+ # The comment resources work the same, but must now include a value for :article_id.
167
+ #
168
+ # comments_url(@article)
169
+ # comment_url(@article, @comment)
170
+ #
171
+ # comments_url(:article_id => @article)
172
+ # comment_url(:article_id => @article, :id => @comment)
173
+ #
174
+ # * <tt>:name_prefix</tt> -- define a prefix for all generated routes, usually ending in an underscore.
175
+ # Use this if you have named routes that may clash.
176
+ #
177
+ # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
178
+ # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
179
+ #
180
+ # * <tt>:collection</tt> -- add named routes for other actions that operate on the collection.
181
+ # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>
182
+ # or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages;rss, with a route of rss_messages_url.
183
+ # * <tt>:member</tt> -- same as :collection, but for actions that operate on a specific member.
184
+ # * <tt>:new</tt> -- same as :collection, but for actions that operate on the new resource action.
185
+ #
186
+ # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
187
+ #
188
+ # Examples:
189
+ #
190
+ # map.resources :messages, :path_prefix => "/thread/:thread_id"
191
+ # # --> GET /thread/7/messages/1
192
+ #
193
+ # map.resources :messages, :collection => { :rss => :get }
194
+ # # --> GET /messages;rss (maps to the #rss action)
195
+ # # also adds a named route called "rss_messages"
196
+ #
197
+ # map.resources :messages, :member => { :mark => :post }
198
+ # # --> POST /messages/1;mark (maps to the #mark action)
199
+ # # also adds a named route called "mark_message"
200
+ #
201
+ # map.resources :messages, :new => { :preview => :post }
202
+ # # --> POST /messages/new;preview (maps to the #preview action)
203
+ # # also adds a named route called "preview_new_message"
204
+ #
205
+ # map.resources :messages, :new => { :new => :any, :preview => :post }
206
+ # # --> POST /messages/new;preview (maps to the #preview action)
207
+ # # also adds a named route called "preview_new_message"
208
+ # # --> /messages/new can be invoked via any request method
209
+ #
210
+ # map.resources :messages, :controller => "categories",
211
+ # :path_prefix => "/category/:category_id",
212
+ # :name_prefix => "category_"
213
+ # # --> GET /categories/7/messages/1
214
+ # # has named route "category_message"
215
+ def resources(*entities, &block)
216
+ options = entities.last.is_a?(Hash) ? entities.pop : { }
217
+ entities.each { |entity| map_resource entity, options.dup, &block }
218
+ end
219
+
220
+ private
221
+ def map_resource(entities, options = {}, &block)
222
+ resource = Resource.new(entities, options)
223
+
224
+ with_options :controller => resource.controller do |map|
225
+ map_collection_actions(map, resource)
226
+ map_new_actions(map, resource)
227
+ map_member_actions(map, resource)
228
+
229
+ if block_given?
230
+ with_options(:path_prefix => resource.nesting_path_prefix, &block)
231
+ end
232
+ end
233
+ end
234
+
235
+ def map_collection_actions(map, resource)
236
+ resource.collection_methods.each do |method, actions|
237
+ route_options = requirements_for(method)
238
+
239
+ actions.each do |action|
240
+ map.named_route(
241
+ "#{resource.name_prefix}#{action}_#{resource.plural}",
242
+ "#{resource.path};#{action}",
243
+ route_options.merge(:action => action.to_s)
244
+ )
245
+
246
+ map.named_route(
247
+ "formatted_#{resource.name_prefix}#{action}_#{resource.plural}",
248
+ "#{resource.path}.:format;#{action}",
249
+ route_options.merge(:action => action.to_s)
250
+ )
251
+ end
252
+ end
253
+
254
+ map.named_route("#{resource.name_prefix}#{resource.plural}", resource.path, :action => "index", :conditions => { :method => :get })
255
+ map.named_route("formatted_#{resource.name_prefix}#{resource.plural}", "#{resource.path}.:format", :action => "index", :conditions => { :method => :get })
256
+
257
+ map.connect(resource.path, :action => "create", :conditions => { :method => :post })
258
+ map.connect("#{resource.path}.:format", :action => "create", :conditions => { :method => :post })
259
+ end
260
+
261
+ def map_new_actions(map, resource)
262
+ resource.new_methods.each do |method, actions|
263
+ route_options = requirements_for(method)
264
+ actions.each do |action|
265
+ if action == :new
266
+ map.named_route("#{resource.name_prefix}new_#{resource.singular}", resource.new_path, route_options.merge(:action => "new"))
267
+ map.named_route("formatted_#{resource.name_prefix}new_#{resource.singular}", "#{resource.new_path}.:format", route_options.merge(:action => "new"))
268
+ else
269
+ map.named_route("#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path};#{action}", route_options.merge(:action => action.to_s))
270
+ map.named_route("formatted_#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path}.:format;#{action}", route_options.merge(:action => action.to_s))
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ def map_member_actions(map, resource)
277
+ resource.member_methods.each do |method, actions|
278
+ route_options = requirements_for(method)
279
+
280
+ actions.each do |action|
281
+ map.named_route("#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path};#{action}", route_options.merge(:action => action.to_s))
282
+ map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path}.:format;#{action}", route_options.merge(:action => action.to_s))
283
+ end
284
+ end
285
+
286
+ map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, :action => "show", :conditions => { :method => :get })
287
+ map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", :action => "show", :conditions => { :method => :get })
288
+
289
+ map.connect(resource.member_path, :action => "update", :conditions => { :method => :put })
290
+ map.connect("#{resource.member_path}.:format", :action => "update", :conditions => { :method => :put })
291
+
292
+ map.connect(resource.member_path, :action => "destroy", :conditions => { :method => :delete })
293
+ map.connect("#{resource.member_path}.:format", :action => "destroy", :conditions => { :method => :delete })
294
+ end
295
+
296
+ def requirements_for(method)
297
+ method == :any ? {} : { :conditions => { :method => method } }
298
+ end
299
+ end
300
+ end
301
+
302
+ ActionController::Routing::RouteSet::Mapper.send :include, ActionController::Resources