actionpack 1.4.0 → 1.5.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 (84) hide show
  1. data/CHANGELOG +66 -0
  2. data/README +94 -64
  3. data/install.rb +1 -20
  4. data/lib/action_controller.rb +15 -7
  5. data/lib/action_controller/assertions/action_pack_assertions.rb +56 -3
  6. data/lib/action_controller/base.rb +137 -64
  7. data/lib/action_controller/caching.rb +11 -11
  8. data/lib/action_controller/cgi_ext/cgi_ext.rb +2 -2
  9. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +4 -4
  10. data/lib/action_controller/cgi_process.rb +9 -1
  11. data/lib/action_controller/components.rb +73 -0
  12. data/lib/action_controller/cookies.rb +1 -1
  13. data/lib/action_controller/dependencies.rb +6 -1
  14. data/lib/action_controller/filters.rb +1 -1
  15. data/lib/action_controller/flash.rb +3 -3
  16. data/lib/action_controller/helpers.rb +17 -21
  17. data/lib/action_controller/layout.rb +2 -2
  18. data/lib/action_controller/request.rb +16 -6
  19. data/lib/action_controller/rescue.rb +15 -3
  20. data/lib/action_controller/routing.rb +304 -0
  21. data/lib/action_controller/scaffolding.rb +10 -12
  22. data/lib/action_controller/session/active_record_store.rb +4 -7
  23. data/lib/action_controller/session/mem_cache_store.rb +2 -2
  24. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +9 -1
  25. data/lib/action_controller/templates/rescues/diagnostics.rhtml +1 -1
  26. data/lib/action_controller/templates/rescues/routing_error.rhtml +8 -0
  27. data/lib/action_controller/test_process.rb +29 -7
  28. data/lib/action_controller/url_rewriter.rb +28 -112
  29. data/lib/action_view.rb +1 -1
  30. data/lib/action_view/base.rb +44 -17
  31. data/lib/action_view/helpers/active_record_helper.rb +1 -1
  32. data/lib/action_view/helpers/asset_tag_helper.rb +59 -0
  33. data/lib/action_view/helpers/date_helper.rb +24 -13
  34. data/lib/action_view/helpers/form_helper.rb +6 -1
  35. data/lib/action_view/helpers/form_options_helper.rb +87 -9
  36. data/lib/action_view/helpers/form_tag_helper.rb +80 -0
  37. data/lib/action_view/helpers/tag_helper.rb +0 -23
  38. data/lib/action_view/helpers/text_helper.rb +26 -1
  39. data/lib/action_view/helpers/url_helper.rb +29 -35
  40. data/lib/action_view/partials.rb +2 -2
  41. data/lib/action_view/vendor/builder/xmlbase.rb +3 -3
  42. data/rakefile +5 -2
  43. data/test/abstract_unit.rb +3 -1
  44. data/test/controller/action_pack_assertions_test.rb +29 -1
  45. data/test/controller/active_record_assertions_test.rb +109 -101
  46. data/test/controller/base_tests.rb +72 -0
  47. data/test/controller/components_test.rb +74 -0
  48. data/test/controller/cookie_test.rb +0 -9
  49. data/test/controller/custom_handler_test.rb +33 -0
  50. data/test/controller/filters_test.rb +36 -0
  51. data/test/controller/helper_test.rb +27 -10
  52. data/test/controller/redirect_test.rb +23 -31
  53. data/test/controller/render_test.rb +81 -66
  54. data/test/controller/request_test.rb +22 -0
  55. data/test/controller/routing_tests.rb +490 -0
  56. data/test/controller/{url_test.rb → url_obsolete.rb} +24 -14
  57. data/test/controller/url_obsolete.rb.rej +747 -0
  58. data/test/fixtures/fun/games/hello_world.rhtml +1 -0
  59. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  60. data/test/template/asset_tag_helper_test.rb +45 -0
  61. data/test/template/form_options_helper_test.rb +161 -1
  62. data/test/template/form_tag_helper_test.rb +22 -0
  63. data/test/template/text_helper_test.rb +7 -0
  64. data/test/template/url_helper_test.rb +5 -2
  65. data/test/template/url_helper_test.rb.rej +105 -0
  66. metadata +32 -27
  67. data/lib/action_controller/support/binding_of_caller.rb +0 -83
  68. data/lib/action_controller/support/breakpoint.rb +0 -518
  69. data/lib/action_controller/support/class_attribute_accessors.rb +0 -57
  70. data/lib/action_controller/support/class_inheritable_attributes.rb +0 -117
  71. data/lib/action_controller/support/clean_logger.rb +0 -10
  72. data/lib/action_controller/support/core_ext.rb +0 -1
  73. data/lib/action_controller/support/core_ext/hash.rb +0 -5
  74. data/lib/action_controller/support/core_ext/hash/keys.rb +0 -35
  75. data/lib/action_controller/support/core_ext/numeric.rb +0 -7
  76. data/lib/action_controller/support/core_ext/numeric/bytes.rb +0 -33
  77. data/lib/action_controller/support/core_ext/numeric/time.rb +0 -59
  78. data/lib/action_controller/support/core_ext/object_and_class.rb +0 -24
  79. data/lib/action_controller/support/core_ext/string.rb +0 -5
  80. data/lib/action_controller/support/core_ext/string/inflections.rb +0 -45
  81. data/lib/action_controller/support/dependencies.rb +0 -63
  82. data/lib/action_controller/support/inflector.rb +0 -84
  83. data/lib/action_controller/support/misc.rb +0 -8
  84. data/lib/action_controller/support/module_attribute_accessors.rb +0 -57
