actionpack 3.1.12 → 3.2.0.rc1

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 (128) hide show
  1. data/CHANGELOG.md +5503 -108
  2. data/README.rdoc +3 -3
  3. data/lib/abstract_controller/asset_paths.rb +1 -1
  4. data/lib/abstract_controller/base.rb +1 -1
  5. data/lib/abstract_controller/callbacks.rb +102 -18
  6. data/lib/abstract_controller/helpers.rb +1 -1
  7. data/lib/abstract_controller/layouts.rb +116 -50
  8. data/lib/abstract_controller/logger.rb +1 -1
  9. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  10. data/lib/abstract_controller/rendering.rb +1 -6
  11. data/lib/abstract_controller/view_paths.rb +6 -5
  12. data/lib/action_controller.rb +0 -15
  13. data/lib/action_controller/caching.rb +0 -1
  14. data/lib/action_controller/caching/actions.rb +5 -6
  15. data/lib/action_controller/caching/fragments.rb +18 -18
  16. data/lib/action_controller/caching/pages.rb +7 -6
  17. data/lib/action_controller/caching/sweeping.rb +1 -1
  18. data/lib/action_controller/log_subscriber.rb +8 -4
  19. data/lib/action_controller/metal.rb +7 -1
  20. data/lib/action_controller/metal/conditional_get.rb +49 -4
  21. data/lib/action_controller/metal/data_streaming.rb +17 -5
  22. data/lib/action_controller/metal/force_ssl.rb +8 -5
  23. data/lib/action_controller/metal/helpers.rb +7 -4
  24. data/lib/action_controller/metal/http_authentication.rb +9 -12
  25. data/lib/action_controller/metal/instrumentation.rb +9 -4
  26. data/lib/action_controller/metal/mime_responds.rb +4 -4
  27. data/lib/action_controller/metal/params_wrapper.rb +12 -8
  28. data/lib/action_controller/metal/redirecting.rb +7 -6
  29. data/lib/action_controller/metal/renderers.rb +9 -11
  30. data/lib/action_controller/metal/request_forgery_protection.rb +2 -1
  31. data/lib/action_controller/metal/rescue.rb +13 -0
  32. data/lib/action_controller/metal/responder.rb +11 -23
  33. data/lib/action_controller/metal/streaming.rb +0 -25
  34. data/lib/action_controller/railtie.rb +1 -0
  35. data/lib/action_controller/railties/paths.rb +4 -3
  36. data/lib/action_controller/record_identifier.rb +4 -4
  37. data/lib/action_controller/test_case.rb +60 -56
  38. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +6 -6
  39. data/lib/action_dispatch.rb +5 -1
  40. data/lib/action_dispatch/http/cache.rb +27 -15
  41. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  42. data/lib/action_dispatch/http/headers.rb +3 -5
  43. data/lib/action_dispatch/http/mime_negotiation.rb +2 -1
  44. data/lib/action_dispatch/http/mime_type.rb +7 -3
  45. data/lib/action_dispatch/http/mime_types.rb +12 -0
  46. data/lib/action_dispatch/http/parameter_filter.rb +3 -1
  47. data/lib/action_dispatch/http/parameters.rb +0 -4
  48. data/lib/action_dispatch/http/request.rb +18 -68
  49. data/lib/action_dispatch/http/response.rb +11 -32
  50. data/lib/action_dispatch/http/upload.rb +3 -14
  51. data/lib/action_dispatch/http/url.rb +1 -1
  52. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  53. data/lib/action_dispatch/middleware/cookies.rb +20 -16
  54. data/lib/action_dispatch/middleware/debug_exceptions.rb +82 -0
  55. data/lib/action_dispatch/middleware/exception_wrapper.rb +78 -0
  56. data/lib/action_dispatch/middleware/flash.rb +6 -9
  57. data/lib/action_dispatch/middleware/params_parser.rb +6 -11
  58. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -0
  59. data/lib/action_dispatch/middleware/reloader.rb +38 -14
  60. data/lib/action_dispatch/middleware/remote_ip.rb +66 -36
  61. data/lib/action_dispatch/middleware/request_id.rb +39 -0
  62. data/lib/action_dispatch/middleware/session/abstract_store.rb +4 -16
  63. data/lib/action_dispatch/middleware/session/cache_store.rb +50 -0
  64. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  65. data/lib/action_dispatch/middleware/show_exceptions.rb +58 -142
  66. data/lib/action_dispatch/middleware/static.rb +2 -10
  67. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  68. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +13 -8
  69. data/lib/action_dispatch/railtie.rb +15 -1
  70. data/lib/action_dispatch/routing.rb +1 -2
  71. data/lib/action_dispatch/routing/mapper.rb +108 -107
  72. data/lib/action_dispatch/routing/redirection.rb +63 -69
  73. data/lib/action_dispatch/routing/route_set.rb +75 -43
  74. data/lib/action_dispatch/routing/routes_proxy.rb +0 -4
  75. data/lib/action_dispatch/routing/url_for.rb +3 -3
  76. data/lib/action_dispatch/testing/assertions/response.rb +5 -7
  77. data/lib/action_dispatch/testing/assertions/routing.rb +10 -9
  78. data/lib/action_dispatch/testing/integration.rb +8 -25
  79. data/lib/action_dispatch/testing/test_process.rb +3 -2
  80. data/lib/action_dispatch/testing/test_request.rb +4 -23
  81. data/lib/action_pack/version.rb +3 -3
  82. data/lib/action_view.rb +1 -5
  83. data/lib/action_view/asset_paths.rb +7 -8
  84. data/lib/action_view/base.rb +7 -5
  85. data/lib/action_view/helpers/asset_paths.rb +1 -1
  86. data/lib/action_view/helpers/asset_tag_helper.rb +4 -8
  87. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +3 -0
  88. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  89. data/lib/action_view/helpers/capture_helper.rb +3 -3
  90. data/lib/action_view/helpers/controller_helper.rb +1 -1
  91. data/lib/action_view/helpers/date_helper.rb +26 -18
  92. data/lib/action_view/helpers/debug_helper.rb +1 -1
  93. data/lib/action_view/helpers/form_helper.rb +71 -13
  94. data/lib/action_view/helpers/form_options_helper.rb +65 -34
  95. data/lib/action_view/helpers/form_tag_helper.rb +24 -18
  96. data/lib/action_view/helpers/javascript_helper.rb +12 -3
  97. data/lib/action_view/helpers/number_helper.rb +3 -2
  98. data/lib/action_view/helpers/record_tag_helper.rb +51 -5
  99. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  100. data/lib/action_view/helpers/sanitize_helper.rb +6 -7
  101. data/lib/action_view/helpers/tag_helper.rb +1 -1
  102. data/lib/action_view/helpers/text_helper.rb +5 -4
  103. data/lib/action_view/helpers/url_helper.rb +19 -11
  104. data/lib/action_view/locale/en.yml +6 -0
  105. data/lib/action_view/log_subscriber.rb +1 -1
  106. data/lib/action_view/lookup_context.rb +123 -125
  107. data/lib/action_view/path_set.rb +60 -13
  108. data/lib/action_view/renderer/abstract_renderer.rb +16 -11
  109. data/lib/action_view/renderer/partial_renderer.rb +59 -40
  110. data/lib/action_view/renderer/template_renderer.rb +29 -17
  111. data/lib/action_view/template.rb +0 -1
  112. data/lib/action_view/template/error.rb +6 -5
  113. data/lib/action_view/template/handlers.rb +0 -6
  114. data/lib/action_view/template/handlers/builder.rb +10 -1
  115. data/lib/action_view/template/handlers/erb.rb +2 -2
  116. data/lib/action_view/template/resolver.rb +20 -31
  117. data/lib/action_view/test_case.rb +7 -10
  118. data/lib/sprockets/assets.rake +1 -1
  119. data/lib/sprockets/bootstrap.rb +3 -31
  120. data/lib/sprockets/compressors.rb +69 -7
  121. data/lib/sprockets/helpers/rails_helper.rb +6 -11
  122. data/lib/sprockets/railtie.rb +1 -0
  123. data/lib/sprockets/static_compiler.rb +0 -3
  124. metadata +57 -86
  125. checksums.yaml +0 -7
  126. data/lib/action_dispatch/middleware/closed_error.rb +0 -7
  127. data/lib/action_dispatch/routing/route.rb +0 -67
  128. data/lib/action_view/template/handler.rb +0 -49
