callbacks.rb 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. module AbstractController
  2. module Callbacks
  3. extend ActiveSupport::Concern
  4. # Uses ActiveSupport::Callbacks as the base functionality. For
  5. # more details on the whole callback system, read the documentation
  6. # for ActiveSupport::Callbacks.
  7. include ActiveSupport::Callbacks
  8. included do
  9. define_callbacks :process_action, :terminator => "response_body"
  10. end
  11. # Override AbstractController::Base's process_action to run the
  12. # process_action callbacks around the normal behavior.
  13. def process_action(*args)
  14. run_callbacks(:process_action, action_name) do
  15. super
  16. end
  17. end
  18. module ClassMethods
  19. # If :only or :except are used, convert the options into the
  20. # primitive form (:per_key) used by ActiveSupport::Callbacks.
  21. # The basic idea is that :only => :index gets converted to
  22. # :if => proc {|c| c.action_name == "index" }, but that the
  23. # proc is only evaluated once per action for the lifetime of
  24. # a Rails process.
  25. #
  26. # ==== Options
  27. # * <tt>only</tt> - The callback should be run only for this action
  28. # * <tt>except</tt> - The callback should be run for all actions except this action
  29. def _normalize_callback_options(options)
  30. if only = options[:only]
  31. only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
  32. options[:per_key] = {:if => only}
  33. end
  34. if except = options[:except]
  35. except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
  36. options[:per_key] = {:unless => except}
  37. end
  38. end
  39. # Skip before, after, and around filters matching any of the names
  40. #
  41. # ==== Parameters
  42. # * <tt>names</tt> - A list of valid names that could be used for
  43. # callbacks. Note that skipping uses Ruby equality, so it's
  44. # impossible to skip a callback defined using an anonymous proc
  45. # using #skip_filter
  46. def skip_filter(*names, &blk)
  47. skip_before_filter(*names)
  48. skip_after_filter(*names)
  49. skip_around_filter(*names)
  50. end
  51. # Take callback names and an optional callback proc, normalize them,
  52. # then call the block with each callback. This allows us to abstract
  53. # the normalization across several methods that use it.
  54. #
  55. # ==== Parameters
  56. # * <tt>callbacks</tt> - An array of callbacks, with an optional
  57. # options hash as the last parameter.
  58. # * <tt>block</tt> - A proc that should be added to the callbacks.
  59. #
  60. # ==== Block Parameters
  61. # * <tt>name</tt> - The callback to be added
  62. # * <tt>options</tt> - A hash of options to be used when adding the callback
  63. def _insert_callbacks(callbacks, block)
  64. options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
  65. _normalize_callback_options(options)
  66. callbacks.push(block) if block
  67. callbacks.each do |callback|
  68. yield callback, options
  69. end
  70. end
  71. ##
  72. # :method: before_filter
  73. #
  74. # :call-seq: before_filter(names, block)
  75. #
  76. # Append a before filter. See _insert_callbacks for parameter details.
  77. ##
  78. # :method: prepend_before_filter
  79. #
  80. # :call-seq: prepend_before_filter(names, block)
  81. #
  82. # Prepend a before filter. See _insert_callbacks for parameter details.
  83. ##
  84. # :method: skip_before_filter
  85. #
  86. # :call-seq: skip_before_filter(names, block)
  87. #
  88. # Skip a before filter. See _insert_callbacks for parameter details.
  89. ##
  90. # :method: append_before_filter
  91. #
  92. # :call-seq: append_before_filter(names, block)
  93. #
  94. # Append a before filter. See _insert_callbacks for parameter details.
  95. ##
  96. # :method: after_filter
  97. #
  98. # :call-seq: after_filter(names, block)
  99. #
  100. # Append an after filter. See _insert_callbacks for parameter details.
  101. ##
  102. # :method: prepend_after_filter
  103. #
  104. # :call-seq: prepend_after_filter(names, block)
  105. #
  106. # Prepend an after filter. See _insert_callbacks for parameter details.
  107. ##
  108. # :method: skip_after_filter
  109. #
  110. # :call-seq: skip_after_filter(names, block)
  111. #
  112. # Skip an after filter. See _insert_callbacks for parameter details.
  113. ##
  114. # :method: append_after_filter
  115. #
  116. # :call-seq: append_after_filter(names, block)
  117. #
  118. # Append an after filter. See _insert_callbacks for parameter details.
  119. ##
  120. # :method: around_filter
  121. #
  122. # :call-seq: around_filter(names, block)
  123. #
  124. # Append an around filter. See _insert_callbacks for parameter details.
  125. ##
  126. # :method: prepend_around_filter
  127. #
  128. # :call-seq: prepend_around_filter(names, block)
  129. #
  130. # Prepend an around filter. See _insert_callbacks for parameter details.
  131. ##
  132. # :method: skip_around_filter
  133. #
  134. # :call-seq: skip_around_filter(names, block)
  135. #
  136. # Skip an around filter. See _insert_callbacks for parameter details.
  137. ##
  138. # :method: append_around_filter
  139. #
  140. # :call-seq: append_around_filter(names, block)
  141. #
  142. # Append an around filter. See _insert_callbacks for parameter details.
  143. # set up before_filter, prepend_before_filter, skip_before_filter, etc.
  144. # for each of before, after, and around.
  145. [:before, :after, :around].each do |filter|
  146. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  147. # Append a before, after or around filter. See _insert_callbacks
  148. # for details on the allowed parameters.
  149. def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
  150. _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
  151. options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after} # options[:if] = (Array.wrap(options[:if]) << "!halted") if false
  152. set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
  153. end # end
  154. end # end
  155. # Prepend a before, after or around filter. See _insert_callbacks
  156. # for details on the allowed parameters.
  157. def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
  158. _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
  159. options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after} # options[:if] = (Array.wrap(options[:if]) << "!halted") if false
  160. set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
  161. end # end
  162. end # end
  163. # Skip a before, after or around filter. See _insert_callbacks
  164. # for details on the allowed parameters.
  165. def skip_#{filter}_filter(*names, &blk) # def skip_before_filter(*names, &blk)
  166. _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
  167. skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
  168. end # end
  169. end # end
  170. # *_filter is the same as append_*_filter
  171. alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter
  172. RUBY_EVAL
  173. end
  174. end
  175. end
  176. end