@@ -1,9 +1,10 @@
1
1
  require 'action_controller/request'
2
2
  require 'action_controller/response'
3
+ require 'action_controller/routing'
3
4
  require 'action_controller/url_rewriter'
4
- require 'action_controller/support/class_attribute_accessors'
5
- require 'action_controller/support/class_inheritable_attributes'
6
- require 'action_controller/support/inflector'
5
+ require 'active_support/class_attribute_accessors'
6
+ require 'active_support/class_inheritable_attributes'
7
+ require 'active_support/inflector'
7
8
  require 'drb'
8
9
 
9
10
  module ActionController #:nodoc:
@@ -13,6 +14,15 @@ module ActionController #:nodoc:
13
14
  end
14
15
  class MissingTemplate < ActionControllerError #:nodoc:
15
16
  end
17
+ class RoutingError < ActionControllerError#:nodoc:
18
+ attr_reader :failures
19
+ def initialize(message, failures=[])
20
+ super(message)
21
+ @failures = failures
22
+ end
23
+ end
24
+ class UnknownController < ActionControllerError #:nodoc:
25
+ end
16
26
  class UnknownAction < ActionControllerError #:nodoc:
17
27
  end
18
28
  class MissingFile < ActionControllerError #:nodoc:
@@ -76,9 +86,9 @@ module ActionController #:nodoc:
76
86
  # <input type="text" name="post[name]" value="david">
77
87
  # <input type="text" name="post[address]" value="hyacintvej">
78
88
  #
79
- # A request stemming from a form holding these inputs will include { "post" # => { "name" => "david", "address" => "hyacintvej" } }.
89
+ # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
80
90
  # If the address input had been named "post[address][street]", the @params would have included
81
- # { "post" => { "address" => { "street" => "hyacintvej" } } }. There's no limit to the depth of the nesting.
91
+ # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
82
92
  #
83
93
  # == Sessions
84
94
  #
@@ -146,22 +156,20 @@ module ActionController #:nodoc:
146
156
  #
147
157
  # Redirects work by rewriting the URL of the current action. So if the show action was called by "/library/books/ISBN/0743536703/show",
148
158
  # we can redirect to an edit action simply by doing <tt>redirect_to(:action => "edit")</tt>, which could throw the user to