@@ -7,7 +7,7 @@ module AbstractController
7
7
 
8
8
  included do
9
9
  config_accessor :logger
10
- extend ActiveSupport::Benchmarkable
10
+ include ActiveSupport::Benchmarkable
11
11
  end
12
12
  end
13
13
  end
@@ -5,8 +5,8 @@ module AbstractController
5
5
  Module.new do
6
6
  define_method(:inherited) do |klass|
7
7
  super(klass)
8
- if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
9
- klass.send(:include, namespace._railtie.routes.url_helpers)
8
+ if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
9
+ klass.send(:include, namespace.railtie_routes_url_helpers)
10
10
  else
11
11
  klass.send(:include, routes.url_helpers)
12
12
  end
@@ -66,12 +66,7 @@ module AbstractController
66
66
  end
67
67
 
68
68
  def view_context_class
69
- @_view_context_class || self.class.view_context_class
70
- end
71
-
72
- def initialize(*)
73
- @_view_context_class = nil
74
- super
69
+ @_view_context_class ||= self.class.view_context_class
75
70
  end
76
71
 
77
72
  # An instance of a view class. The default view class is ActionView::Base
@@ -1,3 +1,5 @@
1
+ require 'action_view/base'
2
+
1
3
  module AbstractController
2
4
  module ViewPaths
