aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/components.rb
blob: 7cbbe3edd2d03f49cbe668ed264f5af68edcb6c0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
module ActionController #:nodoc:
  # Components allows you to call other actions for their rendered response while executing another action. You can either delegate
  # the entire response rendering or you can mix a partial response in with your other content.
  #
  #   class WeblogController < ActionController::Base
  #     # Performs a method and then lets hello_world output its render
  #     def delegate_action
  #       do_other_stuff_before_hello_world
  #       render_component :controller => "greeter",  :action => "hello_world", :params => { "person" => "david" }
  #     end
  #   end
  #
  #   class GreeterController < ActionController::Base
  #     def hello_world
  #       render_text "#{@params['person']} says, Hello World!"
  #     end
  #   end
  #
  # The same can be done in a view to do a partial rendering:
  # 
  #   Let's see a greeting: 
  #   <%= render_component :controller => "greeter", :action => "hello_world" %>
  #
  # It is also possible to specify the controller as a class constant, bypassing the inflector
  # code to compute the controller class at runtime. Therefore,
  # 
  # <%= render_component :controller => GreeterController, :action => "hello_world" %>
  # 
  # would work as well and be slightly faster.
  module Components
    def self.append_features(base) #:nodoc:
      super
      base.helper do
        def render_component(options) 
          @controller.send(:render_component_as_string, options)
        end
      end
    end

    protected
      # Renders the component specified as the response for the current method
      def render_component(options) #:doc:
        component_logging(options) do
          render_text(component_response(options, true).body, response.headers["Status"])
        end
      end

      # Returns the component response as a string
      def render_component_as_string(options) #:doc:
        component_logging(options) do
          response = component_response(options, false)
          if redirected = response.redirected_to
            render_component_as_string redirected
          else
            response.body
          end
       end
      end
  
    private
       def component_response(options, reuse_response)
         c_class = component_class(options)
         c_request = request_for_component(c_class.controller_name, options)
         c_response = reuse_response ? @response : @response.dup
         c_class.process(c_request, c_response, self)
       end
 
       # determine the controller class for the component request
       def component_class(options)
         if controller = options[:controller]
           if controller.is_a? Class
             controller
           else
             "#{controller.camelize}Controller".constantize
           end
         else
           self.class
         end
       end
 
       # Create a new request object based on the current request.
       # The new request inherits the session from the current request,
       # bypassing any session options set for the component controller's class
       def request_for_component(controller_name, options)
         sub_request = @request.dup
         sub_request.session = @request.session
         sub_request.instance_variable_set(:@parameters,
             (options[:params] || {}).with_indifferent_access.regular_update(
                "controller" => controller_name, "action" => options[:action], "id" => options[:id])
         )
         sub_request
        end

      
      def component_logging(options)
        unless logger then yield else
          logger.info("Start rendering component (#{options.inspect}): ")
          result = yield
          logger.info("\n\nEnd of component rendering")
          result
        end
      end
  end
end