149
- # "/library/books/ISBN/0743536703/edit". Naturally, you'll need to setup the .htaccess (or other means of URL rewriting for the web server)
150
- # to point to the proper controller and action in the first place, but once you have, it can be rewritten with ease.
159
+ # "/library/books/ISBN/0743536703/edit". Naturally, you'll need to setup the routes configuration file to point to the proper controller
160
+ # and action in the first place, but once you have, it can be rewritten with ease.
151
161
  #
152
- # Let's consider a bunch of examples on how to go from "/library/books/ISBN/0743536703/edit" to somewhere else:
153
- #
154
- # redirect_to(:action => "show", :action_prefix => "XTC/123") =>
155
- # "http://www.singlefile.com/library/books/XTC/123/show"
162
+ # Let's consider a bunch of examples on how to go from "/clients/37signals/basecamp/project/dash" to somewhere else:
156
163
  #
157
- # redirect_to(:path_params => {"type" => "EXBC"}) =>
158
- # "http://www.singlefile.com/library/books/EXBC/0743536703/show"
164
+ # redirect_to(:action => "edit") =>
165
+ # /clients/37signals/basecamp/project/dash
166
+ #
167
+ # redirect_to(:client_name => "nextangle", :project_name => "rails") =>
168
+ # /clients/nextangle/rails/project/dash
159
169
  #
160
- # redirect_to(:controller => "settings") =>
161
- # "http://www.singlefile.com/library/settings/"
170
+ # Those redirects happen under the configuration of:
162
171
  #
163
- # For more examples of redirecting options, have a look at the unit test in test/controller/url_test.rb. It's very readable and will give
164
- # you an excellent understanding of the different options and what they do.
172
+ # map.connect 'clients/:client_name/:project_name/:controller/:action'
165
173
  #
166
174
  # == Calling multiple redirects or renders
167
175
  #
@@ -205,10 +213,16 @@ module ActionController #:nodoc:
205
213
  # should instead be implemented in the controller to determine when debugging screens should be shown.
206
214
  @@consider_all_requests_local = true
207
215
  cattr_accessor :consider_all_requests_local
216
+
217
+ # Enable or disable the collection of failure information for RoutingErrors.
218
+ # This information can be extremely useful when tweaking custom routes, but is
219
+ # pointless once routes have been tested and verified.
220
+ @@debug_routes = true
221
+ cattr_accessor :debug_routes
208
222
 
209
223
  # Template root determines the base from which template references will be made. So a call to render("test/template")
210
224
  # will be converted to "#{template_root}/test/template.rhtml".
211
- cattr_accessor :template_root
225
+ class_inheritable_accessor :template_root
212
226
 
213
227
  # The logger is used for generating information on the action run-time (including benchmarking) if available.
214
228
  # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
@@ -261,6 +275,41 @@ module ActionController #:nodoc:
261
275
  def controller_name
262
276
  Inflector.underscore(controller_class_name.sub(/Controller/, ""))
263
277
  end
