actionpack 3.0.20 → 3.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +88 -142
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -6
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +24 -19
- data/lib/abstract_controller/callbacks.rb +19 -19
- data/lib/abstract_controller/helpers.rb +11 -13
- data/lib/abstract_controller/layouts.rb +4 -5
- data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
- data/lib/abstract_controller/rendering.rb +34 -31
- data/lib/abstract_controller/url_for.rb +27 -0
- data/lib/abstract_controller/view_paths.rb +31 -6
- data/lib/action_controller.rb +5 -3
- data/lib/action_controller/base.rb +15 -16
- data/lib/action_controller/caching.rb +2 -2
- data/lib/action_controller/caching/actions.rb +11 -12
- data/lib/action_controller/caching/fragments.rb +41 -19
- data/lib/action_controller/caching/pages.rb +3 -9
- data/lib/action_controller/caching/sweeping.rb +0 -1
- data/lib/action_controller/deprecated.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +1 -1
- data/lib/action_controller/metal.rb +78 -20
- data/lib/action_controller/metal/compatibility.rb +0 -9
- data/lib/action_controller/metal/conditional_get.rb +9 -9
- data/lib/action_controller/metal/data_streaming.rb +145 -0
- data/lib/action_controller/metal/force_ssl.rb +35 -0
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +37 -44
- data/lib/action_controller/metal/hide_actions.rb +2 -3
- data/lib/action_controller/metal/http_authentication.rb +41 -38
- data/lib/action_controller/metal/implicit_render.rb +13 -13
- data/lib/action_controller/metal/instrumentation.rb +2 -2
- data/lib/action_controller/metal/mime_responds.rb +25 -19
- data/lib/action_controller/metal/params_wrapper.rb +224 -0
- data/lib/action_controller/metal/redirecting.rb +6 -2
- data/lib/action_controller/metal/renderers.rb +50 -36
- data/lib/action_controller/metal/rendering.rb +34 -25
- data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
- data/lib/action_controller/metal/responder.rb +47 -12
- data/lib/action_controller/metal/streaming.rb +244 -138
- data/lib/action_controller/metal/testing.rb +0 -9
- data/lib/action_controller/metal/url_for.rb +12 -14
- data/lib/action_controller/railtie.rb +19 -37
- data/lib/action_controller/railties/paths.rb +24 -0
- data/lib/action_controller/record_identifier.rb +4 -10
- data/lib/action_controller/test_case.rb +36 -19
- data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
- data/lib/action_dispatch.rb +4 -1
- data/lib/action_dispatch/http/cache.rb +5 -32
- data/lib/action_dispatch/http/filter_parameters.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
- data/lib/action_dispatch/http/mime_type.rb +45 -5
- data/lib/action_dispatch/http/rack_cache.rb +58 -0
- data/lib/action_dispatch/http/request.rb +27 -41
- data/lib/action_dispatch/http/response.rb +56 -54
- data/lib/action_dispatch/http/upload.rb +1 -11
- data/lib/action_dispatch/http/url.rb +102 -42
- data/lib/action_dispatch/middleware/callbacks.rb +8 -25
- data/lib/action_dispatch/middleware/closed_error.rb +7 -0
- data/lib/action_dispatch/middleware/cookies.rb +37 -15
- data/lib/action_dispatch/middleware/flash.rb +80 -11
- data/lib/action_dispatch/middleware/params_parser.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
- data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
- data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
- data/lib/action_dispatch/middleware/stack.rb +50 -17
- data/lib/action_dispatch/middleware/static.rb +41 -29
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
- data/lib/action_dispatch/railtie.rb +8 -0
- data/lib/action_dispatch/routing.rb +13 -1
- data/lib/action_dispatch/routing/mapper.rb +345 -227
- data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
- data/lib/action_dispatch/routing/redirection.rb +110 -0
- data/lib/action_dispatch/routing/route.rb +15 -13
- data/lib/action_dispatch/routing/route_set.rb +116 -90
- data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
- data/lib/action_dispatch/routing/url_for.rb +25 -1
- data/lib/action_dispatch/testing/assertions/response.rb +8 -10
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
- data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
- data/lib/action_dispatch/testing/integration.rb +37 -28
- data/lib/action_dispatch/testing/performance_test.rb +1 -3
- data/lib/action_dispatch/testing/test_process.rb +1 -1
- data/lib/action_dispatch/testing/test_request.rb +9 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -111
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +3 -3
- data/lib/action_view.rb +39 -24
- data/lib/action_view/base.rb +61 -86
- data/lib/action_view/buffers.rb +43 -0
- data/lib/action_view/context.rb +21 -24
- data/lib/action_view/flows.rb +79 -0
- data/lib/action_view/helpers.rb +8 -6
- data/lib/action_view/helpers/active_model_helper.rb +0 -23
- data/lib/action_view/helpers/asset_paths.rb +79 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
- data/lib/action_view/helpers/cache_helper.rb +11 -19
- data/lib/action_view/helpers/capture_helper.rb +19 -8
- data/lib/action_view/helpers/controller_helper.rb +21 -0
- data/lib/action_view/helpers/csrf_helper.rb +22 -4
- data/lib/action_view/helpers/date_helper.rb +36 -22
- data/lib/action_view/helpers/form_helper.rb +199 -113
- data/lib/action_view/helpers/form_options_helper.rb +10 -11
- data/lib/action_view/helpers/form_tag_helper.rb +94 -22
- data/lib/action_view/helpers/javascript_helper.rb +24 -107
- data/lib/action_view/helpers/number_helper.rb +36 -33
- data/lib/action_view/helpers/output_safety_helper.rb +38 -0
- data/lib/action_view/helpers/record_tag_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +90 -0
- data/lib/action_view/helpers/sanitize_helper.rb +2 -2
- data/lib/action_view/helpers/sprockets_helper.rb +69 -0
- data/lib/action_view/helpers/tag_helper.rb +34 -12
- data/lib/action_view/helpers/text_helper.rb +30 -145
- data/lib/action_view/helpers/translation_helper.rb +10 -17
- data/lib/action_view/helpers/url_helper.rb +70 -67
- data/lib/action_view/locale/en.yml +1 -1
- data/lib/action_view/lookup_context.rb +36 -14
- data/lib/action_view/{paths.rb → path_set.rb} +9 -8
- data/lib/action_view/railtie.rb +12 -4
- data/lib/action_view/renderer/abstract_renderer.rb +36 -0
- data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
- data/lib/action_view/renderer/renderer.rb +54 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
- data/lib/action_view/renderer/template_renderer.rb +74 -0
- data/lib/action_view/template.rb +91 -54
- data/lib/action_view/template/error.rb +11 -8
- data/lib/action_view/template/handler.rb +9 -1
- data/lib/action_view/template/handlers.rb +9 -9
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb.rb +21 -41
- data/lib/action_view/template/resolver.rb +171 -57
- data/lib/action_view/template/text.rb +0 -4
- data/lib/action_view/test_case.rb +32 -16
- data/lib/action_view/testing/resolvers.rb +16 -10
- data/lib/sprockets/railtie.rb +100 -0
- metadata +162 -140
- checksums.yaml +0 -7
- data/lib/action_controller/deprecated/base.rb +0 -143
- data/lib/action_controller/deprecated/dispatcher.rb +0 -28
- data/lib/action_controller/deprecated/url_writer.rb +0 -14
- data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
- data/lib/action_view/helpers/prototype_helper.rb +0 -851
- data/lib/action_view/helpers/raw_output_helper.rb +0 -18
- data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
- data/lib/action_view/render/layouts.rb +0 -83
- data/lib/action_view/render/rendering.rb +0 -67
- data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -2,6 +2,7 @@ require 'active_support/core_ext/class/attribute'
|
|
2
2
|
require 'active_support/core_ext/object/blank'
|
3
3
|
|
4
4
|
module ActionController
|
5
|
+
# See <tt>Renderers.add</tt>
|
5
6
|
def self.add_renderer(key, &block)
|
6
7
|
Renderers.add(key, &block)
|
7
8
|
end
|
@@ -15,30 +16,12 @@ module ActionController
|
|
15
16
|
end
|
16
17
|
|
17
18
|
module ClassMethods
|
18
|
-
def _write_render_options
|
19
|
-
renderers = _renderers.map do |name, value|
|
20
|
-
<<-RUBY_EVAL
|
21
|
-
if options.key?(:#{name})
|
22
|
-
_process_options(options)
|
23
|
-
return _render_option_#{name}(options.delete(:#{name}), options)
|
24
|
-
end
|
25
|
-
RUBY_EVAL
|
26
|
-
end
|
27
|
-
|
28
|
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
29
|
-
def _handle_render_options(options)
|
30
|
-
#{renderers.join}
|
31
|
-
end
|
32
|
-
RUBY_EVAL
|
33
|
-
end
|
34
|
-
|
35
19
|
def use_renderers(*args)
|
36
20
|
new = _renderers.dup
|
37
21
|
args.each do |key|
|
38
22
|
new[key] = RENDERERS[key]
|
39
23
|
end
|
40
24
|
self._renderers = new.freeze
|
41
|
-
_write_render_options
|
42
25
|
end
|
43
26
|
alias use_renderer use_renderers
|
44
27
|
end
|
@@ -47,26 +30,64 @@ module ActionController
|
|
47
30
|
_handle_render_options(options) || super
|
48
31
|
end
|
49
32
|
|
33
|
+
def _handle_render_options(options)
|
34
|
+
_renderers.each do |name, value|
|
35
|
+
if options.key?(name.to_sym)
|
36
|
+
_process_options(options)
|
37
|
+
return send("_render_option_#{name}", options.delete(name.to_sym), options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Hash of available renderers, mapping a renderer name to its proc.
|
44
|
+
# Default keys are :json, :js, :xml.
|
50
45
|
RENDERERS = {}
|
46
|
+
|
47
|
+
# Adds a new renderer to call within controller actions.
|
48
|
+
# A renderer is invoked by passing its name as an option to
|
49
|
+
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
|
50
|
+
# pass it a name and a block. The block takes two arguments, the first
|
51
|
+
# is the value paired with its key and the second is the remaining
|
52
|
+
# hash of options passed to +render+.
|
53
|
+
#
|
54
|
+
# === Example
|
55
|
+
# Create a csv renderer:
|
56
|
+
#
|
57
|
+
# ActionController::Renderers.add :csv do |obj, options|
|
58
|
+
# filename = options[:filename] || 'data'
|
59
|
+
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
|
60
|
+
# send_data str, :type => Mime::CSV,
|
61
|
+
# :disposition => "attachment; filename=#{filename}.csv"
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Note that we used Mime::CSV for the csv mime type as it comes with Rails.
|
65
|
+
# For a custom renderer, you'll need to register a mime type with
|
66
|
+
# <tt>Mime::Type.register</tt>.
|
67
|
+
#
|
68
|
+
# To use the csv renderer in a controller action:
|
69
|
+
#
|
70
|
+
# def show
|
71
|
+
# @csvable = Csvable.find(params[:id])
|
72
|
+
# respond_to do |format|
|
73
|
+
# format.html
|
74
|
+
# format.csv { render :csv => @csvable, :filename => @csvable.name }
|
75
|
+
# }
|
76
|
+
# end
|
77
|
+
# To use renderers and their mime types in more concise ways, see
|
78
|
+
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
|
79
|
+
# <tt>ActionController::MimeResponds#respond_with</tt>
|
51
80
|
def self.add(key, &block)
|
52
81
|
define_method("_render_option_#{key}", &block)
|
53
82
|
RENDERERS[key] = block
|
54
|
-
All._write_render_options
|
55
83
|
end
|
56
84
|
|
57
85
|
module All
|
58
86
|
extend ActiveSupport::Concern
|
59
87
|
include Renderers
|
60
88
|
|
61
|
-
INCLUDED = []
|
62
89
|
included do
|
63
90
|
self._renderers = RENDERERS
|
64
|
-
_write_render_options
|
65
|
-
INCLUDED << self
|
66
|
-
end
|
67
|
-
|
68
|
-
def self._write_render_options
|
69
|
-
INCLUDED.each(&:_write_render_options)
|
70
91
|
end
|
71
92
|
end
|
72
93
|
|
@@ -74,24 +95,17 @@ module ActionController
|
|
74
95
|
json = json.to_json(options) unless json.kind_of?(String)
|
75
96
|
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
|
76
97
|
self.content_type ||= Mime::JSON
|
77
|
-
|
98
|
+
json
|
78
99
|
end
|
79
100
|
|
80
101
|
add :js do |js, options|
|
81
102
|
self.content_type ||= Mime::JS
|
82
|
-
|
103
|
+
js.respond_to?(:to_js) ? js.to_js(options) : js
|
83
104
|
end
|
84
105
|
|
85
106
|
add :xml do |xml, options|
|
86
107
|
self.content_type ||= Mime::XML
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
add :update do |proc, options|
|
91
|
-
view_context = self.view_context
|
92
|
-
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
|
93
|
-
self.content_type = Mime::JS
|
94
|
-
self.response_body = generator.to_s
|
108
|
+
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
95
109
|
end
|
96
110
|
end
|
97
111
|
end
|
@@ -2,7 +2,6 @@ module ActionController
|
|
2
2
|
module Rendering
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
include ActionController::RackDelegation
|
6
5
|
include AbstractController::Rendering
|
7
6
|
|
8
7
|
# Before processing, set the request formats in current controller formats.
|
@@ -19,38 +18,48 @@ module ActionController
|
|
19
18
|
response_body
|
20
19
|
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
options
|
21
|
+
# Overwrite render_to_string because body can now be set to a rack body.
|
22
|
+
def render_to_string(*)
|
23
|
+
if self.response_body = super
|
24
|
+
string = ""
|
25
|
+
response_body.each { |r| string << r }
|
26
|
+
string
|
29
27
|
end
|
28
|
+
ensure
|
29
|
+
self.response_body = nil
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
private
|
33
|
+
|
34
|
+
# Normalize arguments by catching blocks and setting them on :update.
|
35
|
+
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
36
|
+
options = super
|
37
|
+
options[:update] = blk if block_given?
|
38
|
+
options
|
39
|
+
end
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
# Normalize both text and status options.
|
42
|
+
def _normalize_options(options) #:nodoc:
|
43
|
+
if options.key?(:text) && options[:text].respond_to?(:to_text)
|
44
|
+
options[:text] = options[:text].to_text
|
45
|
+
end
|
40
46
|
|
41
|
-
|
47
|
+
if options[:status]
|
48
|
+
options[:status] = Rack::Utils.status_code(options[:status])
|
42
49
|
end
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
status, content_type, location = options.values_at(:status, :content_type, :location)
|
51
|
+
super
|
52
|
+
end
|
47
53
|
|
48
|
-
|
49
|
-
|
50
|
-
|
54
|
+
# Process controller specific options, as status, content-type and location.
|
55
|
+
def _process_options(options) #:nodoc:
|
56
|
+
status, content_type, location = options.values_at(:status, :content_type, :location)
|
51
57
|
|
52
|
-
|
53
|
-
|
58
|
+
self.status = status if status
|
59
|
+
self.content_type = content_type if content_type
|
60
|
+
self.headers["Location"] = url_for(location) if location
|
54
61
|
|
62
|
+
super
|
63
|
+
end
|
55
64
|
end
|
56
65
|
end
|
@@ -4,45 +4,27 @@ module ActionController #:nodoc:
|
|
4
4
|
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
5
5
|
end
|
6
6
|
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# string
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
7
|
+
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
|
8
|
+
# by including a token in the rendered html for your application. This token is
|
9
|
+
# stored as a random string in the session, to which an attacker does not have
|
10
|
+
# access. When a request reaches your application, \Rails then verifies the received
|
11
|
+
# token with the token in the session. Only HTML and javascript requests are checked,
|
12
|
+
# so this will not protect your XML API (presumably you'll have a different
|
13
|
+
# authentication scheme there anyway). Also, GET requests are not protected as these
|
14
|
+
# should be idempotent.
|
14
15
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
16
|
+
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
|
17
|
+
# which will check the token and raise an ActionController::InvalidAuthenticityToken
|
18
|
+
# if it doesn't match what was expected. A call to this method is generated for new
|
19
|
+
# \Rails applications by default. You can customize the error message by editing
|
20
|
+
# public/422.html.
|
19
21
|
#
|
20
|
-
# The token parameter is named <tt>authenticity_token</tt> by default.
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# <tt>form_authenticity_token</tt>.
|
24
|
-
#
|
25
|
-
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
|
26
|
-
# 1.x, add this to config/environments/test.rb:
|
27
|
-
#
|
28
|
-
# # Disable request forgery protection in test environment
|
29
|
-
# config.action_controller.allow_forgery_protection = false
|
30
|
-
#
|
31
|
-
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
32
|
-
#
|
33
|
-
# Here are some resources:
|
34
|
-
# * http://isc.sans.org/diary.html?storyid=1750
|
35
|
-
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
36
|
-
#
|
37
|
-
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
38
|
-
# There are a few guidelines you should follow:
|
39
|
-
#
|
40
|
-
# * Keep your GET requests safe and idempotent. More reading material:
|
41
|
-
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
42
|
-
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
43
|
-
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
|
44
|
-
# for "Expires: at end of session"
|
22
|
+
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
|
23
|
+
# value of this token must be added to every layout that renders forms by including
|
24
|
+
# <tt>csrf_meta_tags</tt> in the html +head+.
|
45
25
|
#
|
26
|
+
# Learn more about CSRF attacks and securing your application in the
|
27
|
+
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
|
46
28
|
module RequestForgeryProtection
|
47
29
|
extend ActiveSupport::Concern
|
48
30
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_support/json'
|
2
2
|
|
3
3
|
module ActionController #:nodoc:
|
4
|
-
#
|
4
|
+
# Responsible for exposing a resource to different mime requests,
|
5
5
|
# usually depending on the HTTP verb. The responder is triggered when
|
6
6
|
# <code>respond_with</code> is called. The simplest case to study is a GET request:
|
7
7
|
#
|
@@ -24,10 +24,10 @@ module ActionController #:nodoc:
|
|
24
24
|
#
|
25
25
|
# === Builtin HTTP verb semantics
|
26
26
|
#
|
27
|
-
# The default Rails responder holds semantics for each HTTP verb. Depending on the
|
27
|
+
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
|
28
28
|
# content type, verb and the resource status, it will behave differently.
|
29
29
|
#
|
30
|
-
# Using Rails default responder, a POST request for creating an object could
|
30
|
+
# Using \Rails default responder, a POST request for creating an object could
|
31
31
|
# be written as:
|
32
32
|
#
|
33
33
|
# def create
|
@@ -77,8 +77,37 @@ module ActionController #:nodoc:
|
|
77
77
|
#
|
78
78
|
# respond_with(@project, :manager, @task)
|
79
79
|
#
|
80
|
-
#
|
80
|
+
# === Custom options
|
81
81
|
#
|
82
|
+
# <code>respond_with</code> also allow you to pass options that are forwarded
|
83
|
+
# to the underlying render call. Those options are only applied success
|
84
|
+
# scenarios. For instance, you can do the following in the create method above:
|
85
|
+
#
|
86
|
+
# def create
|
87
|
+
# @project = Project.find(params[:project_id])
|
88
|
+
# @task = @project.comments.build(params[:task])
|
89
|
+
# flash[:notice] = 'Task was successfully created.' if @task.save
|
90
|
+
# respond_with(@project, @task, :status => 201)
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# This will return status 201 if the task was saved with success. If not,
|
94
|
+
# it will simply ignore the given options and return status 422 and the
|
95
|
+
# resource errors. To customize the failure scenario, you can pass a
|
96
|
+
# a block to <code>respond_with</code>:
|
97
|
+
#
|
98
|
+
# def create
|
99
|
+
# @project = Project.find(params[:project_id])
|
100
|
+
# @task = @project.comments.build(params[:task])
|
101
|
+
# respond_with(@project, @task, :status => 201) do |format|
|
102
|
+
# if @task.save
|
103
|
+
# flash[:notice] = 'Task was successfully created.'
|
104
|
+
# else
|
105
|
+
# format.html { render "some_special_template" }
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
|
82
111
|
class Responder
|
83
112
|
attr_reader :controller, :request, :format, :resource, :resources, :options
|
84
113
|
|
@@ -115,7 +144,7 @@ module ActionController #:nodoc:
|
|
115
144
|
# Main entry point for responder responsible to dispatch to the proper format.
|
116
145
|
#
|
117
146
|
def respond
|
118
|
-
method =
|
147
|
+
method = "to_#{format}"
|
119
148
|
respond_to?(method) ? send(method) : to_format
|
120
149
|
end
|
121
150
|
|
@@ -133,14 +162,18 @@ module ActionController #:nodoc:
|
|
133
162
|
# responds to :to_format and display it.
|
134
163
|
#
|
135
164
|
def to_format
|
136
|
-
|
165
|
+
if get? || !has_errors?
|
166
|
+
default_render
|
167
|
+
else
|
168
|
+
display_errors
|
169
|
+
end
|
137
170
|
rescue ActionView::MissingTemplate => e
|
138
171
|
api_behavior(e)
|
139
172
|
end
|
140
173
|
|
141
174
|
protected
|
142
175
|
|
143
|
-
# This is the common behavior for
|
176
|
+
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
|
144
177
|
def navigation_behavior(error)
|
145
178
|
if get?
|
146
179
|
raise error
|
@@ -151,14 +184,12 @@ module ActionController #:nodoc:
|
|
151
184
|
end
|
152
185
|
end
|
153
186
|
|
154
|
-
# This is the common behavior for
|
187
|
+
# This is the common behavior for formats associated with APIs, such as :xml and :json.
|
155
188
|
def api_behavior(error)
|
156
189
|
raise error unless resourceful?
|
157
190
|
|
158
191
|
if get?
|
159
192
|
display resource
|
160
|
-
elsif has_errors?
|
161
|
-
display resource.errors, :status => :unprocessable_entity
|
162
193
|
elsif post?
|
163
194
|
display resource, :status => :created, :location => api_location
|
164
195
|
elsif has_empty_resource_definition?
|
@@ -171,7 +202,7 @@ module ActionController #:nodoc:
|
|
171
202
|
# Checks whether the resource responds to the current format or not.
|
172
203
|
#
|
173
204
|
def resourceful?
|
174
|
-
resource.respond_to?(
|
205
|
+
resource.respond_to?("to_#{format}")
|
175
206
|
end
|
176
207
|
|
177
208
|
# Returns the resource location by retrieving it from the options or
|
@@ -187,7 +218,7 @@ module ActionController #:nodoc:
|
|
187
218
|
# controller.
|
188
219
|
#
|
189
220
|
def default_render
|
190
|
-
@default_response.call
|
221
|
+
@default_response.call(options)
|
191
222
|
end
|
192
223
|
|
193
224
|
# Display is just a shortcut to render a resource with the current format.
|
@@ -211,6 +242,10 @@ module ActionController #:nodoc:
|
|
211
242
|
controller.render given_options.merge!(options).merge!(format => resource)
|
212
243
|
end
|
213
244
|
|
245
|
+
def display_errors
|
246
|
+
controller.render format => resource.errors, :status => :unprocessable_entity
|
247
|
+
end
|
248
|
+
|
214
249
|
# Check whether the resource has errors.
|
215
250
|
#
|
216
251
|
def has_errors?
|
@@ -1,157 +1,263 @@
|
|
1
1
|
require 'active_support/core_ext/file/path'
|
2
|
+
require 'rack/chunked'
|
2
3
|
|
3
4
|
module ActionController #:nodoc:
|
4
|
-
#
|
5
|
-
#
|
5
|
+
# Allows views to be streamed back to the client as they are rendered.
|
6
|
+
#
|
7
|
+
# The default way Rails renders views is by first rendering the template
|
8
|
+
# and then the layout. The response is sent to the client after the whole
|
9
|
+
# template is rendered, all queries are made, and the layout is processed.
|
10
|
+
#
|
11
|
+
# Streaming inverts the rendering flow by rendering the layout first and
|
12
|
+
# streaming each part of the layout as they are processed. This allows the
|
13
|
+
# header of the HTML (which is usually in the layout) to be streamed back
|
14
|
+
# to client very quickly, allowing JavaScripts and stylesheets to be loaded
|
15
|
+
# earlier than usual.
|
16
|
+
#
|
17
|
+
# This approach was introduced in Rails 3.1 and is still improving. Several
|
18
|
+
# Rack middlewares may not work and you need to be careful when streaming.
|
19
|
+
# Those points are going to be addressed soon.
|
20
|
+
#
|
21
|
+
# In order to use streaming, you will need to use a Ruby version that
|
22
|
+
# supports fibers (fibers are supported since version 1.9.2 of the main
|
23
|
+
# Ruby implementation).
|
24
|
+
#
|
25
|
+
# == Examples
|
26
|
+
#
|
27
|
+
# Streaming can be added to a controller easily, all you need to do is
|
28
|
+
# call +stream+ in the controller class:
|
29
|
+
#
|
30
|
+
# class PostsController
|
31
|
+
# stream
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# The +stream+ method accepts the same options as +before_filter+ and friends:
|
35
|
+
#
|
36
|
+
# class PostsController
|
37
|
+
# stream :only => :index
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# You can also selectively turn on streaming for specific actions:
|
41
|
+
#
|
42
|
+
# class PostsController
|
43
|
+
# def index
|
44
|
+
# @posts = Post.scoped
|
45
|
+
# render :stream => true
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# == When to use streaming
|
50
|
+
#
|
51
|
+
# Streaming may be considered to be overkill for lightweight actions like
|
52
|
+
# +new+ or +edit+. The real benefit of streaming is on expensive actions
|
53
|
+
# that, for example, do a lot of queries on the database.
|
54
|
+
#
|
55
|
+
# In such actions, you want to delay queries execution as much as you can.
|
56
|
+
# For example, imagine the following +dashboard+ action:
|
57
|
+
#
|
58
|
+
# def dashboard
|
59
|
+
# @posts = Post.all
|
60
|
+
# @pages = Page.all
|
61
|
+
# @articles = Article.all
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Most of the queries here are happening in the controller. In order to benefit
|
65
|
+
# from streaming you would want to rewrite it as:
|
66
|
+
#
|
67
|
+
# def dashboard
|
68
|
+
# # Allow lazy execution of the queries
|
69
|
+
# @posts = Post.scoped
|
70
|
+
# @pages = Page.scoped
|
71
|
+
# @articles = Article.scoped
|
72
|
+
# render :stream => true
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# == Communication between layout and template
|
76
|
+
#
|
77
|
+
# When streaming, rendering happens top-down instead of inside-out.
|
78
|
+
# Rails starts with the layout, and the template is rendered later,
|
79
|
+
# when its +yield+ is reached.
|
80
|
+
#
|
81
|
+
# This means that, if your application currently relies on instance
|
82
|
+
# variables set in the template to be used in the layout, they won't
|
83
|
+
# work once you move to streaming. The proper way to communicate
|
84
|
+
# between layout and template, regardless of whether you use streaming
|
85
|
+
# or not, is by using +content_for+, +provide+ and +yield+.
|
86
|
+
#
|
87
|
+
# Take a simple example where the layout expects the template to tell
|
88
|
+
# which title to use:
|
89
|
+
#
|
90
|
+
# <html>
|
91
|
+
# <head><title><%= yield :title %></title></head>
|
92
|
+
# <body><%= yield %></body>
|
93
|
+
# </html>
|
94
|
+
#
|
95
|
+
# You would use +content_for+ in your template to specify the title:
|
96
|
+
#
|
97
|
+
# <%= content_for :title, "Main" %>
|
98
|
+
# Hello
|
99
|
+
#
|
100
|
+
# And the final result would be:
|
101
|
+
#
|
102
|
+
# <html>
|
103
|
+
# <head><title>Main</title></head>
|
104
|
+
# <body>Hello</body>
|
105
|
+
# </html>
|
106
|
+
#
|
107
|
+
# However, if +content_for+ is called several times, the final result
|
108
|
+
# would have all calls concatenated. For instance, if we have the following
|
109
|
+
# template:
|
110
|
+
#
|
111
|
+
# <%= content_for :title, "Main" %>
|
112
|
+
# Hello
|
113
|
+
# <%= content_for :title, " page" %>
|
114
|
+
#
|
115
|
+
# The final result would be:
|
116
|
+
#
|
117
|
+
# <html>
|
118
|
+
# <head><title>Main page</title></head>
|
119
|
+
# <body>Hello</body>
|
120
|
+
# </html>
|
121
|
+
#
|
122
|
+
# This means that, if you have <code>yield :title</code> in your layout
|
123
|
+
# and you want to use streaming, you would have to render the whole template
|
124
|
+
# (and eventually trigger all queries) before streaming the title and all
|
125
|
+
# assets, which kills the purpose of streaming. For this reason Rails 3.1
|
126
|
+
# introduces a new helper called +provide+ that does the same as +content_for+
|
127
|
+
# but tells the layout to stop searching for other entries and continue rendering.
|
128
|
+
#
|
129
|
+
# For instance, the template above using +provide+ would be:
|
130
|
+
#
|
131
|
+
# <%= provide :title, "Main" %>
|
132
|
+
# Hello
|
133
|
+
# <%= content_for :title, " page" %>
|
134
|
+
#
|
135
|
+
# Giving:
|
136
|
+
#
|
137
|
+
# <html>
|
138
|
+
# <head><title>Main</title></head>
|
139
|
+
# <body>Hello</body>
|
140
|
+
# </html>
|
141
|
+
#
|
142
|
+
# That said, when streaming, you need to properly check your templates
|
143
|
+
# and choose when to use +provide+ and +content_for+.
|
144
|
+
#
|
145
|
+
# == Headers, cookies, session and flash
|
146
|
+
#
|
147
|
+
# When streaming, the HTTP headers are sent to the client right before
|
148
|
+
# it renders the first line. This means that, modifying headers, cookies,
|
149
|
+
# session or flash after the template starts rendering will not propagate
|
150
|
+
# to the client.
|
151
|
+
#
|
152
|
+
# If you try to modify cookies, session or flash, an +ActionDispatch::ClosedError+
|
153
|
+
# will be raised, showing those objects are closed for modification.
|
154
|
+
#
|
155
|
+
# == Middlewares
|
156
|
+
#
|
157
|
+
# Middlewares that need to manipulate the body won't work with streaming.
|
158
|
+
# You should disable those middlewares whenever streaming in development
|
159
|
+
# or production. For instance, +Rack::Bug+ won't work when streaming as it
|
160
|
+
# needs to inject contents in the HTML body.
|
161
|
+
#
|
162
|
+
# Also +Rack::Cache+ won't work with streaming as it does not support
|
163
|
+
# streaming bodies yet. Whenever streaming Cache-Control is automatically
|
164
|
+
# set to "no-cache".
|
165
|
+
#
|
166
|
+
# == Errors
|
167
|
+
#
|
168
|
+
# When it comes to streaming, exceptions get a bit more complicated. This
|
169
|
+
# happens because part of the template was already rendered and streamed to
|
170
|
+
# the client, making it impossible to render a whole exception page.
|
171
|
+
#
|
172
|
+
# Currently, when an exception happens in development or production, Rails
|
173
|
+
# will automatically stream to the client:
|
174
|
+
#
|
175
|
+
# "><script type="text/javascript">window.location = "/500.html"</script></html>
|
176
|
+
#
|
177
|
+
# The first two characters (">) are required in case the exception happens
|
178
|
+
# while rendering attributes for a given tag. You can check the real cause
|
179
|
+
# for the exception in your logger.
|
180
|
+
#
|
181
|
+
# == Web server support
|
182
|
+
#
|
183
|
+
# Not all web servers support streaming out-of-the-box. You need to check
|
184
|
+
# the instructions for each of them.
|
185
|
+
#
|
186
|
+
# ==== Unicorn
|
187
|
+
#
|
188
|
+
# Unicorn supports streaming but it needs to be configured. For this, you
|
189
|
+
# need to create a config file as follow:
|
190
|
+
#
|
191
|
+
# # unicorn.config.rb
|
192
|
+
# listen 3000, :tcp_nopush => false
|
193
|
+
#
|
194
|
+
# And use it on initialization:
|
195
|
+
#
|
196
|
+
# unicorn_rails --config-file unicorn.config.rb
|
197
|
+
#
|
198
|
+
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
|
199
|
+
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
|
200
|
+
#
|
201
|
+
# If you are using Unicorn with Nginx, you may need to tweak Nginx.
|
202
|
+
# Streaming should work out of the box on Rainbows.
|
203
|
+
#
|
204
|
+
# ==== Passenger
|
205
|
+
#
|
206
|
+
# To be described.
|
207
|
+
#
|
6
208
|
module Streaming
|
7
209
|
extend ActiveSupport::Concern
|
8
210
|
|
9
|
-
include
|
211
|
+
include AbstractController::Rendering
|
212
|
+
attr_internal :stream
|
10
213
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile".
|
20
|
-
# Your server can also configure this for you by setting the X-Sendfile-Type header.
|
21
|
-
#
|
22
|
-
# Be careful to sanitize the path parameter if it is coming from a web
|
23
|
-
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
|
24
|
-
# download any file on your server.
|
25
|
-
#
|
26
|
-
# Options:
|
27
|
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
28
|
-
# 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
|
31
|
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
32
|
-
# Valid values are 'inline' and 'attachment' (default).
|
33
|
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
34
|
-
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
|
35
|
-
# the URL, which is necessary for i18n filenames on certain browsers
|
36
|
-
# (setting <tt>:filename</tt> overrides this option).
|
37
|
-
#
|
38
|
-
# The default Content-Type and Content-Disposition headers are
|
39
|
-
# 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
|
41
|
-
# a variety of quirks (especially when downloading over SSL).
|
42
|
-
#
|
43
|
-
# Simple download:
|
44
|
-
#
|
45
|
-
# send_file '/path/to.zip'
|
46
|
-
#
|
47
|
-
# Show a JPEG in the browser:
|
48
|
-
#
|
49
|
-
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
|
50
|
-
#
|
51
|
-
# Show a 404 page in the browser:
|
52
|
-
#
|
53
|
-
# send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
|
54
|
-
#
|
55
|
-
# Read about the other Content-* HTTP headers if you'd like to
|
56
|
-
# provide the user with more information (such as Content-Description) in
|
57
|
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
|
58
|
-
#
|
59
|
-
# Also be aware that the document may be cached by proxies and browsers.
|
60
|
-
# 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
|
63
|
-
# http://www.mnot.net/cache_docs/ for an overview of web caching and
|
64
|
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
65
|
-
# for the Cache-Control header spec.
|
66
|
-
def send_file(path, options = {}) #:doc:
|
67
|
-
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
|
68
|
-
|
69
|
-
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
70
|
-
send_file_headers! options
|
71
|
-
|
72
|
-
if options[:x_sendfile]
|
73
|
-
ActiveSupport::Deprecation.warn(":x_sendfile is no longer needed in send_file", caller)
|
214
|
+
module ClassMethods
|
215
|
+
# Render streaming templates. It accepts :only, :except, :if and :unless as options
|
216
|
+
# to specify when to stream, as in ActionController filters.
|
217
|
+
def stream(options={})
|
218
|
+
if defined?(Fiber)
|
219
|
+
before_filter :_stream_filter, options
|
220
|
+
else
|
221
|
+
raise "You cannot use streaming if Fiber is not available."
|
74
222
|
end
|
75
|
-
|
76
|
-
self.status = options[:status] || 200
|
77
|
-
self.content_type = options[:content_type] if options.key?(:content_type)
|
78
|
-
self.response_body = File.open(path, "rb")
|
79
|
-
end
|
80
|
-
|
81
|
-
# Sends the given binary data to the browser. This method is similar to
|
82
|
-
# <tt>render :text => data</tt>, but also allows you to specify whether
|
83
|
-
# the browser should display the response as a file attachment (i.e. in a
|
84
|
-
# download dialog) or as inline data. You may also set the content type,
|
85
|
-
# the apparent file name, and other things.
|
86
|
-
#
|
87
|
-
# Options:
|
88
|
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
89
|
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
|
90
|
-
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
|
91
|
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
92
|
-
# Valid values are 'inline' and 'attachment' (default).
|
93
|
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
94
|
-
#
|
95
|
-
# Generic data download:
|
96
|
-
#
|
97
|
-
# send_data buffer
|
98
|
-
#
|
99
|
-
# Download a dynamically-generated tarball:
|
100
|
-
#
|
101
|
-
# send_data generate_tgz('dir'), :filename => 'dir.tgz'
|
102
|
-
#
|
103
|
-
# Display an image Active Record in the browser:
|
104
|
-
#
|
105
|
-
# send_data image.data, :type => image.content_type, :disposition => 'inline'
|
106
|
-
#
|
107
|
-
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
108
|
-
#
|
109
|
-
# <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
|
110
|
-
# data to the browser, then use <tt>render :text => proc { ... }</tt>
|
111
|
-
# instead. See ActionController::Base#render for more information.
|
112
|
-
def send_data(data, options = {}) #:doc:
|
113
|
-
send_file_headers! options.dup
|
114
|
-
render options.slice(:status, :content_type).merge(:text => data)
|
115
223
|
end
|
224
|
+
end
|
116
225
|
|
117
|
-
|
118
|
-
def send_file_headers!(options)
|
119
|
-
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
|
120
|
-
[:type, :disposition].each do |arg|
|
121
|
-
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
|
122
|
-
end
|
123
|
-
|
124
|
-
if options.key?(:length)
|
125
|
-
ActiveSupport::Deprecation.warn("You do not need to provide the file's length", caller)
|
126
|
-
end
|
226
|
+
protected
|
127
227
|
|
128
|
-
|
129
|
-
|
228
|
+
# Mark following render calls as streaming.
|
229
|
+
def _stream_filter #:nodoc:
|
230
|
+
self.stream = true
|
231
|
+
end
|
130
232
|
|
131
|
-
|
233
|
+
# Consider the stream option when normalazing options.
|
234
|
+
def _normalize_options(options) #:nodoc:
|
235
|
+
super
|
236
|
+
options[:stream] = self.stream unless options.key?(:stream)
|
237
|
+
end
|
132
238
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
239
|
+
# Set proper cache control and transfer encoding when streaming
|
240
|
+
def _process_options(options) #:nodoc:
|
241
|
+
super
|
242
|
+
if options[:stream]
|
243
|
+
if env["HTTP_VERSION"] == "HTTP/1.0"
|
244
|
+
options.delete(:stream)
|
137
245
|
else
|
138
|
-
|
246
|
+
headers["Cache-Control"] ||= "no-cache"
|
247
|
+
headers["Transfer-Encoding"] = "chunked"
|
248
|
+
headers.delete("Content-Length")
|
139
249
|
end
|
250
|
+
end
|
251
|
+
end
|
140
252
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
)
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
# Fix a problem with IE 6.0 on opening downloaded files:
|
149
|
-
# If Cache-Control: no-cache is set (which Rails does by default),
|
150
|
-
# IE removes the file it just downloaded from its cache immediately
|
151
|
-
# after it displays the "open/save" dialog, which means that if you
|
152
|
-
# hit "open" the file isn't there anymore when the application that
|
153
|
-
# is called for handling the download is run, so let's workaround that
|
154
|
-
response.cache_control[:public] ||= false
|
253
|
+
# Call render_to_body if we are streaming instead of usual +render+.
|
254
|
+
def _render_template(options) #:nodoc:
|
255
|
+
if options.delete(:stream)
|
256
|
+
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
|
257
|
+
else
|
258
|
+
super
|
155
259
|
end
|
260
|
+
end
|
156
261
|
end
|
157
262
|
end
|
263
|
+
|