public
Description: response for lets you decorate your actions respond_to blocks
Homepage: http://blog.ardes.com/response_for
Clone URL: git://github.com/ianwhite/response_for.git
Click here to lend your support to: response_for and make a donation at www.pledgie.com !
API change and rewrite.  Vastly simplified and consistent internals.  
response_for can now be summed up as this 'adds a default respond_to to your 
actions which dont render or redirect'
ianwhite (author)
Sat Sep 13 19:48:13 -0700 2008
commit  3b1ae5e4eecda3b200177a804eeae3f9d5ba10ab
tree    a5f99c0f75ef7d51c7a63b0c7633fa1c50e3bb4f
parent  64e4e9efdb54187596c34d2b877544faa292ea0a
...
 
1
2
3
...
1
2
3
4
0
@@ -1,3 +1,4 @@
0
+.DS_Store
0
 doc/*
0
 garlic
0
 garlic.rb
...
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
0
@@ -1,50 +1,149 @@
0
 
0
-BackToFooController
0
-- get :foo, :format => 'xml' should not render foo
0
-
0
-FooBailOutController
0
-- get :foo, :bail_out => true should redirect
0
-
0
-FooController
0
-- get :foo should render text/html: foo
0
-- get :foo should assign @foo
0
-- get :foo, :format => 'html' should render foo
0
-- get :foo, :format => 'xml' should not render foo
0
-
0
-FooAController
0
-- get :foo should call a
0
-- get :baz should call bazza (inside the response_for block)
0
-
0
-FooBController
0
-- get :foo should call b
0
-
0
-InlineXmlFooController
0
-- get :foo should assign @foo
0
-- get :foo, :format => 'html' should render 'foo'
0
-- get :foo, :format => 'xml' should call xml_call with 'foo
0
-- get :foo, :format => 'xml' should have response.body of 'XML'
0
-
0
-XmlFooController
0
-- get :foo should render foo
0
-- get :foo should assign @foo
0
-- get :foo, :format => 'html' should render foo
0
-- get :foo, :format => 'xml' should render foo
0
-- get :bar, :format => 'xml' should render bar
0
-
0
-XmlOnlyFooController
0
-- get :foo should render xml: foo
0
-- get :bar should render xml: bar
0
-- get :foo should assign @foo
0
-- get :foo, :format => 'html' should not render foo
0
-- get :foo, :format => 'xml' should render foo
0
-
0
-class method #action_responses
0
-- @child.action_responses[:action] should be copy of parent's action response for :action
0
-- @grandchild.action_responses[:action] should be copy of @child.action_responses[:action]
0
-- @child.action_responses[:action] not be same object as parent's action response for :action
0
-- @grandchild.action_responses[:action] not be same object as @child.action_responses[:action]
0
-- adding to @grandchild.action_responses[:action] should not change parents
0
-
0
-Finished in 0.328216 seconds
0
-
0
-28 examples, 0 failures
0
+DefaultRailsBehaviourSpec::TheController GET :two_respond_tos
0
+- should recieve first and second in order
0
+
0
+DefaultRailsBehaviourSpec::TheController GET :two_responses
0
+- should only receive first and NOT second
0
+
0
+action_responses
0
+- SuperController.action_responses should not == SubController.action_responses
0
+- SuperController should have one action_response for 'a_response'
0
+- SubController should have two action_responses for 'a_response', and one each for 'an_action', and 'performing_action'
0
+
0
+InheritedControllerSpec::SuperController GET :an_action
0
+- should execute action
0
+- should render :an_action
0
+
0
+InheritedControllerSpec::SuperController GET :a_response
0
+- should execute inside the super response block
0
+- should NOT execute inside the sub response block
0
+
0
+InheritedControllerSpec::SuperController GET :performing_action
0
+- should redirect
0
+
0
+InheritedControllerSpec::SubController GET :an_action (decorated with redirecting response_for)
0
+- should execute action
0
+- should redirect
0
+
0
+InheritedControllerSpec::SubController GET :a_response (decorated with a new response)
0
+- should NOT execute the super response
0
+- should execute the sub response
0
+
0
+InheritedControllerSpec::SubController GET :performing_action
0
+- should NOT execute the sub response
0
+- should redirect as per the super def
0
+
0
+NoResponseIfPerformedSpec::TheController when before_filter redirects, GET :an_action
0
+- should redirect to 'http://redirected.from.before_filter'
0
+- should not execute inside response_for
0
+
0
+NoResponseIfPerformedSpec::TheController when before_filter doesn't redirect, GET :an_action
0
+- should execute inside response for
0
+- should render :an_action
0
+
0
+Picking template PickTemplateSpec::TemplateOnlyController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should IGNORE and render an_action.html
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::RespondToTypesController
0
+- GET :an_action, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::RespondToTypesController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should render an_action.xml
0
+- text/javascript, should render an_action.js
0
+- application/atom+xml, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::RespondToTypesController GET :an_action, :format =>
0
+- :html, should render an_action.html
0
+- :js, should render an_action.js
0
+- :xml, should render an_action.xml
0
+- :atom, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::RespondToBlockController
0
+- GET :an_action, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::RespondToBlockController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should render an_action.xml
0
+- text/javascript, should render an_action.js
0
+- application/atom+xml, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::RespondToBlockController GET :an_action, :format =>
0
+- :html, should render an_action.html
0
+- :js, should render an_action.js
0
+- :xml, should render an_action.xml
0
+- :atom, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForTypesController
0
+- GET :an_action, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForTypesController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should render an_action.xml
0
+- text/javascript, should render an_action.js
0
+- application/atom+xml, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForTypesController GET :an_action, :format =>
0
+- :html, should render an_action.html
0
+- :js, should render an_action.js
0
+- :xml, should render an_action.xml
0
+- :atom, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForBlockController
0
+- GET :an_action, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForBlockController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should render an_action.xml
0
+- text/javascript, should render an_action.js
0
+- application/atom+xml, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForBlockController GET :an_action, :format =>
0
+- :html, should render an_action.html
0
+- :js, should render an_action.js
0
+- :xml, should render an_action.xml
0
+- :atom, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForMixOfBlockAndTypesController
0
+- GET :an_action, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForMixOfBlockAndTypesController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should render an_action.xml
0
+- text/javascript, should render an_action.js
0
+- application/atom+xml, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::ResponseForMixOfBlockAndTypesController GET :an_action, :format =>
0
+- :html, should render an_action.html
0
+- :js, should render an_action.js
0
+- :xml, should render an_action.xml
0
+- :atom, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::InheritedController
0
+- GET :an_action, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::InheritedController GET :an_action, HTTP_ACCEPT =
0
+- text/html, should render an_action.html
0
+- application/xml, should render an_action.xml
0
+- text/javascript, should render an_action.js
0
+- application/atom+xml, should render an_action.atom
0
+
0
+Picking template [:atom, :xml, :html, :js] PickTemplateSpec::InheritedController GET :an_action, :format =>
0
+- :html, should render an_action.html
0
+- :js, should render an_action.js
0
+- :xml, should render an_action.xml
0
+- :atom, should render an_action.atom
0
+
0
+RemoveResponseForSpec::TheController
0
+- should have action_responses for :foo and :bar
0
+
0
+RemoveResponseForSpec::TheController.remove_response_for :bar
0
+- should hanve action_responses for :foo
0
+
0
+RemoveResponseForSpec::TheController.remove_response_for
0
+- should have empty action_responses
0
+
0
+Finished in 0.451715 seconds
0
+
0
+79 examples, 0 failures
...
3
4
5
6
7
8
9
 
10
11
12
13
14
15
16
17
 
 
18
 
 
 
 
 
 
 
19
20
21
...
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
105
106
107
 
 
108
109
110
 
111
112
113
114
115
 
 
 
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
 
 
 
 
 
 
 
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
 
 
 
170
171
172
173
...
3
4
5
 
 
 
 
6
7
8
9
10
 
 
 
 
11
12
13
14
15
16
17
18
19
20
21
22
23
...
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
105
106
107
108
 
 
 
109
110
111
112
 
113
114
115
116
117
118
 
 
 
 
119
120
121
 
 
122
123
124
 
 
 
125
126
127
128
129
 
 
 
 
 
 
 
 
 
 
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
132
133
134
135
136
137
138
139
 
 
140
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
143
144
145
146
147
148
0
@@ -3,19 +3,21 @@ module Ardes #:nodoc:
0
     def self.included(base)
0
       base.class_eval do
0
         extend ClassMethods
0
-        alias_method_chain :run_before_filters, :response_for
0
-        alias_method_chain :respond_to, :response_for
0
-        alias_method_chain :render, :response_for
0
-        alias_method_chain :erase_render_results, :response_for
0
+        alias_method_chain :default_render, :response_for
0
       end
0
     end
0
     
0
     module ClassMethods
0
-      # response_for allows you to decorate your actions with small respond_to
0
-      # chunks.
0
-      # 
0
-      # One use for this is with subclassed controllers, so you don't have to rewrite the entire action.
0
+      # response_for allows you to specify a default response for your actions that don't specify a
0
+      # respond_to block.
0
       # 
0
+      # Using response_for, you may keep the response logic out of the action, so that it can be overriden easily
0
+      # without having to rewrite the entire action.  This is very useful when subclassing controllers.
0
+      #
0
+      # == Usage
0
+      #
0
+      #   response_for :action1 [, :action2], [,:types => [:mime, :type, :list]] [ do |format| ... end] # or
0
+      #   response_for :action1 [, :action2], [,:type => :mime_type] [ do |format| ... end]
0
       #
0
       # === Example
0
       #
0
@@ -39,134 +41,107 @@ module Ardes #:nodoc:
0
       #     response_for :index, :show, :types => [:html, :xml, :js]
0
       #   end
0
       #
0
-      # === Important
0
-      # If you want to make sure that no repsonse_for can override your repsond_to
0
-      # block, then use respond_to_without_response_for
0
-      #
0
-      # An example of this would be when you want to redirect on html in a 
0
-      # before_filterl, and you definitely don't want response_for to override that
0
+      # === respond_to takes precedence
0
       # 
0
-      # === Usage
0
+      # If you write your action with a respond_to block, response_for will never be invoked for that action.
0
+      # If you want to write a controller so that its subclasses can use response_for then do this:
0
       #
0
-      #   response_for :action1 [, :action2], [,:types => [:mime, :type, :list]] [,:replace => <boolean>] [ do |format| ... end]
0
+      #   # before
0
+      #   def show
0
+      #     @thing = Thing.find(params[:id])
0
+      #     respond_to do |format|
0
+      #       format.html { }
0
+      #       format.xml  { render :xml => @thing }
0
+      #     end
0
+      #   end
0
+      # 
0
+      #   # after
0
+      #   def show
0
+      #     @thing = Thing.find(params[:id])
0
+      #   end
0
       #
0
-      # For example:
0
+      #   response_for :show do |format|
0
+      #     format.html { }
0
+      #     format.xml  { render :xml => @thing }
0
+      #   end
0
+      # 
0
+      # === Other examples 
0
       #
0
       #   response_for :index, :types => [:fbml]    # index will respond to fbml and try to render, say, index.fbml.builder
0
       #
0
       #   response_for :update do |format|          # this example is for a resources_controller controller
0
-      #     if resource.valid?
0
+      #     if !(resource.new_record? || resource.changed?) # => resource.saved?
0
       #       format.js { render(:update) {|page| page.replace(dom_id(resource), :partial => resource}}
0
       #     else
0
       #       format.js { render(:update) {|page| page.visual_effect :shake, dom_id(resource) }}
0
       #     end
0
       #   end
0
       #
0
-      #   response_for :index, :replace => true do |format|   # will ignore index's current respond_to block, and use the following
0
-      #     format.xml
0
-      #     format.js
0
-      #   end
0
-      #
0
       # === Notes
0
       #
0
-      # * You don't need to have a respond_to block in the action for response_for to work
0
-      # * You can stack up multiple response_for calls, the most recent has precedence
0
+      # * If the before_filters or action renders or redirects, then response_for will not be invoked
0
+      # * you can stack up multiple response_for calls, the most recent has precedence
0
       # * the specifed block is executed within the controller instance, so you can use controller
0
-      #   instance methods are instance variables (i.e. you can make it look just like a regular
0
+      #   instance methods and instance variables (i.e. you can make it look just like a regular
0
       #   respond_to block)
0
       # * you can add a response_for an action that is just a public template (where there is no
0
       #   actual action method defined)
0
       # * you can combine the :types option with a block, the block has precedence if you specify the
0
       #   same mime type in both.
0
       def response_for(*actions, &block)
0
-        options = actions.extract_options!
0
-        options.assert_valid_keys(:replace, :types)
0
+        (options = actions.extract_options!).assert_valid_keys(:types)
0
         
0
-        types = (options[:types] && proc{|r| options[:types].each {|t| r.send(t)}}) || nil
0
+        types_block = if options[:types]
0
+          proc {|responder| Array(options[:types]).each {|type| responder.send type}}
0
+        end
0
         
0
+        # store responses against action names
0
         actions.collect(&:to_s).each do |action|
0
-          if options[:replace]
0
-            action_responses[action] = []
0
-            respond_to_replaced[action] = true
0
-          else
0
-            action_responses[action] ||= []
0
-          end
0
-          action_responses[action] << types if types
0
-          action_responses[action] << block if block_given?
0
+          action_responses[action] ||= []
0
+          action_responses[action].unshift types_block if types_block
0
+          action_responses[action].unshift block if block
0
+          
0
+          # if there's no action yet defined, create a stub - this is so that you
0
+          # can provide responses for actions which are simply templates, or simply define responses
0
+          class_eval "def #{action}; end" unless instance_methods.include?(action)
0
         end
0
       end
0
-      
0
-      # Removes any response_for for the supplied action names.
0
-      # This will not remove any respond_to block that is defined in the action itself
0
+    
0
+      # remove any response for the specified actions.  If no arguments are given, the
0
+      # entire all repsonses for all actions are removed
0
       def remove_response_for(*actions)
0
-        actions.collect(&:to_s).each {|action| respond_to_replaced[action] = action_responses[action] = nil}
0
+        if actions.empty?
0
+          instance_variable_set('@action_responses', nil)
0
+        else
0
+          actions.each {|action| action_responses.delete(action.to_s)}
0
+        end
0
       end
0
-    
0
-    protected
0
-      # return action_responses Hash. On initialize, return a hash whose contents are duplicates
0
-      # of the superclass's action_responses.
0
+      
0
+      # return action_responses Hash. On initialize, set and return hash whose values are copies of superclass action_responses, if any
0
       def action_responses
0
-        instance_variable_get('@action_responses') or
0
-          instance_variable_set('@action_responses', (superclass.action_responses.inject({}) {|m,(k,v)| m.merge(k => v.dup)} rescue {}))
0
+        instance_variable_get('@action_responses') || instance_variable_set('@action_responses', copy_of_each_of_superclass_action_responses)
0
       end
0
       
0
-      # hash of actions where the respond_to blcok has been replaced
0
-      def respond_to_replaced
0
-        read_inheritable_attribute(:respond_to_replaced) || write_inheritable_attribute(:respond_to_replaced, {})
0
+    private
0
+      def copy_of_each_of_superclass_action_responses
0
+        (superclass.action_responses rescue {}).inject({}){|m,(k,v)| m.merge(k => v.dup)}
0
       end
0
     end
0
-  
0
-  protected
0
-    # we only want response_for to trigger once we've got to the performing action stage
0
-    # otherwise respond_to in before filters will act unpredictably
0
-    def run_before_filters_with_response_for(*args)
0
-      @running_before_filters = true
0
-      run_before_filters_without_response_for(*args)
0
-    ensure
0
-      @running_before_filters = nil
0
-    end
0
     
0
-    # if you want to ignore any response_for blocks, and gain exclusive respond_to
0
-    # control, pass :exclusive => true
0
-    #
0
-    #   respond_to :exclusive => true do |format|
0
-    #
0
-    # But, also see ClassMethods#remove_response_for
0
-    def respond_to_with_response_for(*types, &block)
0
-      options = types.extract_options!
0
-      return respond_to_without_response_for(*types, &block) if options[:exclusive] || @running_before_filters
0
-      
0
-      respond_to_without_response_for do |responder|
0
-        if action_blocks = self.class.send(:action_responses)[action_name]
0
-          action_blocks.reverse.each {|b| instance_exec(responder, &b)}
0
-        end
0
-        unless self.class.send(:respond_to_replaced)[action_name]
0
-          types.each {|type| responder.send(type)}
0
-          block.call(responder) if block
0
+  protected
0
+    def respond_to_action_responses
0
+      if (responses = self.class.action_responses[action_name]) && responses.any?
0
+        respond_to do |responder|
0
+          responses.each do |response|
0
+            instance_exec(responder, &response)
0
+          end
0
         end
0
       end
0
-    ensure
0
-      @performed_respond_to = true
0
     end
0
     
0
-    # removes the @performed_respond_to along with the standard erase_render_results call
0
-    def erase_render_results_with_response_for
0
-      @performed_respond_to = false
0
-      erase_render_results_without_response_for
0
-    end
0
-    
0
-    # Adds a hook for when render is called without arguments or a block.
0
-    # Prior to rendering, call respond_to if there is a response_for, and 
0
-    # if respond_to has not yet been performed.
0
-    #
0
-    # This allows actions without an explicit respond_to block to be decorated
0
-    # with response_for
0
-    def render_with_response_for(*args, &block)
0
-      if !instance_variable_get('@performed_respond_to') && self.class.send(:action_responses)[action_name] && !block_given? && args.reject{|a| a.nil? || a.empty?}.empty?
0
-        respond_to
0
-        return if performed?
0
-      end
0
-      render_without_response_for(*args, &block)
0
+    def default_render_with_response_for
0
+      respond_to_action_responses
0
+      default_render_without_response_for unless performed?
0
     end
0
   end
0
 end
0
\ No newline at end of file

Comments