278
+
279
+ # Convert the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
280
+ def controller_path
281
+ components = self.name.to_s.split('::').collect { |name| name.underscore }
282
+ components[-1] = $1 if /^(.*)_controller$/ =~ components[-1]
283
+ components.shift if components.first == 'controllers' # Transitional conditional to accomodate root Controllers module
284
+ components.join('/')
285
+ end
286
+
287
+ # Return an array containing the names of public methods that have been marked hidden from the action processor.
288
+ # By default, all methods defined in ActionController::Base and included modules are hidden.
289
+ # More methods can be hidden using +hide_actions+.
290
+ def hidden_actions
291
+ write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods) unless read_inheritable_attribute(:hidden_actions)
292
+ read_inheritable_attribute(:hidden_actions)
293
+ end
294
+
295
+ # Hide each of the given methods from being callable as actions.
296
+ def hide_action(*names)
297
+ write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect {|n| n.to_s})
298
+ end
299
+
300
+ # Set the template root to be one directory behind the root dir of the controller. Examples:
301
+ # /code/weblog/components/admin/users_controller.rb with Admin::UsersController
302
+ # will use /code/weblog/components as template root
303
+ # and find templates in /code/weblog/components/admin/users/
304
+ #
305
+ # /code/weblog/components/admin/parties/users_controller.rb with Admin::Parties::UsersController
306
+ # will also use /code/weblog/components as template root
307
+ # and find templates in /code/weblog/components/admin/parties/users/
308
+ def uses_component_template_root
309
+ path_of_calling_controller = File.dirname(caller[0].split(/:\d+:/).first)
310
+ path_of_controller_root = path_of_calling_controller.sub(/#{controller_path.split("/")[0..-2]}$/, "")
311
+ self.template_root = path_of_controller_root
312
+ end
264
313
  end
265
314
 
266
315
  public
@@ -277,45 +326,65 @@ module ActionController #:nodoc:
277
326
  return @response
278
327
  end
279
328
 
280
- # Returns an URL that has been rewritten according to the hash of +options+ (for doing a complete redirect, use redirect_to). The
281
- # valid keys in options are specified below with an example going from "/library/books/ISBN/0743536703/show" (mapped to
282
- # books_controller?action=show&type=ISBN&id=0743536703):
329
+ # Returns a URL that has been rewritten according to the options hash and the defined Routes.
330
+ # (For doing a complete redirect, use redirect_to).
331
+ #
332
+ # <tt>url_for</tt> is used to:
333
+ # �
334
+ # All keys given to url_for are forwarded to the Route module save for the following:
335
+ # * <tt>:anchor</tt> -- specifies the anchor name to be appended to the path. For example,
336
+ # <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
337
+ # will produce "/posts/show/10#comments".
338
+ # * <tt>:only_path</tt> -- if true, returns the absolute URL (omitting the protocol, host name, and port)
339
+ # * <tt>:host</tt> -- overrides the default (current) host if provided
340
+ # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided
341
+ #
342
+ # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the <base> and a query string.
343
+ # Routes composes a query string as the key/value pairs not included in the <base>.
344
+ #
345
+ # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
346
+ # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
347
+ # �
348
+ # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent'
349
+ # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts'
350
+ # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10'
351
+ #
352
+ # When generating a new URL, missing values may be filled in from the current request's parameters. For example,
353
+ # <tt>url_for :action => 'some_action'</tt> will retain the current controller, as expected. This behavior extends to
354
+ # other parameters, including <tt>:controller</tt>, <tt>:id</tt>, and any other parameters that are placed into a Route's
355
+ # path.
356
+ # �
357
+ # The URL helpers such as <tt>url_for</tt> have a limited form of memory: when generating a new URL, they can look for
358
+ # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be
359
+ # taken from the defaults. There are a few simple rules on how this is performed:
283
360
  #
284
- # .---> controller .--> action
285
- # /library/books/ISBN/0743536703/show
286
- # '------> '--------------> action_prefix
287
- # controller_prefix (or module)
361
+ # * If the controller name begins with a slash, no defaults are used: <tt>url_for :controller => '/home'</tt>
362
+ # * If the controller changes, the action will default to index unless provided
288
363
  #
289
- # * <tt>:controller_prefix</tt> - specifies the string before the controller name, which would be "/library" for the example.
290
- # Called with "/shop" gives "/shop/books/ISBN/0743536703/show".
291
- # * <tt>:module</tt> - serves as a alias to :controller_prefix (overwrites :controller_prefix unless its nil)
292
- # * <tt>:controller</tt> - specifies a new controller and clears out everything after the controller name (including the action,
293
- # the pre- and suffix, and all params), so called with "settings" gives "/library/settings/".
294
- # * <tt>:action_prefix</tt> - specifies the string between the controller name and the action name, which would
295
- # be "/ISBN/0743536703" for the example. Called with "/XTC/123/" gives "/library/books/XTC/123/show".
296
- # * <tt>:action</tt> - specifies a new action, so called with "edit" gives "/library/books/ISBN/0743536703/edit"
297
- # * <tt>:action_suffix</tt> - specifies the string after the action name, which would be empty for the example.
298
- # Called with "/detailed" gives "/library/books/ISBN/0743536703/detailed".
299
- # * <tt>:path_params</tt> - specifies a hash that contains keys mapping to the request parameter names. In the example,
300
- # { "type" => "ISBN", "id" => "0743536703" } would be the path_params. It serves as another way of replacing part of
301
- # the action_prefix or action_suffix. So passing { "type" => "XTC" } would give "/library/books/XTC/0743536703/show".
302
- # * <tt>:id</tt> - shortcut where ":id => 5" can be used instead of specifying :path_params => { "id" => 5 }.
303
- # Called with "123" gives "/library/books/ISBN/123/show".
304
- # * <tt>:params</tt> - specifies a hash that represents the regular request parameters, such as { "cat" => 1,
305
- # "origin" => "there"} that would give "?cat=1&origin=there". Called with { "temporary" => 1 } in the example would give
306
- # "/library/books/ISBN/0743536703/show?temporary=1"
307
- # * <tt>:anchor</tt> - specifies the anchor name to be appended to the path. Called with "x14" would give
308
- # "/library/books/ISBN/0743536703/show#x14"
309
- # * <tt>:only_path</tt> - if true, returns the absolute URL (omitting the protocol, host name, and port).
364
+ # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the
365
+ # route given by <tt>map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'</tt>.
310
366
  #
311
- # Naturally, you can combine multiple options in a single redirect. Examples:
367
+ # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases URLs which are generated
368
+ # from this page.
312
369
  #
313
- # redirect_to(:controller_prefix => "/shop", :controller => "settings")
314
- # redirect_to(:controller_prefix => false, :controller => "settings") # breaks out of the current controller_prefix
315
- # redirect_to(:action => "edit", :id => 3425)
316
- # redirect_to(:action => "edit", :path_params => { "type" => "XTC" }, :params => { "temp" => 1})
317
- # redirect_to(:action => "publish", :action_prefix => "/published", :anchor => "x14")
370
+ # * <tt>url_for :action => 'bio'</tt> -- During the generation of this URL, default values will be used for the first and
371
+ # last components, and the action shall change. The generated URL will be, "people/david/hh/bio".
372
+ # * <tt>url_for :first => 'davids-little-brother'</tt> This generates the URL 'people/hh/davids-little-brother' -- note
373
+ # that this URL leaves out the assumed action of 'bio'.
318
374
  #
375
+ # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
376
+ # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
377
+ # value that appears in the slot for <tt>:first</tt> is not equal to default value for <tt>:first</tt> we stop using
378
+ # defaults. On it's own, this rule can account for much of the typical Rails URL behavior.
379
+ # �
380
+ # Although a convienence, defaults can occasionaly get in your way. In some cases a default persists longer than desired.
381
+ # The default may be cleared by adding <tt>:name => nil</tt> to <tt>url_for</tt>'s options.
382
+ # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
383
+ # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
384
+ # displayed on:
385
+ #
386
+ # url_for :controller => 'posts', :action => nil
387
+ #
319
388
  # Instead of passing an options hash, you can also pass a method reference in the form of a symbol. Consider this example:
320
389
  #
321
390
  # class WeblogController < ActionController::Base
@@ -337,10 +406,6 @@ module ActionController #:nodoc:
337
406
  end
338
407
  end
339
408
 
340
- def module_name
341
- @params["module"]
342
- end
343
-
344
409
  # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
345
410
  def controller_class_name
346
411
  self.class.controller_class_name
@@ -405,10 +470,16 @@ module ActionController #:nodoc:
405
470
 
406
471
  # Renders an empty response that can be used when the request is only interested in triggering an effect. Do note that good
407
472
  # HTTP manners mandate that you don't use GET requests to trigger data changes.
408
- def render_nothing(status = nil)
473
+ def render_nothing(status = nil) #:doc:
409
474
  render_text "", status
410
475
  end
411
476
 
477
+ # Returns the result of the render as a string.
478
+ def render_to_string(template_name = default_template_name) #:doc:
479
+ add_variables_to_assigns
480
+ @template.render_file(template_name)
481
+ end
482
+
412
483
  # Sends the file by streaming it 4096 bytes at a time. This way the
413
484
  # whole file doesn't need to be read into memory at once. This makes
414
485
  # it feasible to send even large files.
@@ -458,6 +529,8 @@ module ActionController #:nodoc:
458
529
  options[:filename] ||= File.basename(path)
459
530
  send_file_headers! options
460
531
 
532
+ @performed_render = false
533
+
461
534
  if options[:stream]
462
535
  render_text do
463
536
  logger.info "Streaming file #{path}" unless logger.nil?
@@ -506,6 +579,7 @@ module ActionController #:nodoc:
506
579
  def send_data(data, options = {}) #:doc:
507
580
  logger.info "Sending data #{options[:filename]}" unless logger.nil?
508
581
  send_file_headers! options.merge(:length => data.size)
582
+ @performed_render = false
509
583
  render_text data
510
584
  end
511
585
 
@@ -521,7 +595,7 @@ module ActionController #:nodoc:
521
595
  # the form of a hash, just like the one you would use for url_for directly. Example:
522
596
  #
523
597
  # def default_url_options(options)
524
- # { :controller_prefix => @project.active? ? "projects/" : "accounts/" }
598
+ # { :project => @project.active? ? @project.url_name : "unknown" }
525
599
  # end
526
600
  #
527
601
  # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
@@ -558,7 +632,7 @@ module ActionController #:nodoc:
558
632
  @performed_redirect = true
559
633
  end
560
634
 
561
- # Resets the session by clearsing out all the objects stored within and initializing a new session object.
635
+ # Resets the session by clearing out all the objects stored within and initializing a new session object.
562
636
  def reset_session #:doc:
563
637
  @request.reset_session
564
638
  @session = @request.session
@@ -594,7 +668,7 @@ module ActionController #:nodoc:
594
668
  end
595
669
 
596
670
  def initialize_current_url
597
- @url = UrlRewriter.new(@request, controller_name, action_name)
671
+ @url = UrlRewriter.new(@request, @params.clone())
598
672
  end
599
673
 
600
674
  def log_processing
@@ -618,10 +692,9 @@ module ActionController #:nodoc:
618
692
  end
619
693
 
620
694
  def action_methods
621
- action_controller_classes = self.class.ancestors.reject{ |a| [Object, Kernel].include?(a) }
622
- action_controller_classes.inject([]) { |action_methods, klass| action_methods + klass.public_instance_methods(false) }
695
+ @action_methods ||= (self.class.public_instance_methods - self.class.hidden_actions)
623
696
  end
624
-
697
+
625
698
  def add_variables_to_assigns
626
699
  add_instance_variables_to_assigns
627
700
  add_class_variables_to_assigns if view_controller_internals
@@ -691,7 +764,7 @@ module ActionController #:nodoc:
691
764
  end
692
765
 
693
766
  def default_template_name(default_action_name = action_name)
694
- module_name ? "#{module_name}/#{controller_name}/#{default_action_name}" : "#{controller_name}/#{default_action_name}"
767
+ "#{self.class.controller_path}/#{default_action_name}"
695
768
  end
696
769
  end
697
770
  end
@@ -23,7 +23,7 @@ module ActionController #:nodoc:
23
23
  # are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are a great fit
24
24
  # for this approach, but account-based systems where people log in and manipulate their own data are often less likely candidates.
25
25
  #
26
- # Specifying which actions to cach is done through the <tt>caches</tt> class method:
26
+ # Specifying which actions to cache is done through the <tt>caches</tt> class method:
27
27
  #
28
28
  # class WeblogController < ActionController::Base
29
29
  # caches_page :show, :new
@@ -67,7 +67,7 @@ module ActionController #:nodoc:
67
67
  def expire_page(path)
68
68
  return unless perform_caching
69
69
  File.delete(page_cache_path(path)) if File.exists?(page_cache_path(path))
70
- logger.info "Expired page: #{path}" unless logger.nil?
70
+ logger.info "Expired page: #{page_cache_file(path)}" unless logger.nil?
71
71
  end
72
72
 
73
73
  # Manually cache the +content+ in the key determined by +path+. Example:
@@ -76,7 +76,7 @@ module ActionController #:nodoc:
76
76
  return unless perform_caching
77
77
  FileUtils.makedirs(File.dirname(page_cache_path(path)))
78
78
  File.open(page_cache_path(path), "w+") { |f| f.write(content) }
79
- logger.info "Cached page: #{path}" unless logger.nil?
79
+ logger.info "Cached page: #{page_cache_file(path)}" unless logger.nil?
80
80
  end
81
81
 
82
82
  # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
@@ -89,12 +89,12 @@ module ActionController #:nodoc:
89
89
  end
90
90
 
91
91
  private
92
+ def page_cache_file(path)
93
+ (path.empty? ? "/index" : path) + ".html"
94
+ end
95
+
92
96
  def page_cache_path(path)
93
- if path[-1,1] == '/'
94
- page_cache_directory + path + '/index'
95
- else
96
- page_cache_directory + path
97
- end
97
+ page_cache_directory + page_cache_file(path)
98
98
  end
99
99
  end
100
100
 
@@ -121,7 +121,7 @@ module ActionController #:nodoc:
121
121
 
122
122
  private
123
123
  def caching_allowed
124
- !@request.post? && (@request.parameters.reject { |k, v| %w( id action controller ).include?(k) }).empty?
124
+ !@request.post?
125
125
  end
126
126
  end
127
127
 
@@ -308,13 +308,13 @@ module ActionController #:nodoc:
308
308
 
309
309
  class DRbStore < MemoryStore #:nodoc:
310
310
  def initialize(address = 'druby://localhost:9192')
311
- @data = DRbObject.new(nil, address)
311
+ @data, @mutex = DRbObject.new(nil, address), Mutex.new
312
312
  end
313
313
  end
314
314
 
315
315
  class MemCacheStore < MemoryStore #:nodoc:
316
316
  def initialize(address = 'localhost')
317
- @data = MemCache.new(address)
317
+ @data, @mutex = MemCache.new(address), Mutex.new
318
318
  end
319
319
  end
320
320
 
@@ -4,7 +4,7 @@ require 'cgi/session/pstore'
4
4
  require 'action_controller/cgi_ext/cgi_methods'
5
5
 
6
6
  # Wrapper around the CGIMethods that have been secluded to allow testing without
7
- # an instatiated CGI object
7
+ # an instantiated CGI object
8
8
  class CGI #:nodoc:
9
9
  class << self
10
10
  alias :escapeHTML_fail_on_nil :escapeHTML
@@ -40,4 +40,4 @@ class CGI #:nodoc:
40
40
  parameters['database_manager'] = CGI::Session::PStore
41
41
  CGI::Session.new(self, parameters)
42
42
  end
43
- end
43
+ end
@@ -14,7 +14,7 @@ class CGI #:nodoc:
14
14
  @params = CGI::parse(read_query_params)
15
15
  end
16
16
 
17
- @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
17
+ @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] || env_table['COOKIE']))
18
18
  end
