您的位置:首页 > 编程语言

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: