Skip to content
This repository

AS::Callbacks::Callback refactor #3637

Merged
merged 1 commit into from over 2 years ago

2 participants

Bogdan Gusiev José Valim
Bogdan Gusiev

In order to make Callbacks code always operate on valid peaces of code

Concatenated Callback#start and Callback#end method into #apply
method.

The main concern is around filter. Current implementation of #start #end for around filter looks like this:

def start(....)
  "#{name}(halted) do"
end 
def end(....)
    "  value
  end"
end

This is insanely confusing. Now it looks like:

def apply(...)
  <<-RUBY_EVAL
#{name}(halted) do
  #{code}
  value
end
RUBY_EVAL
end
Bogdan Gusiev AS::Callbacks::Callback refactor
In order to make Callbacks code always operate on valid peaces of code
Concatenated Callback#start and Callback#end method into #apply
method.
3dc80b7
José Valim josevalim merged commit 0e8dda7 into from January 05, 2012
José Valim josevalim closed this January 05, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Nov 15, 2011
Bogdan Gusiev AS::Callbacks::Callback refactor
In order to make Callbacks code always operate on valid peaces of code
Concatenated Callback#start and Callback#end method into #apply
method.
3dc80b7
This page is out of date. Refresh to see the latest.
120  activesupport/lib/active_support/callbacks.rb
@@ -158,19 +158,10 @@ def _one_time_conditions_valid_#{@callback_id}?
158 158
         RUBY_EVAL
159 159
       end
160 160
 
161  
-      # This will supply contents for before and around filters, and no
162  
-      # contents for after filters (for the forward pass).
163  
-      def start(key=nil, object=nil)
164  
-        return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
165  
-
166  
-        # options[0] is the compiled form of supplied conditions
167  
-        # options[1] is the "end" for the conditional
168  
-        #
  161
+      # Wraps code with filter
  162
+      def apply(code, key=nil, object=nil)
169 163
         case @kind
170 164
         when :before
171  
-          # if condition    # before_save :filter_name, :if => :condition
172  
-          #   filter_name
173  
-          # end
174 165
           <<-RUBY_EVAL
175 166
             if !halted && #{@compiled_options}
176 167
               # This double assignment is to prevent warnings in 1.9.3.  I would
@@ -180,62 +171,64 @@ def start(key=nil, object=nil)
180 171
               result = result = #{@filter}
181 172
               halted = (#{chain.config[:terminator]})
182 173
             end
  174
+            #{code}
183 175
           RUBY_EVAL
184  
-        when :around
185  
-          # Compile around filters with conditions into proxy methods
186  
-          # that contain the conditions.
187  
-          #
188  
-          # For `around_save :filter_name, :if => :condition':
189  
-          #
190  
-          # def _conditional_callback_save_17
191  
-          #   if condition
192  
-          #     filter_name do
193  
-          #       yield self
194  
-          #     end
195  
-          #   else
196  
-          #     yield self
197  
-          #   end
198  
-          # end
199  
-          #
200  
-          name = "_conditional_callback_#{@kind}_#{next_id}"
201  
-          @klass.class_eval <<-RUBY_EVAL,  __FILE__, __LINE__ + 1
202  
-             def #{name}(halted)
203  
-              if #{@compiled_options} && !halted
204  
-                #{@filter} do
205  
-                  yield self
206  
-                end
207  
-              else
208  
-                yield self
209  
-              end
210  
-            end
211  
-          RUBY_EVAL
212  
-          "#{name}(halted) do"
213  
-        end
214  
-      end
215  
-
216  
-      # This will supply contents for around and after filters, but not
217  
-      # before filters (for the backward pass).
218  
-      def end(key=nil, object=nil)
219  
-        return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
220  
-
221  
-        case @kind
222 176
         when :after
223  
-          # after_save :filter_name, :if => :condition
224 177
           <<-RUBY_EVAL
  178
+          #{code}
225 179
           if #{@compiled_options}
226 180
             #{@filter}
227 181
           end
228 182
           RUBY_EVAL
229 183
         when :around
  184
+          name = define_conditional_callback
230 185
           <<-RUBY_EVAL
  186
+          #{name}(halted) do
  187
+            #{code}
231 188
             value
232 189
           end
233 190
           RUBY_EVAL
234 191
         end
235 192
       end
236 193
 
  194
+
  195
+      def one_time_conditions_valid?(object)
  196
+        object.send("_one_time_conditions_valid_#{@callback_id}?")
  197
+      end
  198
+
237 199
       private
238 200
 
  201
+      # Compile around filters with conditions into proxy methods
  202
+      # that contain the conditions.
  203
+      #
  204
+      # For `around_save :filter_name, :if => :condition':
  205
+      #
  206
+      # def _conditional_callback_save_17
  207
+      #   if condition
  208
+      #     filter_name do
  209
+      #       yield self
  210
+      #     end
  211
+      #   else
  212
+      #     yield self
  213
+      #   end
  214
+      # end
  215
+      #
  216
+      def define_conditional_callback
  217
+        name = "_conditional_callback_#{@kind}_#{next_id}"
  218
+        @klass.class_eval <<-RUBY_EVAL,  __FILE__, __LINE__ + 1
  219
+          def #{name}(halted)
  220
+           if #{@compiled_options} && !halted
  221
+             #{@filter} do
  222
+               yield self
  223
+             end
  224
+           else
  225
+             yield self
  226
+           end
  227
+         end
  228
+        RUBY_EVAL
  229
+        name
  230
+      end
  231
+
239 232
       # Options support the same options as filters themselves (and support
240 233
       # symbols, string, procs, and objects), so compile a conditional
241 234
       # expression based on the options
@@ -338,10 +331,20 @@ def compile(key=nil, object=nil)
338 331
         method << "value = nil"
339 332
         method << "halted = false"
340 333
 
341  
-        each do |callback|
342  
-          method << callback.start(key, object)
  334
+        callbacks = yielding
  335
+        applicable_callbacks_for(key, object).reverse_each do |callback|
  336
+          callbacks = callback.apply(callbacks, key, object)
343 337
         end
  338
+        method << callbacks
  339
+
  340
+        method << "raise rescued_error if rescued_error" if config[:rescuable]
  341
+        method << "halted ? false : (block_given? ? value : true)"
  342
+        method.flatten.compact.join("\n")
  343
+      end
344 344
 
  345
+      # Returns part of method that evaluates the callback block
  346
+      def yielding
  347
+        method = []
345 348
         if config[:rescuable]
346 349
           method << "rescued_error = nil"
347 350
           method << "begin"
@@ -354,14 +357,15 @@ def compile(key=nil, object=nil)
354 357
           method << "rescued_error = e"
355 358
           method << "end"
356 359
         end
  360
+        method.join("\n")
  361
+      end
357 362
 
358  
-        reverse_each do |callback|
359  
-          method << callback.end(key, object)
  363
+      # Selects callbacks that have valid <tt>:per_key</tt> condition
  364
+      def applicable_callbacks_for(key, object)
  365
+        return self unless key
  366
+        select do |callback|
  367
+          callback.one_time_conditions_valid?(object)
360 368
         end
361  
-
362  
-        method << "raise rescued_error if rescued_error" if config[:rescuable]
363  
-        method << "halted ? false : (block_given? ? value : true)"
364  
-        method.compact.join("\n")
365 369
       end
366 370
     end
367 371
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.