3
5
  extend ActiveSupport::Concern
@@ -8,7 +10,7 @@ module AbstractController
8
10
  self._view_paths.freeze
9
11
  end
10
12
 
11
- delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
13
+ delegate :template_exists?, :view_paths, :formats, :formats=,
12
14
  :locale, :locale=, :to => :lookup_context
13
15
 
14
16
  module ClassMethods
@@ -63,7 +65,7 @@ module AbstractController
63
65
  # the default view path. You may also provide a custom view path
64
66
  # (see ActionView::PathSet for more information)
65
67
  def append_view_path(path)
66
- self.view_paths = view_paths.dup + Array(path)
68
+ self._view_paths = view_paths + Array(path)
67
69
  end
68
70
 
69
71
  # Prepend a path to the list of view paths for this controller.
@@ -73,7 +75,7 @@ module AbstractController
73
75
  # the default view path. You may also provide a custom view path
74
76
  # (see ActionView::PathSet for more information)
75
77
  def prepend_view_path(path)
76
- self.view_paths = Array(path) + view_paths.dup
78
+ self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
77
79
  end
78
80
 
79
81
  # A list of all of the default view paths for this controller.
@@ -87,8 +89,7 @@ module AbstractController
87
89
  # * <tt>paths</tt> - If a PathSet is provided, use that;
88
90
  # otherwise, process the parameter into a PathSet.
89
91
  def view_paths=(paths)
90
- self._view_paths = ActionView::Base.process_view_paths(paths)
91
- self._view_paths.freeze
92
+ self._view_paths = ActionView::PathSet.new(Array.wrap(paths))
92
93
  end
93
94
  end
94
95
  end
@@ -47,21 +47,6 @@ module ActionController
47
47
 
48
48
  eager_autoload do
49
49
  autoload :RecordIdentifier
50
-
51
- # TODO: Don't autoload exceptions, setup explicit
52
- # requires for files that need them
53
- autoload_at "action_controller/metal/exceptions" do
54
- autoload :ActionControllerError
55
- autoload :RenderError
56
- autoload :RoutingError
57
- autoload :MethodNotAllowed
58
- autoload :NotImplemented
59
- autoload :UnknownController
60
- autoload :MissingFile
61
- autoload :RenderError
62
- autoload :SessionOverflowError
63
- autoload :UnknownHttpMethod
64
- end
65
50
  end