19
19
 
20
20
  private
@@ -30,13 +30,13 @@ class CGI #:nodoc:
30
30
  case env_table['REQUEST_METHOD']
31
31
  when 'GET', 'HEAD'
32
32
  if defined? MOD_RUBY
33
- Apache::request.args or ''
33
+ Apache::request.args || ''
34
34
  else
35
- env_table['QUERY_STRING'] or ''
35
+ env_table['QUERY_STRING'] || ''
36
36
  end
37
37
  when 'POST'
38
38
  stdinput.binmode if stdinput.respond_to?(:binmode)
39
- content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
39
+ content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
40
40
  env_table['RAW_POST_DATA'] = content.freeze
41
41
  else
42
42
  read_from_cmdline
@@ -46,8 +46,16 @@ module ActionController #:nodoc:
46
46
  super()
47
47
  end
48
48
 
49
+ def query_string
50
+ return @cgi.query_string unless @cgi.query_string.nil? || @cgi.query_string.empty?
51
+ parts = env['REQUEST_URI'].split('?')
52
+ parts.shift
53
+ return parts.join('?')
54
+ end
55
+
49
56
  def query_parameters
50
- @cgi.query_string ? CGIMethods.parse_query_parameters(@cgi.query_string) : {}
57
+ qs = self.query_string
58
+ qs.empty? ? {} : CGIMethods.parse_query_parameters(query_string)
51
59
  end
