rendering_controller.rb 5.3 KB
Newer Older
1
require "abstract_controller/logger"
2

3
module AbstractController
4
  module RenderingController
5
    extend ActiveSupport::Concern
J
Joshua Peek 已提交
6

7
    include AbstractController::Logger
8 9

    included do
10
      attr_internal :formats
11

12
      extlib_inheritable_accessor :_view_paths
13

14
      self._view_paths ||= ActionView::PathSet.new
15
    end
16

17 18 19
    # An instance of a view class. The default view class is ActionView::Base
    #
    # The view class must have the following methods:
20 21
    # View.for_controller[controller] Create a new ActionView instance for a 
    #   controller
22
    # View#render_partial[options]
23 24
    #   - responsible for setting options[:_template]
    #   - Returns String with the rendered partial
25
    #   options<Hash>:: see _render_partial in ActionView::Base
26
    # View#render_template[template, layout, options, partial]
27
    #   - Returns String with the rendered template
28 29 30 31 32 33
    #   template<ActionView::Template>:: The template to render
    #   layout<ActionView::Template>:: The layout to render around the template
    #   options<Hash>:: See _render_template_with_layout in ActionView::Base
    #   partial<Boolean>:: Whether or not the template to render is a partial
    #
    # Override this method in a to change the default behavior.
34 35
    def view_context
      @_view_context ||= ActionView::Base.for_controller(self)
36
    end
J
Joshua Peek 已提交
37

38 39
    # Mostly abstracts the fact that calling render twice is a DoubleRenderError.
    # Delegates render_to_body and sticks the result in self.response_body.
40
    def render(*args)
Y
Yehuda Katz 已提交
41
      if response_body
42 43
        raise AbstractController::DoubleRenderError, "OMG"
      end
J
Joshua Peek 已提交
44

45
      self.response_body = render_to_body(*args)
46
    end
J
Joshua Peek 已提交
47

48
    # Raw rendering of a template to a Rack-compatible body.
49 50 51 52
    #
    # ==== Options
    # _partial_object<Object>:: The object that is being rendered. If this
    #   exists, we are in the special case of rendering an object as a partial.
J
Joshua Peek 已提交
53
    #
Y
Yehuda Katz 已提交
54
    # :api: plugin
55
    def render_to_body(options = {})
56
      # TODO: Refactor so we can just use the normal template logic for this
57
      if options.key?(:partial)
58
        view_context.render_partial(options)
59 60 61
      else
        _determine_template(options)
        _render_template(options)
62
      end
63 64
    end

65 66
    # Raw rendering of a template to a string. Just convert the results of
    # render_to_body into a String.
J
Joshua Peek 已提交
67
    #
68 69
    # :api: plugin
    def render_to_string(options = {})
70
      AbstractController::RenderingController.body_to_s(render_to_body(options))
71 72
    end

73 74 75 76 77 78
    # Renders the template from an object.
    #
    # ==== Options
    # _template<ActionView::Template>:: The template to render
    # _layout<ActionView::Template>:: The layout to wrap the template in (optional)
    # _partial<TrueClass, FalseClass>:: Whether or not the template to be rendered is a partial
79
    def _render_template(options)
Y
Yehuda Katz 已提交
80
      view_context.render_template(options)
81
    end
J
Joshua Peek 已提交
82

83 84 85
    # The list of view paths for this controller. See ActionView::ViewPathSet for
    # more details about writing custom view paths.
    def view_paths
J
Joshua Peek 已提交
86 87
      _view_paths
    end
88

89 90 91 92 93 94 95 96 97 98 99 100
    # Return a string representation of a Rack-compatible response body.
    def self.body_to_s(body)
      if body.respond_to?(:to_str)
        body
      else
        strings = []
        body.each { |part| strings << part.to_s }
        body.close if body.respond_to?(:close)
        strings.join
      end
    end

101
  private
102 103 104 105 106 107 108 109 110
    # Take in a set of options and determine the template to render
    #
    # ==== Options
    # _template<ActionView::Template>:: If this is provided, the search is over
    # _template_name<#to_s>:: The name of the template to look up. Otherwise,
    #   use the current action name.
    # _prefix<String>:: The prefix to look inside of. In a file system, this corresponds
    #   to a directory.
    # _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial
111 112 113
    def _determine_template(options)
      name = (options[:_template_name] || action_name).to_s

114
      options[:_template] ||= view_paths.find(
115 116 117 118
        name, { :formats => formats }, options[:_prefix], options[:_partial]
      )
    end

119
    module ClassMethods
120 121 122 123 124 125
      # Append a path to the list of view paths for this controller.
      #
      # ==== Parameters
      # path<String, ViewPath>:: If a String is provided, it gets converted into 
      # the default view path. You may also provide a custom view path 
      # (see ActionView::ViewPathSet for more information)
126 127 128
      def append_view_path(path)
        self.view_paths << path
      end
J
Joshua Peek 已提交
129

130 131 132 133 134 135
      # Prepend a path to the list of view paths for this controller.
      #
      # ==== Parameters
      # path<String, ViewPath>:: If a String is provided, it gets converted into 
      # the default view path. You may also provide a custom view path 
      # (see ActionView::ViewPathSet for more information)
136 137 138 139
      def prepend_view_path(path)
        self.view_paths.unshift(path)
      end

140
      # A list of all of the default view paths for this controller.
Y
Yehuda Katz 已提交
141 142 143
      def view_paths
        self._view_paths
      end
J
Joshua Peek 已提交
144

145 146 147 148 149
      # Set the view paths.
      #
      # ==== Parameters
      # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
      #   otherwise, process the parameter into a ViewPathSet.
Y
Yehuda Katz 已提交
150 151 152 153
      def view_paths=(paths)
        self._view_paths = paths.is_a?(ActionView::PathSet) ?
                            paths : ActionView::Base.process_view_paths(paths)
      end
154 155
    end
  end
156
end