66
51
  end
67
52
 
@@ -24,7 +24,6 @@ module ActionController #:nodoc:
24
24
  #
25
25
  # config.action_controller.cache_store = :memory_store
26
26
  # config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
27
- # config.action_controller.cache_store = :drb_store, "druby://localhost:9192"
28
27
  # config.action_controller.cache_store = :mem_cache_store, "localhost"
29
28
  # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
30
29
  # config.action_controller.cache_store = MyOwnStore.new("parameter")
@@ -38,9 +38,9 @@ module ActionController #:nodoc:
38
38
  # <tt>:action => 'lists'</tt> is not the same as
39
39
  # <tt>:action => 'list', :format => :xml</tt>.
40
40
  #
41
- # You can set modify the default action cache path by passing a
42
- # <tt>:cache_path</tt> option. This will be passed directly to
43
- # <tt>ActionCachePath.path_for</tt>. This is handy for actions with
41
+ # You can modify the default action cache path by passing a
42
+ # <tt>:cache_path</tt> option. This will be passed directly to
43
+ # <tt>ActionCachePath.path_for</tt>. This is handy for actions with
44
44
  # multiple possible routes that should be cached differently. If a
45
45
  # block is given, it is called with the current controller instance.
46
46
  #
@@ -116,9 +116,8 @@ module ActionController #:nodoc:
116
116
  def expire_action(options = {})
117
117
  return unless cache_configured?
118
118
 
119
- actions = options[:action]
120
- if actions.is_a?(Array)
121
- actions.each {|action| expire_action(options.merge(:action => action)) }
119
+ if options.is_a?(Hash) && options[:action].is_a?(Array)
120
+ options[:action].each {|action| expire_action(options.merge(:action => action)) }
122
121
  else
123
122
  expire_fragment(ActionCachePath.new(self, options, false).path)
124
123
  end
@@ -1,36 +1,36 @@
1
1
  module ActionController #:nodoc:
2
2
  module Caching
3
- # Fragment caching is used for caching various blocks within
3
+ # Fragment caching is used for caching various blocks within
4
4
  # views without caching the entire action as a whole. This is
5
- # useful when certain elements of an action change frequently or
6
- # depend on complicated state while other parts rarely change or
5
+ # useful when certain elements of an action change frequently or
6
+ # depend on complicated state while other parts rarely change or
7
7
  # can be shared amongst multiple parties. The caching is done using
8
- # the <tt>cache</tt> helper available in the Action View. A
8
+ # the <tt>cache</tt> helper available in the Action View. A
9
9
  # template with fragment caching might look like:
10
10
  #
11
11
  # <b>Hello <%= @name %></b>
12
12
  #
13
13
  # <% cache do %>
14
14
  # All the topics in the system:
15
- # <%= render :partial => "topic", :collection => Topic.find(:all) %>
15
+ # <%= render :partial => "topic", :collection => Topic.all %>
16
16
  # <% end %>
17
17
  #
18
18
  # This cache will bind the name of the action that called it, so if
19
- # this code was part of the view for the topics/list action, you
19
+ # this code was part of the view for the topics/list action, you
20
20
  # would be able to invalidate it using:
21
21
  #
22
22
  # expire_fragment(:controller => "topics", :action => "list")
23
23
  #
24
- # This default behavior is limited if you need to cache multiple
25
- # fragments per action or if the action itself is cached using
26
- # <tt>caches_action</tt>. To remedy this, there is an option to
27
- # qualify the name of the cached fragment by using the
24
+ # This default behavior is limited if you need to cache multiple
25
+ # fragments per action or if the action itself is cached using
26
+ # <tt>caches_action</tt>. To remedy this, there is an option to
27
+ # qualify the name of the cached fragment by using the
28
28
  # <tt>:action_suffix</tt> option:
29
29
  #
