Permalink
Browse files

Deprecated all render_* methods in favor of consolidating all renderi…

…ng behavior in Base#render(options). This enables more natural use of combining options, such as layouts

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1350 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 0367317 commit da0c4c5c9695e2ebe8d98b391d901b598dd293a2 @dhh dhh committed May 22, 2005
View
@@ -1,5 +1,19 @@
*SVN*
+* Deprecated all render_* methods in favor of consolidating all rendering behavior in Base#render(options). This enables more natural use of combining options, such as layouts. Examples:
+
+ +---------------------------------------------------------------+-------------------------------------------------------+
+ | BEFORE | AFTER |
+ +---------------------------------------------------------------+-------------------------------------------------------+
+ | render_with_layout "weblog/show", "200 OK", "layouts/dialog" | render :action => "show", :layout => "dialog" |
+ | render_without_layout "weblog/show" | render :action => "show", :layout => false |
+ | render_action "error", "404 Not Found" | render :action => "error", :status => "404 Not Found" |
+ | render_template "xml.div('stuff')", "200 OK", :rxml | render :inline => "xml.div('stuff')", :type => :rxml |
+ | render_text "hello world!" | render :text => "hello world!" |
+ | render_partial_collection "person", @people, nil, :a => 1 | render :partial => "person", :collection => @people, |
+ | | :locals => { :a => 1 } |
+ +---------------------------------------------------------------+-------------------------------------------------------+
+
* Deprecated redirect_to_path and redirect_to_url in favor of letting redirect_to do the right thing when passed either a path or url.
* Fixed use of an integer as return code for renders, so render_text "hello world", 404 now works #1327
@@ -32,6 +32,7 @@
end
require 'action_controller/base'
+require 'action_controller/deprecated_renders_and_redirects'
require 'action_controller/rescue'
require 'action_controller/benchmarking'
require 'action_controller/filters'
@@ -47,6 +48,7 @@
require 'action_controller/caching'
require 'action_controller/components'
require 'action_controller/verification'
+require 'action_controller/streaming'
require 'action_view'
ActionController::Base.template_class = ActionView::Base
@@ -66,4 +68,5 @@
include ActionController::Caching
include ActionController::Components
include ActionController::Verification
+ include ActionController::Streaming
end
Oops, something went wrong.
@@ -15,12 +15,12 @@ def self.append_features(base)
}
end
- def render_with_benchmark(template_name = default_template_name, status = "200 OK")
+ def render_with_benchmark(options = {}, deprecated_status = nil)
if logger.nil?
- render_without_benchmark(template_name, status)
+ render_without_benchmark(options, deprecated_status)
else
db_runtime = ActiveRecord::Base.connection.reset_runtime if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
- @rendering_runtime = Benchmark::measure{ render_without_benchmark(template_name, status) }.real
+ @rendering_runtime = Benchmark::measure{ render_without_benchmark(options, deprecated_status) }.real
if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
@db_rt_before_render = db_runtime
@@ -3,17 +3,17 @@ module ActionController #:nodoc:
# the cookies being written is what will be sent out will the response. Cookies are read by value (so you won't get the cookie object
# itself back -- just the value it holds). Examples for writing:
#
- # cookies["user_name"] = "david" # => Will set a simple session cookie
- # cookies["login"] = { :value => "XJ-122", :expires => Time.now + 360} # => Will set a cookie that expires in 1 hour
+ # cookies[:user_name] = "david" # => Will set a simple session cookie
+ # cookies[:login] = { :value => "XJ-122", :expires => Time.now + 360} # => Will set a cookie that expires in 1 hour
#
# Examples for reading:
#
- # cookies["user_name"] # => "david"
+ # cookies[:user_name] # => "david"
# cookies.size # => 2
#
# Example for deleting:
#
- # cookies.delete "user_name"
+ # cookies.delete :user_name
#
# All the option symbols for setting cookies are:
#
@@ -24,10 +24,16 @@ module ActionController #:nodoc:
# * <tt>secure</tt> - whether this cookie is a secure cookie or not (default to false).
# Secure cookies are only transmitted to HTTPS servers.
module Cookies
- # Returns the cookie container, which operates as described above.
- def cookies
- CookieJar.new(self)
- end
+ protected
+ # Returns the cookie container, which operates as described above.
+ def cookies
+ CookieJar.new(self)
+ end
+
+ # Deprecated cookie writer method
+ def cookie(*options)
+ @response.headers["cookie"] << CGI::Cookie.new(*options)
+ end
end
class CookieJar < Hash #:nodoc:
@@ -0,0 +1,68 @@
+module ActionController
+ class Base
+ protected
+ # Works like render, but instead of requiring a full template name, you can get by with specifying the action name. So calling
+ # <tt>render_action "show_many"</tt> in WeblogController#display will render "#{template_root}/weblog/show_many.rhtml" or
+ # "#{template_root}/weblog/show_many.rxml".
+ def render_action(action_name, status = nil) #:doc:
+ render :action => action_name, :status => status
+ end
+
+ # Works like render, but disregards the template_root and requires a full path to the template that needs to be rendered. Can be
+ # used like <tt>render_file "/Users/david/Code/Ruby/template"</tt> to render "/Users/david/Code/Ruby/template.rhtml" or
+ # "/Users/david/Code/Ruby/template.rxml".
+ def render_file(template_path, status = nil, use_full_path = false) #:doc:
+ render :file => template_path, :status => status, :use_full_path => use_full_path
+ end
+
+ # Renders the +template+ string, which is useful for rendering short templates you don't want to bother having a file for. So
+ # you'd call <tt>render_template "Hello, <%= @user.name %>"</tt> to greet the current user. Or if you want to render as Builder
+ # template, you could do <tt>render_template "xml.h1 @user.name", nil, "rxml"</tt>.
+ def render_template(template, status = nil, type = "rhtml") #:doc:
+ render :inline => template, :status => status, :type => type
+ end
+
+ # Renders the +text+ string without parsing it through any template engine. Useful for rendering static information as it's
+ # considerably faster than rendering through the template engine.
+ # Use block for response body if provided (useful for deferred rendering or streaming output).
+ def render_text(text = nil, status = nil) #:doc:
+ render(:text => text, :status => status) { yield }
+ end
+
+ # Renders an empty response that can be used when the request is only interested in triggering an effect. Do note that good
+ # HTTP manners mandate that you don't use GET requests to trigger data changes.
+ def render_nothing(status = nil) #:doc:
+ render :nothing => true, :status => status
+ end
+
+ # Renders the partial specified by <tt>partial_path</tt>, which by default is the name of the action itself. Example:
+ #
+ # class WeblogController < ActionController::Base
+ # def show
+ # render_partial # renders "weblog/_show.r(xml|html)"
+ # end
+ # end
+ def render_partial(partial_path = default_template_name, object = nil, local_assigns = {}) #:doc:
+ render :partial => partial_path, :object => object, :locals => local_assigns
+ end
+
+ # Renders a collection of partials using <tt>partial_name</tt> to iterate over the +collection+.
+ def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})#:doc:
+ render :partial => partial_name, :collection => collection, :spacer_template => partial_spacer_template, :locals => local_assigns
+ end
+
+
+ # Deprecated in favor of calling redirect_to directly with the path.
+ def redirect_to_path(path) #:doc:
+ redirect_to(path)
+ end
+
+ # Deprecated in favor of calling redirect_to directly with the url. If the resource has moved permanently, it's possible to pass
+ # true as the second parameter and the browser will get "301 Moved Permanently" instead of "302 Found". This can also be done through
+ # just setting the headers["Status"] to "301 Moved Permanently" before using the redirect_to.
+ def redirect_to_url(url, permanently = false) #:doc:
+ headers["Status"] = "301 Moved Permanently" if permanently
+ redirect_to(url)
+ end
+ end
+end
@@ -6,9 +6,6 @@ def self.append_features(base)
alias_method :render_without_layout, :render
alias_method :render, :render_with_layout
- alias_method :r_without_layout, :r
- alias_method :r, :r_with_layout
-
class << self
alias_method :inherited_without_layout, :inherited
end
@@ -205,7 +202,7 @@ def active_layout(passed_layout = nil)
active_layout.include?("/") ? active_layout : "layouts/#{active_layout}" if active_layout
end
- def render_with_layout(template_name = default_template_name, status = nil, layout = nil) #:nodoc:
+ def xrender_with_layout(template_name = default_template_name, status = nil, layout = nil) #:nodoc:
if layout ||= active_layout and action_has_layout?
add_variables_to_assigns
logger.info("Rendering #{template_name} within #{layout}") unless logger.nil?
@@ -216,23 +213,25 @@ def render_with_layout(template_name = default_template_name, status = nil, layo
end
end
- def r_with_layout(options = {})
- if (layout = active_layout_for_r(options)) && options[:text]
+ def render_with_layout(options = {}, deprecated_status = nil, deprecated_layout = nil)
+ if (layout = active_layout_for_r(options, deprecated_layout)) && options[:text]
add_variables_to_assigns
logger.info("Rendering #{template_name} within #{layout}") unless logger.nil?
- @content_for_layout = r_without_layout(options)
+ @content_for_layout = render_without_layout(options)
add_variables_to_assigns
erase_render_results
- r_without_layout(options.merge({ :text => @template.render_file(layout, true)}))
+ render_without_layout(options.merge({ :text => @template.render_file(layout, true), :status => options[:status] || deprecated_status }))
else
- r_without_layout(options)
+ render_without_layout(options, deprecated_status)
end
end
private
- def active_layout_for_r(options = {})
+ def active_layout_for_r(options = {}, deprecated_layout = nil)
+ return deprecated_layout unless deprecated_layout.nil?
+
case options[:layout]
when FalseClass
nil
@@ -244,7 +243,7 @@ def active_layout_for_r(options = {})
end
def action_has_layout?
- conditions = self.class.layout_conditions
+ conditions = self.class.layout_conditions || {}
case
when conditions[:only]
conditions[:only].include?(action_name)
@@ -0,0 +1,133 @@
+module ActionController #:nodoc:
+ # Methods for sending files and streams to the browser instead of rendering.
+ module Streaming
+ DEFAULT_SEND_FILE_OPTIONS = {
+ :type => 'application/octet-stream'.freeze,
+ :disposition => 'attachment'.freeze,
+ :stream => true,
+ :buffer_size => 4096
+ }.freeze
+
+ protected
+ # Sends the file by streaming it 4096 bytes at a time. This way the
+ # whole file doesn't need to be read into memory at once. This makes
+ # it feasible to send even large files.
+ #
+ # Be careful to sanitize the path parameter if it coming from a web
+ # page. send_file(@params['path']) allows a malicious user to
+ # download any file on your server.
+ #
+ # Options:
+ # * <tt>:filename</tt> - suggests a filename for the browser to use.
+ # Defaults to File.basename(path).
+ # * <tt>:type</tt> - specifies an HTTP content type.
+ # Defaults to 'application/octet-stream'.
+ # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+ # Valid values are 'inline' and 'attachment' (default).
+ # * <tt>:streaming</tt> - whether to send the file to the user agent as it is read (true)
+ # or to read the entire file before sending (false). Defaults to true.
+ # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
+ # Defaults to 4096.
+ #
+ # The default Content-Type and Content-Disposition headers are
+ # set to download arbitrary binary files in as many browsers as
+ # possible. IE versions 4, 5, 5.5, and 6 are all known to have
+ # a variety of quirks (especially when downloading over SSL).
+ #
+ # Simple download:
+ # send_file '/path/to.zip'
+ #
+ # Show a JPEG in browser:
+ # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
+ #
+ # Read about the other Content-* HTTP headers if you'd like to
+ # provide the user with more information (such as Content-Description).
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
+ #
+ # Also be aware that the document may be cached by proxies and browsers.
+ # The Pragma and Cache-Control headers declare how the file may be cached
+ # by intermediaries. They default to require clients to validate with
+ # the server before releasing cached responses. See
+ # http://www.mnot.net/cache_docs/ for an overview of web caching and
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
+ # for the Cache-Control header spec.
+ def send_file(path, options = {}) #:doc:
+ raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
+
+ options[:length] ||= File.size(path)
+ options[:filename] ||= File.basename(path)
+ send_file_headers! options
+
+ @performed_render = false
+
+ if options[:stream]
+ render :text => Proc.new {
+ logger.info "Streaming file #{path}" unless logger.nil?
+ len = options[:buffer_size] || 4096
+ File.open(path, 'rb') do |file|
+ if $stdout.respond_to?(:syswrite)
+ begin
+ while true
+ $stdout.syswrite file.sysread(len)
+ end
+ rescue EOFError
+ end
+ else
+ while buf = file.read(len)
+ $stdout.write buf
+ end
+ end
+ end
+ }
+ else
+ logger.info "Sending file #{path}" unless logger.nil?
+ File.open(path, 'rb') { |file| render :text => file.read }
+ end
+ end
+
+ # Send binary data to the user as a file download. May set content type, apparent file name,
+ # and specify whether to show data inline or download as an attachment.
+ #
+ # Options:
+ # * <tt>:filename</tt> - Suggests a filename for the browser to use.
+ # * <tt>:type</tt> - specifies an HTTP content type.
+ # Defaults to 'application/octet-stream'.
+ # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+ # Valid values are 'inline' and 'attachment' (default).
+ #
+ # Generic data download:
+ # send_data buffer
+ #
+ # Download a dynamically-generated tarball:
+ # send_data generate_tgz('dir'), :filename => 'dir.tgz'
+ #
+ # Display an image Active Record in the browser:
+ # send_data image.data, :type => image.content_type, :disposition => 'inline'
+ #
+ # See +send_file+ for more information on HTTP Content-* headers and caching.
+ def send_data(data, options = {}) #:doc:
+ logger.info "Sending data #{options[:filename]}" unless logger.nil?
+ send_file_headers! options.merge(:length => data.size)
+ @performed_render = false
+ render :text => data
+ end
+
+ private
+ def send_file_headers!(options)
+ options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
+ [:length, :type, :disposition].each do |arg|
+ raise ArgumentError, ":#{arg} option required" if options[arg].nil?
+ end
+
+ disposition = options[:disposition].dup || 'attachment'
+ disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
+
+ @headers.update(
+ 'Content-Length' => options[:length],
+ 'Content-Type' => options[:type].strip, # fixes a problem with extra '\r' with some browsers
+ 'Content-Disposition' => disposition,
+ 'Content-Transfer-Encoding' => 'binary'
+ );
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit da0c4c5

Please sign in to comment.