Rails源代码分析(40):ActionController Base的render方法
2008-12-29 13:05
330 查看
1 使用render方法
# Renders the content that will be returned to the browser as the response body. 1) Rendering an action # rendering is the most common form and the type used automatically by Action Controller when nothing else is # specified. By default, actions are rendered within the current layout (if one exists). # # # Renders the template for the action "goal" within the current controller render :action => "goal" # # # Renders the template for the action "short_goal" within the current controller, # # but without the current active layout render :action => "short_goal", :layout => false # # # Renders the template for the action "long_goal" within the current controller, # # but with a custom layout render :action => "long_goal", :layout => "spectacular" 2) Rendering partials # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the # controller action responding to Ajax calls). By default, the current layout is not used. # # # Renders the same partial with a local variable. render :partial => "person", :locals => { :name => "david" } # # # Renders the partial, making @new_person available through # # the local variable 'person' render :partial => "person", :object => @new_person # # # Renders a collection of the same partial by making each element # # of @winners available through the local variable "person" as it # # builds the complete response. render :partial => "person", :collection => @winners # # # Renders the same collection of partials, but also renders the # # person_divider partial between each person partial. render :partial => "person", :collection => @winners, :spacer_template => "person_divider" # # # Renders a collection of partials located in a view subfolder # # outside of our current controller. In this example we will be # # rendering app/views/shared/_note.r(html|xml) Inside the partial # # each element of @new_notes is available as the local var "note". render :partial => "shared/note", :collection => @new_notes # # # Renders the partial with a status code of 500 (internal error). render :partial => "broken", :status => 500 # # Note that the partial filename must also be a valid Ruby variable name, # so e.g. 2005 and register-user are invalid. 3) Automatic etagging # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified # and the response body will be set to an empty string. No etag header will be inserted if it's already set. 4) Rendering a template # rendering works just like action rendering except that it takes a path relative to the template root. # The current layout is automatically applied. # # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb) render :template => "weblog/show" # # # Renders the template with a local variable render :template => "weblog/show", :locals => {:customer => Customer.new} 5) Rendering a file # # File rendering works just like action rendering except that it takes a filesystem path. By default, the path # is assumed to be absolute, and the current layout is not applied. # # # Renders the template located at the absolute filesystem path render :file => "/path/to/some/template.erb" render :file => "c:/path/to/some/template.erb" # # # Renders a template within the current layout, and with a 404 status code render :file => "/path/to/some/template.erb", :layout => true, :status => 404 render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404 # # # Renders a template relative to the template root and chooses the proper file extension render :file => "some/template", :use_full_path => true 6) Rendering text # # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text # rendering is not done within the active layout. # # # Renders the clear text "hello world" with status code 200 render :text => "hello world!" # # # Renders the clear text "Explosion!" with status code 500 render :text => "Explosion!", :status => 500 # # # Renders the clear text "Hi there!" within the current active layout (if one exists) render :text => "Hi there!", :layout => true # # # Renders the clear text "Hi there!" within the layout # # placed in "app/views/layouts/special.r(html|xml)" render :text => "Hi there!", :layout => "special" # # The <tt>:text</tt> option can also accept a Proc object, which can be used to manually control the page generation. This should # generally be avoided, as it violates the separation between code and content, and because almost everything that can be # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates. # # # Renders "Hello from code!" render :text => proc { |response, output| output.write("Hello from code!") } 7) Rendering JSON # JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected # that the response will be parsed (or eval'd) for use as a data structure. # # # Renders '{"name": "David"}' render :json => {:name => "David"}.to_json # # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will # automatically do that for you: # # # Also renders '{"name": "David"}' render :json => {:name => "David"} # # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag), # so the <tt>:callback</tt> option is provided for these cases. # # # Renders 'show({"name": "David"})' render :json => {:name => "David"}.to_json, :callback => 'show' 8) Rendering an inline template # # Rendering of an inline template works as a cross between text and action rendering where the source for the template # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering # and the current layout is not used. # # # Renders "hello, hello, hello, again" render :inline => "<%= 'hello, ' * 3 + 'again' %>" # # # Renders "<p>Good seeing you!</p>" using Builder render :inline => "xml.p { 'Good seeing you!' }", :type => :builder # # # Renders "hello david" render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" } 9) Rendering inline JavaScriptGenerator page updates # # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details), # you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline. # render :update do |page| page.replace_html 'user_list', :partial => 'user', :collection => @users page.visual_effect :highlight, 'user_list' end # 10) Rendering with status and location headers # # All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together: # render :xml => post.to_xml, :status => :created, :location => post_url(post)
2 实现 def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
if options.nil? # 1 默认情况
return render_for_file(default_template_name, nil, true)
elsif !extra_options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
else
if options == :update
options = extra_options.merge({ :update => true })
elsif !options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}"
end
end
if content_type = options[:content_type]
response.content_type = content_type.to_s
end
if location = options[:location]
response.headers["Location"] = url_for(location)
end
if options.has_key?(:text)
render_for_text(options[:text], options[:status]) # render :text=>'...'
else
if file = options[:file]
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
elsif template = options[:template]
# template 文件绝对路径, status, 使用full path, 设置局部变量
render_for_file(template, options[:status], true, options[:locals] || {})
elsif inline = options[:inline]
add_variables_to_assigns
tmpl = ActionView::InlineTemplate.new(@template, options[:inline], options[:locals], options[:type])
# 生成模板
render_for_text(@template.render_template(tmpl), options[:status])
# 根据inline,生成文本输出
elsif action_name = options[:action]
template = default_template_name(action_name.to_s)
# render_with_a_layout 和 render_with_no_layout 在前面的Layout Module里面就看到了
if options[:layout] && !template_exempt_from_layout?(template)
render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
else
render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
end
elsif xml = options[:xml]
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])
elsif partial = options[:partial]
partial = default_template_name if partial == true
add_variables_to_assigns
if collection = options[:collection]
render_for_text(
@template.send!(:render_partial_collection, partial, collection,
options[:spacer_template], options[:locals]), options[:status]
)
else
render_for_text(
@template.send!(:render_partial, partial,
ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]), options[:status]
)
end
elsif options[:update]
add_variables_to_assigns
@template.send! :evaluate_assigns
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
response.content_type = Mime::JS
render_for_text(generator.to_s, options[:status])
elsif options[:nothing]
# Safari doesn't pass the headers of the return if the response is zero length
render_for_text(" ", options[:status])
else
render_for_file(default_template_name, options[:status], true)
end
end
end
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
@performed_render = true
response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
if append_response
response.body ||= ''
response.body << text.to_s
else
response.body = text.is_a?(Proc) ? text : text.to_s
end
end
def render_for_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
add_variables_to_assigns
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
render_for_text(@template.render_file(template_path, use_full_path, locals), status)
end
# Renders the content that will be returned to the browser as the response body. 1) Rendering an action # rendering is the most common form and the type used automatically by Action Controller when nothing else is # specified. By default, actions are rendered within the current layout (if one exists). # # # Renders the template for the action "goal" within the current controller render :action => "goal" # # # Renders the template for the action "short_goal" within the current controller, # # but without the current active layout render :action => "short_goal", :layout => false # # # Renders the template for the action "long_goal" within the current controller, # # but with a custom layout render :action => "long_goal", :layout => "spectacular" 2) Rendering partials # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the # controller action responding to Ajax calls). By default, the current layout is not used. # # # Renders the same partial with a local variable. render :partial => "person", :locals => { :name => "david" } # # # Renders the partial, making @new_person available through # # the local variable 'person' render :partial => "person", :object => @new_person # # # Renders a collection of the same partial by making each element # # of @winners available through the local variable "person" as it # # builds the complete response. render :partial => "person", :collection => @winners # # # Renders the same collection of partials, but also renders the # # person_divider partial between each person partial. render :partial => "person", :collection => @winners, :spacer_template => "person_divider" # # # Renders a collection of partials located in a view subfolder # # outside of our current controller. In this example we will be # # rendering app/views/shared/_note.r(html|xml) Inside the partial # # each element of @new_notes is available as the local var "note". render :partial => "shared/note", :collection => @new_notes # # # Renders the partial with a status code of 500 (internal error). render :partial => "broken", :status => 500 # # Note that the partial filename must also be a valid Ruby variable name, # so e.g. 2005 and register-user are invalid. 3) Automatic etagging # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified # and the response body will be set to an empty string. No etag header will be inserted if it's already set. 4) Rendering a template # rendering works just like action rendering except that it takes a path relative to the template root. # The current layout is automatically applied. # # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb) render :template => "weblog/show" # # # Renders the template with a local variable render :template => "weblog/show", :locals => {:customer => Customer.new} 5) Rendering a file # # File rendering works just like action rendering except that it takes a filesystem path. By default, the path # is assumed to be absolute, and the current layout is not applied. # # # Renders the template located at the absolute filesystem path render :file => "/path/to/some/template.erb" render :file => "c:/path/to/some/template.erb" # # # Renders a template within the current layout, and with a 404 status code render :file => "/path/to/some/template.erb", :layout => true, :status => 404 render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404 # # # Renders a template relative to the template root and chooses the proper file extension render :file => "some/template", :use_full_path => true 6) Rendering text # # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text # rendering is not done within the active layout. # # # Renders the clear text "hello world" with status code 200 render :text => "hello world!" # # # Renders the clear text "Explosion!" with status code 500 render :text => "Explosion!", :status => 500 # # # Renders the clear text "Hi there!" within the current active layout (if one exists) render :text => "Hi there!", :layout => true # # # Renders the clear text "Hi there!" within the layout # # placed in "app/views/layouts/special.r(html|xml)" render :text => "Hi there!", :layout => "special" # # The <tt>:text</tt> option can also accept a Proc object, which can be used to manually control the page generation. This should # generally be avoided, as it violates the separation between code and content, and because almost everything that can be # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates. # # # Renders "Hello from code!" render :text => proc { |response, output| output.write("Hello from code!") } 7) Rendering JSON # JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected # that the response will be parsed (or eval'd) for use as a data structure. # # # Renders '{"name": "David"}' render :json => {:name => "David"}.to_json # # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will # automatically do that for you: # # # Also renders '{"name": "David"}' render :json => {:name => "David"} # # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag), # so the <tt>:callback</tt> option is provided for these cases. # # # Renders 'show({"name": "David"})' render :json => {:name => "David"}.to_json, :callback => 'show' 8) Rendering an inline template # # Rendering of an inline template works as a cross between text and action rendering where the source for the template # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering # and the current layout is not used. # # # Renders "hello, hello, hello, again" render :inline => "<%= 'hello, ' * 3 + 'again' %>" # # # Renders "<p>Good seeing you!</p>" using Builder render :inline => "xml.p { 'Good seeing you!' }", :type => :builder # # # Renders "hello david" render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" } 9) Rendering inline JavaScriptGenerator page updates # # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details), # you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline. # render :update do |page| page.replace_html 'user_list', :partial => 'user', :collection => @users page.visual_effect :highlight, 'user_list' end # 10) Rendering with status and location headers # # All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together: # render :xml => post.to_xml, :status => :created, :location => post_url(post)
2 实现 def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
if options.nil? # 1 默认情况
return render_for_file(default_template_name, nil, true)
elsif !extra_options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
else
if options == :update
options = extra_options.merge({ :update => true })
elsif !options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}"
end
end
if content_type = options[:content_type]
response.content_type = content_type.to_s
end
if location = options[:location]
response.headers["Location"] = url_for(location)
end
if options.has_key?(:text)
render_for_text(options[:text], options[:status]) # render :text=>'...'
else
if file = options[:file]
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
elsif template = options[:template]
# template 文件绝对路径, status, 使用full path, 设置局部变量
render_for_file(template, options[:status], true, options[:locals] || {})
elsif inline = options[:inline]
add_variables_to_assigns
tmpl = ActionView::InlineTemplate.new(@template, options[:inline], options[:locals], options[:type])
# 生成模板
render_for_text(@template.render_template(tmpl), options[:status])
# 根据inline,生成文本输出
elsif action_name = options[:action]
template = default_template_name(action_name.to_s)
# render_with_a_layout 和 render_with_no_layout 在前面的Layout Module里面就看到了
if options[:layout] && !template_exempt_from_layout?(template)
render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
else
render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
end
elsif xml = options[:xml]
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])
elsif partial = options[:partial]
partial = default_template_name if partial == true
add_variables_to_assigns
if collection = options[:collection]
render_for_text(
@template.send!(:render_partial_collection, partial, collection,
options[:spacer_template], options[:locals]), options[:status]
)
else
render_for_text(
@template.send!(:render_partial, partial,
ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]), options[:status]
)
end
elsif options[:update]
add_variables_to_assigns
@template.send! :evaluate_assigns
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
response.content_type = Mime::JS
render_for_text(generator.to_s, options[:status])
elsif options[:nothing]
# Safari doesn't pass the headers of the return if the response is zero length
render_for_text(" ", options[:status])
else
render_for_file(default_template_name, options[:status], true)
end
end
end
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
@performed_render = true
response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
if append_response
response.body ||= ''
response.body << text.to_s
else
response.body = text.is_a?(Proc) ? text : text.to_s
end
end
def render_for_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
add_variables_to_assigns
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
render_for_text(@template.render_file(template_path, use_full_path, locals), status)
end
相关文章推荐
- Rails源代码分析(42):ActionView Base
- ios actionsheet 跟 imagepickercontroller冲突的问题解决方法
- MVC前后台获取Action、Controller、ID名方法 以及 路由规则
- MultiActionController 的方法
- jsp中Action使用session方法实例分析
- Liferay7 BPM门户开发之40: Form表单的Action到Render的数据传递
- Rails源代码分析(46):ActionView::Partials
- There is already 'baseController' bean method spring方法重定义
- Asp.net Mvc Framework可以在Controller中使用的Url.Action方法
- Rails源代码分析(45):ActionView Template (3)
- SpringMVC-MultiActionController默认方法名解析器
- MVC的思想,画图分析LoadingPager :Controller和BaseFragment的抽取
- 部分视图调用方法总结(Action 、 RenderAction 、 Partial 、 RenderPartial)
- iOS UIAlertController UIAlertAction使用方法
- Spring MVC增强型Controller-MultiActionController 使用方法
- 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析] – format丶
- zend_controller_action中initView()与render()的使用
- 后台数据download成excel的方法(controller/action)
- 全局过滤器中排除指定Controller和action方法
- action里不同方法之间不能共享全局变量的原因分析