30
30
  # <% cache(:action => "list", :action_suffix => "all_topics") do %>
31
31
  #
32
- # That would result in a name such as
33
- # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
32
+ # That would result in a name such as
33
+ # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
34
34
  # action cache and with any fragments that use a different suffix.
35
35
  # Note that the URL doesn't have to really exist or be callable
36
36
  # - the url_for system is just used to generate unique cache names
@@ -38,21 +38,21 @@ module ActionController #:nodoc:
38
38
  #
39
39
  # The expiration call for this example is:
40
40
  #
41
- # expire_fragment(:controller => "topics",
42
- # :action => "list",
41
+ # expire_fragment(:controller => "topics",
42
+ # :action => "list",
43
43
  # :action_suffix => "all_topics")
44
44
  module Fragments
45
45
  # Given a key (as described in <tt>expire_fragment</tt>), returns
46
- # a key suitable for use in reading, writing, or expiring a
46
+ # a key suitable for use in reading, writing, or expiring a
47
47
  # cached fragment. If the key is a hash, the generated key is the
48
- # return value of url_for on that hash (without the protocol).
48
+ # return value of url_for on that hash (without the protocol).
49
49
  # All keys are prefixed with <tt>views/</tt> and uses
50
50
  # ActiveSupport::Cache.expand_cache_key for the expansion.
51
51
  def fragment_cache_key(key)
52
52
  ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
53
53
  end
54
54
 
55
- # Writes <tt>content</tt> to the location signified by
55
+ # Writes <tt>content</tt> to the location signified by
56
56
  # <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
57
57
  def write_fragment(key, content, options = nil)
58
58
  return content unless cache_configured?
@@ -77,7 +77,7 @@ module ActionController #:nodoc:
77
77
  end
78
78
  end
79
79
 
80
- # Check if a cached fragment from the location signified by
80
+ # Check if a cached fragment from the location signified by
81
81
  # <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
82
82
  def fragment_exist?(key, options = nil)
83
83
  return unless cache_configured?
@@ -16,9 +16,10 @@ module ActionController #:nodoc:
16
16
  # caches_page :show, :new
17
17
  # end
18
18
  #
19
- # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>,
20
- # which match the URLs used to trigger the dynamic generation. This is how the web server is able
21
- # pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it.
19
+ # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
20
+ # that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the
21
+ # existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack.
22
+ # This is much faster than handling the full dynamic request in the usual way.
22
23
  #
23
24
  # Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
24
25
  # is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
@@ -121,7 +122,7 @@ module ActionController #:nodoc:
121
122
 
122
123
  if options.is_a?(Hash)
123
124
  if options[:action].is_a?(Array)
124
- options[:action].dup.each do |action|
125
+ options[:action].each do |action|
125
126
  self.class.expire_page(url_for(options.merge(:only_path => true, :action => action)))
126
127
  end
127
128
  else
@@ -132,8 +133,8 @@ module ActionController #:nodoc:
132
133
  end
133
134
  end
134
135
 
135
- # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
136
- # If no options are provided, the requested url is used. Example:
136
+ # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used.
137
+ # If no options are provided, the url of the current request being handled is used. Example:
137
138
  # cache_page "I'm the cached content", :controller => "lists", :action => "show"
138
139
  def cache_page(content = nil, options = nil)
139
140
  return unless self.class.perform_caching && caching_allowed?
@@ -88,7 +88,7 @@ module ActionController #:nodoc:
88
88
  end
89
89
 
90
90
  def method_missing(method, *arguments, &block)
91
- return if @controller.nil?
91
+ return unless @controller
92
92
  @controller.__send__(method, *arguments, &block)
93
93
  end
94
94
  end
@@ -10,7 +10,7 @@ module ActionController
10
10
  format = payload[:format]
11
11
  format = format.to_s.upcase if format.is_a?(Symbol)
12
12
 
13
- info " Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
13
+ info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
14
14
  info " Parameters: #{params.inspect}" unless params.empty?
15
15
  end
16
16
 
@@ -20,14 +20,18 @@ module ActionController
20
20
 
21
21
  status = payload[:status]
22
22
  if status.nil? && payload[:exception].present?
23
- status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
24
- end
23
+ status = Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code)
24
+ end
25
25
  message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
26
26
  message << " (#{additions.join(" | ")})" unless additions.blank?
27
27
 
28
28
  info(message)
29
29
  end
30
30
 
31
+ def halted_callback(event)
32
+ info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
33
+ end
34
+
31
35
  def send_file(event)
32
36
  message = "Sent file %s"
33
37
  message << " (%.1fms)"
@@ -59,4 +63,4 @@ module ActionController
59
63
  end
60
64
  end
61
65
 
62
- ActionController::LogSubscriber.attach_to :action_controller
66
+ ActionController::LogSubscriber.attach_to :action_controller
@@ -182,7 +182,13 @@ module ActionController
182
182
  end
183
183
 
184
184
  def response_body=(val)
185
- body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
185
+ body = if val.is_a?(String)
186
+ [val]
187
+ elsif val.nil? || val.respond_to?(:each)
188
+ val
189
+ else
190
+ [val]
191
+ end
186
192
  super body
187
193
  end
188
194
 
@@ -23,8 +23,27 @@ module ActionController
23
23
  # This will render the show template if the request isn't sending a matching etag or
24
24
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
25
25
  #
26
- def fresh_when(options)
27
- options.assert_valid_keys(:etag, :last_modified, :public)
26
+ # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
27
+ #
28
+ # def show
29
+ # @article = Article.find(params[:id])
30
+ # fresh_when(@article)
31
+ # end
32
+ #
33
+ # When passing a record, you can still set whether the public header:
34
+ #
35
+ # def show
36
+ # @article = Article.find(params[:id])
37
+ # fresh_when(@article, :public => true)
38
+ # end
39
+ def fresh_when(record_or_options, additional_options = {})
40
+ if record_or_options.is_a? Hash
41
+ options = record_or_options
42
+ options.assert_valid_keys(:etag, :last_modified, :public)
43
+ else
44
+ record = record_or_options
45
+ options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
46
+ end
28
47
 
29
48
  response.etag = options[:etag] if options[:etag]
30
49
  response.last_modified = options[:last_modified] if options[:last_modified]
@@ -55,8 +74,34 @@ module ActionController
55
74
  # end
56
75
  # end
57
76
  # end
58
- def stale?(options)
59
- fresh_when(options)
77
+ #
78
+ # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
79
+ #
80
+ # def show
81
+ # @article = Article.find(params[:id])
82
+ #
83
+ # if stale?(@article)
84
+ # @statistics = @article.really_expensive_call
85
+ # respond_to do |format|
86
+ # # all the supported formats
87
+ # end
88
+ # end
89
+ # end
90
+ #
91
+ # When passing a record, you can still set whether the public header:
92
+ #
93
+ # def show
94
+ # @article = Article.find(params[:id])
95
+ #
96
+ # if stale?(@article, :public => true)
97
+ # @statistics = @article.really_expensive_call
98
+ # respond_to do |format|
99
+ # # all the supported formats
100
+ # end
101
+ # end
102
+ # end
103
+ def stale?(record_or_options, additional_options = {})
104
+ fresh_when(record_or_options, additional_options)
60
105
  !request.fresh?(response)
61
106
  end
62
107
 
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/file/path'
2
+ require 'action_controller/metal/exceptions'
2
3
 
3
4
  module ActionController #:nodoc:
4
5
  # Methods for sending arbitrary data and for streaming files to the browser,
@@ -26,8 +27,11 @@ module ActionController #:nodoc:
26
27
  # Options:
27
28
  # * <tt>:filename</tt> - suggests a filename for the browser to use.
28
29
  # Defaults to <tt>File.basename(path)</tt>.
29
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
30
- # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
30
+ # * <tt>:type</tt> - specifies an HTTP content type.
31
+ # You can specify either a string or a symbol for a registered type register with
32
+ # <tt>Mime::Type.register</tt>, for example :json
33
+ # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
34
+ # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
31
35
  # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