52
60
 
53
61
  def request_parameters
@@ -0,0 +1,73 @@
1
+ module ActionController #:nodoc:
2
+ # Components allows you to call other actions for their rendered response while execution another action. You can either delegate
3
+ # the entire response rendering or you can mix a partial response in with your other content.
4
+ #
5
+ # class WeblogController < ActionController::Base
6
+ # # Performs a method and then lets hello_world output its render
7
+ # def delegate_action
8
+ # do_other_stuff_before_hello_world
9
+ # render_component :controller => "greeter", :action => "hello_world"
10
+ # end
11
+ # end
12
+ #
13
+ # class GreeterController < ActionController::Base
14
+ # def hello_world
15
+ # render_text "Hello World!"
16
+ # end
17
+ # end
18
+ #
19
+ # The same can be done in a view to do a partial rendering:
20
+ #
21
+ # Let's see a greeting:
22
+ # <%= render_component :controller => "greeter", :action => "hello_world" %>
23
+ module Components
24
+ def self.append_features(base) #:nodoc:
25
+ super
26
+ base.helper do
27
+ def render_component(options)
28
+ @controller.send(:render_component_as_string, options)
29
+ end
30
+ end
31
+ end
32
+
33
+ protected
34
+ # Renders the component specified as the response for the current method
35
+ def render_component(options = {}) #:doc:
36
+ component_logging(options) { render_text(component_response(options).body, response.headers["Status"]) }
37
+ end
38
+
39
+ # Returns the component response as a string
40
+ def render_component_as_string(options) #:doc:
41
+ component_logging(options) { component_response(options, false).body }
42
+ end
43
+
44
+ private
45
+ def component_response(options, reuse_response = true)
46
+ component_class(options).process(request_for_component(options), reuse_response ? @response : response_for_component)
47
+ end
48
+
49
+ def component_class(options)
50
+ options[:controller] ? (options[:controller].camelize + "Controller").constantize : self.class
51
+ end
52
+
53
+ def request_for_component(options)
54
+ request_for_component = @request.dup
55
+ request_for_component.send(
56
+ :instance_variable_set, :@parameters,
57
+ (options[:params] || {}).merge({ "controller" => options[:controller], "action" => options[:action] })
58
+ )
59
+ return request_for_component
60
+ end
61
+
62
+ def response_for_component
63
+ @response.dup
64
+ end
65
+
66
+ def component_logging(options)
67
+ logger.info("Start rendering component (#{options.inspect}): ") unless logger.nil?
68
+ result = yield
69
+ logger.info("\n\nEnd of component rendering") unless logger.nil?
70
+ return result
71
+ end
72
+ end
73
+ end