32
36
  # Valid values are 'inline' and 'attachment' (default).
33
37
  # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
@@ -37,7 +41,7 @@ module ActionController #:nodoc:
37
41
  #
38
42
  # The default Content-Type and Content-Disposition headers are
39
43
  # set to download arbitrary binary files in as many browsers as
40
- # possible. IE versions 4, 5, 5.5, and 6 are all known to have
44
+ # possible. IE versions 4, 5, 5.5, and 6 are all known to have
41
45
  # a variety of quirks (especially when downloading over SSL).
42
46
  #
43
47
  # Simple download:
@@ -58,8 +62,8 @@ module ActionController #:nodoc:
58
62
  #
59
63
  # Also be aware that the document may be cached by proxies and browsers.
60
64
  # The Pragma and Cache-Control headers declare how the file may be cached
61
- # by intermediaries. They default to require clients to validate with
62
- # the server before releasing cached responses. See
65
+ # by intermediaries. They default to require clients to validate with
66
+ # the server before releasing cached responses. See
63
67
  # http://www.mnot.net/cache_docs/ for an overview of web caching and
64
68
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
65
69
  # for the Cache-Control header spec.
@@ -84,6 +88,8 @@ module ActionController #:nodoc:
84
88
  # * <tt>:filename</tt> - suggests a filename for the browser to use.
85
89
  # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
86
90
  # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
91
+ # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
92
+ # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
87
93
  # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
88
94
  # Valid values are 'inline' and 'attachment' (default).
89
95
  # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
@@ -108,6 +114,8 @@ module ActionController #:nodoc:
108
114
 
109
115
  private
110
116
  def send_file_headers!(options)
117
+ type_provided = options.has_key?(:type)
118
+
111
119
  options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
112
120
  [:type, :disposition].each do |arg|
113
121
  raise ArgumentError, ":#{arg} option required" if options[arg].nil?
@@ -123,6 +131,10 @@ module ActionController #:nodoc:
123
131
  raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
124
132
  self.content_type = extension
125
133
  else
134
+ if !type_provided && options[:filename]
135
+ # If type wasn't provided, try guessing from file extension.
136
+ content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.tr('.','')) || content_type
137
+ end
126
138
  self.content_type = content_type
127
139
  end
128
140
 
@@ -1,12 +1,12 @@
1
1
  module ActionController
2
- # This module provides a method which will redirects browser to use HTTPS
2
+ # This module provides a method which will redirect browser to use HTTPS
3
3
  # protocol. This will ensure that user's sensitive information will be
4
4
  # transferred safely over the internet. You _should_ always force browser
5
5
  # to use HTTPS when you're transferring sensitive information such as
6
6
  # user authentication, account information, or credit card information.
7
7
  #
8
- # Note that if you really concern about your application safety, you might
9
- # consider using +config.force_ssl+ in your configuration config file instead.
8
+ # Note that if you are really concerned about your application security,
9
+ # you might consider using +config.force_ssl+ in your config file instead.
10
10
  # That will ensure all the data transferred via HTTPS protocol and prevent
11
11
  # user from getting session hijacked when accessing the site under unsecured
12
12
  # HTTP protocol.
@@ -24,12 +24,15 @@ module ActionController
24
24
  # * <tt>only</tt> - The callback should be run only for this action
25
25
  # * <tt>except<tt> - The callback should be run for all actions except this action
26
26
  def force_ssl(options = {})
27
+ host = options.delete(:host)
27
28
  before_filter(options) do
28
29
  if !request.ssl? && !Rails.env.development?
29
- redirect_to :protocol => 'https://', :status => :moved_permanently
30
+ redirect_options = {:protocol => 'https://', :status => :moved_permanently}
31
+ redirect_options.merge!(:host => host) if host
32
+ redirect_to redirect_options
30
33
  end
31
34
  end
32
35
  end
33
36
  end
34
37
  end
35
- end
38
+ end