activesupport 3.2.22.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +364 -135
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -4
  5. data/lib/active_support/backtrace_cleaner.rb +33 -25
  6. data/lib/active_support/basic_object.rb +7 -17
  7. data/lib/active_support/benchmarkable.rb +19 -15
  8. data/lib/active_support/buffered_logger.rb +9 -113
  9. data/lib/active_support/cache/file_store.rb +14 -14
  10. data/lib/active_support/cache/mem_cache_store.rb +24 -30
  11. data/lib/active_support/cache/memory_store.rb +2 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +48 -37
  13. data/lib/active_support/cache.rb +228 -183
  14. data/lib/active_support/callbacks.rb +201 -253
  15. data/lib/active_support/concern.rb +16 -23
  16. data/lib/active_support/concurrency/latch.rb +27 -0
  17. data/lib/active_support/configurable.rb +69 -12
  18. data/lib/active_support/core_ext/array/access.rb +17 -9
  19. data/lib/active_support/core_ext/array/conversions.rb +115 -55
  20. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  21. data/lib/active_support/core_ext/array/grouping.rb +21 -22
  22. data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
  23. data/lib/active_support/core_ext/array/wrap.rb +13 -16
  24. data/lib/active_support/core_ext/array.rb +0 -1
  25. data/lib/active_support/core_ext/benchmark.rb +7 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -24
  27. data/lib/active_support/core_ext/class/attribute.rb +40 -32
  28. data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
  29. data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -5
  31. data/lib/active_support/core_ext/date/calculations.rb +44 -187
  32. data/lib/active_support/core_ext/date/conversions.rb +16 -38
  33. data/lib/active_support/core_ext/date/zones.rb +25 -2
  34. data/lib/active_support/core_ext/date.rb +5 -0
  35. data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
  36. data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
  37. data/lib/active_support/core_ext/date_time/calculations.rb +77 -62
  38. data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
  39. data/lib/active_support/core_ext/date_time/zones.rb +11 -8
  40. data/lib/active_support/core_ext/date_time.rb +4 -0
  41. data/lib/active_support/core_ext/enumerable.rb +26 -73
  42. data/lib/active_support/core_ext/file/atomic.rb +27 -11
  43. data/lib/active_support/core_ext/file.rb +0 -1
  44. data/lib/active_support/core_ext/hash/conversions.rb +145 -79
  45. data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
  46. data/lib/active_support/core_ext/hash/diff.rb +5 -4
  47. data/lib/active_support/core_ext/hash/except.rb +1 -9
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
  49. data/lib/active_support/core_ext/hash/keys.rb +108 -24
  50. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  51. data/lib/active_support/core_ext/hash/slice.rb +12 -12
  52. data/lib/active_support/core_ext/hash.rb +0 -1
  53. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  54. data/lib/active_support/core_ext/integer/time.rb +17 -12
  55. data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
  56. data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
  57. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  58. data/lib/active_support/core_ext/load_error.rb +7 -5
  59. data/lib/active_support/core_ext/logger.rb +7 -23
  60. data/lib/active_support/core_ext/marshal.rb +21 -0
  61. data/lib/active_support/core_ext/module/aliasing.rb +8 -9
  62. data/lib/active_support/core_ext/module/anonymous.rb +2 -7
  63. data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
  64. data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
  65. data/lib/active_support/core_ext/module/delegation.rb +92 -49
  66. data/lib/active_support/core_ext/module/deprecation.rb +19 -3
  67. data/lib/active_support/core_ext/module/introspection.rb +17 -27
  68. data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
  69. data/lib/active_support/core_ext/module/remove_method.rb +1 -5
  70. data/lib/active_support/core_ext/module.rb +1 -3
  71. data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
  72. data/lib/active_support/core_ext/numeric/time.rb +6 -6
  73. data/lib/active_support/core_ext/numeric.rb +1 -0
  74. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  75. data/lib/active_support/core_ext/object/blank.rb +7 -23
  76. data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
  77. data/lib/active_support/core_ext/object/duplicable.rb +1 -30
  78. data/lib/active_support/core_ext/object/inclusion.rb +8 -7
  79. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  80. data/lib/active_support/core_ext/object/to_json.rb +8 -0
  81. data/lib/active_support/core_ext/object/to_param.rb +5 -2
  82. data/lib/active_support/core_ext/object/try.rb +46 -25
  83. data/lib/active_support/core_ext/object/with_options.rb +7 -8
  84. data/lib/active_support/core_ext/object.rb +1 -0
  85. data/lib/active_support/core_ext/proc.rb +3 -0
  86. data/lib/active_support/core_ext/range/conversions.rb +0 -2
  87. data/lib/active_support/core_ext/range/include_range.rb +3 -1
  88. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  89. data/lib/active_support/core_ext/range.rb +0 -2
  90. data/lib/active_support/core_ext/string/access.rb +95 -90
  91. data/lib/active_support/core_ext/string/conversions.rb +27 -38
  92. data/lib/active_support/core_ext/string/encoding.rb +6 -9
  93. data/lib/active_support/core_ext/string/filters.rb +24 -18
  94. data/lib/active_support/core_ext/string/indent.rb +43 -0
  95. data/lib/active_support/core_ext/string/inflections.rb +70 -60
  96. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  97. data/lib/active_support/core_ext/string/multibyte.rb +41 -64
  98. data/lib/active_support/core_ext/string/output_safety.rb +59 -51
  99. data/lib/active_support/core_ext/string/zones.rb +13 -0
  100. data/lib/active_support/core_ext/string.rb +2 -3
  101. data/lib/active_support/core_ext/struct.rb +6 -0
  102. data/lib/active_support/core_ext/thread.rb +74 -0
  103. data/lib/active_support/core_ext/time/calculations.rb +108 -184
  104. data/lib/active_support/core_ext/time/conversions.rb +27 -51
  105. data/lib/active_support/core_ext/time/marshal.rb +0 -27
  106. data/lib/active_support/core_ext/time/zones.rb +27 -17
  107. data/lib/active_support/core_ext/time.rb +5 -0
  108. data/lib/active_support/core_ext/uri.rb +13 -17
  109. data/lib/active_support/core_ext.rb +3 -2
  110. data/lib/active_support/dependencies/autoload.rb +47 -20
  111. data/lib/active_support/dependencies.rb +160 -141
  112. data/lib/active_support/deprecation/behaviors.rb +44 -30
  113. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  114. data/lib/active_support/deprecation/method_wrappers.rb +33 -18
  115. data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
  116. data/lib/active_support/deprecation/reporting.rb +40 -11
  117. data/lib/active_support/deprecation.rb +39 -14
  118. data/lib/active_support/descendants_tracker.rb +34 -19
  119. data/lib/active_support/duration.rb +6 -8
  120. data/lib/active_support/file_update_checker.rb +63 -47
  121. data/lib/active_support/gzip.rb +11 -5
  122. data/lib/active_support/hash_with_indifferent_access.rb +127 -42
  123. data/lib/active_support/i18n.rb +4 -0
  124. data/lib/active_support/i18n_railtie.rb +5 -22
  125. data/lib/active_support/inflections.rb +14 -12
  126. data/lib/active_support/inflector/inflections.rb +108 -71
  127. data/lib/active_support/inflector/methods.rb +181 -160
  128. data/lib/active_support/inflector/transliterate.rb +16 -17
  129. data/lib/active_support/json/decoding.rb +18 -17
  130. data/lib/active_support/json/encoding.rb +93 -39
  131. data/lib/active_support/json/variable.rb +10 -1
  132. data/lib/active_support/key_generator.rb +75 -0
  133. data/lib/active_support/lazy_load_hooks.rb +21 -19
  134. data/lib/active_support/locale/en.yml +100 -3
  135. data/lib/active_support/log_subscriber/test_helper.rb +18 -15
  136. data/lib/active_support/log_subscriber.rb +35 -52
  137. data/lib/active_support/logger.rb +57 -0
  138. data/lib/active_support/logger_silence.rb +24 -0
  139. data/lib/active_support/message_encryptor.rb +38 -35
  140. data/lib/active_support/message_verifier.rb +9 -15
  141. data/lib/active_support/multibyte/chars.rb +80 -333
  142. data/lib/active_support/multibyte/unicode.rb +74 -64
  143. data/lib/active_support/multibyte.rb +5 -28
  144. data/lib/active_support/notifications/fanout.rb +112 -18
  145. data/lib/active_support/notifications/instrumenter.rb +33 -14
  146. data/lib/active_support/notifications.rb +79 -26
  147. data/lib/active_support/number_helper.rb +637 -0
  148. data/lib/active_support/ordered_hash.rb +8 -190
  149. data/lib/active_support/ordered_options.rb +21 -23
  150. data/lib/active_support/per_thread_registry.rb +52 -0
  151. data/lib/active_support/proxy_object.rb +13 -0
  152. data/lib/active_support/rails.rb +27 -0
  153. data/lib/active_support/railtie.rb +12 -32
  154. data/lib/active_support/rescuable.rb +9 -4
  155. data/lib/active_support/string_inquirer.rb +13 -8
  156. data/lib/active_support/subscriber.rb +93 -0
  157. data/lib/active_support/tagged_logging.rb +51 -73
  158. data/lib/active_support/test_case.rb +46 -17
  159. data/lib/active_support/testing/assertions.rb +56 -26
  160. data/lib/active_support/testing/autorun.rb +5 -0
  161. data/lib/active_support/testing/constant_lookup.rb +54 -0
  162. data/lib/active_support/testing/declarative.rb +1 -1
  163. data/lib/active_support/testing/deprecation.rb +0 -19
  164. data/lib/active_support/testing/isolation.rb +29 -58
  165. data/lib/active_support/testing/pending.rb +5 -43
  166. data/lib/active_support/testing/setup_and_teardown.rb +6 -92
  167. data/lib/active_support/testing/tagged_logging.rb +25 -0
  168. data/lib/active_support/time.rb +6 -21
  169. data/lib/active_support/time_with_zone.rb +80 -43
  170. data/lib/active_support/values/time_zone.rb +81 -60
  171. data/lib/active_support/values/unicode_tables.dat +0 -0
  172. data/lib/active_support/version.rb +7 -6
  173. data/lib/active_support/xml_mini/jdom.rb +9 -11
  174. data/lib/active_support/xml_mini/libxml.rb +1 -2
  175. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  176. data/lib/active_support/xml_mini/nokogiri.rb +1 -2
  177. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  178. data/lib/active_support/xml_mini/rexml.rb +6 -8
  179. data/lib/active_support/xml_mini.rb +35 -17
  180. data/lib/active_support.rb +8 -21
  181. metadata +104 -72
  182. data/lib/active_support/base64.rb +0 -54
  183. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  184. data/lib/active_support/core_ext/date/freeze.rb +0 -33
  185. data/lib/active_support/core_ext/exception.rb +0 -3
  186. data/lib/active_support/core_ext/file/path.rb +0 -5
  187. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  188. data/lib/active_support/core_ext/float.rb +0 -1
  189. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
  190. data/lib/active_support/core_ext/io.rb +0 -15
  191. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  192. data/lib/active_support/core_ext/module/synchronization.rb +0 -45
  193. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  194. data/lib/active_support/core_ext/process.rb +0 -1
  195. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  196. data/lib/active_support/core_ext/range/cover.rb +0 -3
  197. data/lib/active_support/core_ext/rexml.rb +0 -46
  198. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  199. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  200. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  201. data/lib/active_support/memoizable.rb +0 -116
  202. data/lib/active_support/multibyte/exceptions.rb +0 -8
  203. data/lib/active_support/multibyte/utils.rb +0 -60
  204. data/lib/active_support/ruby/shim.rb +0 -22
  205. data/lib/active_support/security_utils.rb +0 -27
  206. data/lib/active_support/testing/mochaing.rb +0 -7
  207. data/lib/active_support/testing/performance/jruby.rb +0 -115
  208. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  209. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  210. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  211. data/lib/active_support/testing/performance/ruby.rb +0 -152
  212. data/lib/active_support/testing/performance.rb +0 -317
  213. data/lib/active_support/time/autoload.rb +0 -5
  214. data/lib/active_support/whiny_nil.rb +0 -24
@@ -1,30 +1,29 @@
1
+ require 'thread_safe'
1
2
  require 'active_support/concern'
2
3
  require 'active_support/descendants_tracker'
3
- require 'active_support/core_ext/array/wrap'
4
4
  require 'active_support/core_ext/class/attribute'
5
5
  require 'active_support/core_ext/kernel/reporting'
6
6
  require 'active_support/core_ext/kernel/singleton_class'
7
- require 'active_support/core_ext/object/inclusion'
8
7
 
9
8
  module ActiveSupport
10
- # \Callbacks are code hooks that are run at key points in an object's lifecycle.
11
- # The typical use case is to have a base class define a set of callbacks relevant
12
- # to the other functionality it supplies, so that subclasses can install callbacks
13
- # that enhance or modify the base functionality without needing to override
14
- # or redefine methods of the base class.
9
+ # Callbacks are code hooks that are run at key points in an object's lifecycle.
10
+ # The typical use case is to have a base class define a set of callbacks
11
+ # relevant to the other functionality it supplies, so that subclasses can
12
+ # install callbacks that enhance or modify the base functionality without
13
+ # needing to override or redefine methods of the base class.
15
14
  #
16
- # Mixing in this module allows you to define the events in the object's lifecycle
17
- # that will support callbacks (via +ClassMethods.define_callbacks+), set the instance
18
- # methods, procs, or callback objects to be called (via +ClassMethods.set_callback+),
19
- # and run the installed callbacks at the appropriate times (via +run_callbacks+).
15
+ # Mixing in this module allows you to define the events in the object's
16
+ # lifecycle that will support callbacks (via +ClassMethods.define_callbacks+),
17
+ # set the instance methods, procs, or callback objects to be called (via
18
+ # +ClassMethods.set_callback+), and run the installed callbacks at the
19
+ # appropriate times (via +run_callbacks+).
20
20
  #
21
- # Three kinds of callbacks are supported: before callbacks, run before a certain event;
22
- # after callbacks, run after the event; and around callbacks, blocks that surround the
23
- # event, triggering it when they yield. Callback code can be contained in instance
24
- # methods, procs or lambdas, or callback objects that respond to certain predetermined
25
- # methods. See +ClassMethods.set_callback+ for details.
26
- #
27
- # ==== Example
21
+ # Three kinds of callbacks are supported: before callbacks, run before a
22
+ # certain event; after callbacks, run after the event; and around callbacks,
23
+ # blocks that surround the event, triggering it when they yield. Callback code
24
+ # can be contained in instance methods, procs or lambdas, or callback objects
25
+ # that respond to certain predetermined methods. See +ClassMethods.set_callback+
26
+ # for details.
28
27
  #
29
28
  # class Record
30
29
  # include ActiveSupport::Callbacks
@@ -55,7 +54,6 @@ module ActiveSupport
55
54
  # saving...
56
55
  # - save
57
56
  # saved
58
- #
59
57
  module Callbacks
60
58
  extend Concern
61
59
 
@@ -63,28 +61,29 @@ module ActiveSupport
63
61
  extend ActiveSupport::DescendantsTracker
64
62
  end
65
63
 
64
+ CALLBACK_FILTER_TYPES = [:before, :after, :around]
65
+
66
66
  # Runs the callbacks for the given event.
67
67
  #
68
68
  # Calls the before and around callbacks in the order they were set, yields
69
- # the block (if given one), and then runs the after callbacks in reverse order.
70
- # Optionally accepts a key, which will be used to compile an optimized callback
71
- # method for each key. See +ClassMethods.define_callbacks+ for more information.
69
+ # the block (if given one), and then runs the after callbacks in reverse
70
+ # order.
72
71
  #
73
- # If the callback chain was halted, returns +false+. Otherwise returns the result
74
- # of the block, or +true+ if no block is given.
72
+ # If the callback chain was halted, returns +false+. Otherwise returns the
73
+ # result of the block, or +true+ if no block is given.
75
74
  #
76
75
  # run_callbacks :save do
77
76
  # save
78
77
  # end
79
- #
80
- def run_callbacks(kind, *args, &block)
81
- send("_run_#{kind}_callbacks", *args, &block)
78
+ def run_callbacks(kind, &block)
79
+ runner_name = self.class.__define_callbacks(kind, self)
80
+ send(runner_name, &block)
82
81
  end
83
82
 
84
83
  private
85
84
 
86
85
  # A hook invoked everytime a before callback is halted.
87
- # This can be overriden in AS::Callback implementors in order
86
+ # This can be overridden in AS::Callback implementors in order
88
87
  # to provide better debugging/logging.
89
88
  def halted_callback_hook(filter)
90
89
  end
@@ -92,41 +91,37 @@ module ActiveSupport
92
91
  class Callback #:nodoc:#
93
92
  @@_callback_sequence = 0
94
93
 
95
- attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
94
+ attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
96
95
 
97
96
  def initialize(chain, filter, kind, options, klass)
98
97
  @chain, @kind, @klass = chain, kind, klass
98
+ deprecate_per_key_option(options)
99
99
  normalize_options!(options)
100
100
 
101
- @per_key = options.delete(:per_key)
102
101
  @raw_filter, @options = filter, options
103
102
  @filter = _compile_filter(filter)
104
- @compiled_options = _compile_options(options)
105
- @callback_id = next_id
103
+ recompile_options!
104
+ end
106
105
 
107
- _compile_per_key_options
106
+ def deprecate_per_key_option(options)
107
+ if options[:per_key]
108
+ raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
109
+ end
108
110
  end
109
111
 
110
112
  def clone(chain, klass)
111
113
  obj = super()
112
114
  obj.chain = chain
113
115
  obj.klass = klass
114
- obj.per_key = @per_key.dup
115
116
  obj.options = @options.dup
116
- obj.per_key[:if] = @per_key[:if].dup
117
- obj.per_key[:unless] = @per_key[:unless].dup
118
117
  obj.options[:if] = @options[:if].dup
119
118
  obj.options[:unless] = @options[:unless].dup
120
119
  obj
121
120
  end
122
121
 
123
122
  def normalize_options!(options)
124
- options[:if] = Array.wrap(options[:if])
125
- options[:unless] = Array.wrap(options[:unless])
126
-
127
- options[:per_key] ||= {}
128
- options[:per_key][:if] = Array.wrap(options[:per_key][:if])
129
- options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
123
+ options[:if] = Array(options[:if])
124
+ options[:unless] = Array(options[:unless])
130
125
  end
131
126
 
132
127
  def name
@@ -141,44 +136,26 @@ module ActiveSupport
141
136
  @kind == _kind && @filter == _filter
142
137
  end
143
138
 
144
- def _update_filter(filter_options, new_options)
145
- filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
146
- filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
139
+ def duplicates?(other)
140
+ matches?(other.kind, other.filter)
147
141
  end
148
142
 
149
- def recompile!(_options, _per_key)
150
- _update_filter(self.options, _options)
151
- _update_filter(self.per_key, _per_key)
152
-
153
- @callback_id = next_id
154
- @filter = _compile_filter(@raw_filter)
155
- @compiled_options = _compile_options(@options)
156
- _compile_per_key_options
143
+ def _update_filter(filter_options, new_options)
144
+ filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
145
+ filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
157
146
  end
158
147
 
159
- def _compile_per_key_options
160
- key_options = _compile_options(@per_key)
148
+ def recompile!(_options)
149
+ deprecate_per_key_option(_options)
150
+ _update_filter(self.options, _options)
161
151
 
162
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
163
- def _one_time_conditions_valid_#{@callback_id}?
164
- true if #{key_options}
165
- end
166
- RUBY_EVAL
152
+ recompile_options!
167
153
  end
168
154
 
169
- # This will supply contents for before and around filters, and no
170
- # contents for after filters (for the forward pass).
171
- def start(key=nil, object=nil)
172
- return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
173
-
174
- # options[0] is the compiled form of supplied conditions
175
- # options[1] is the "end" for the conditional
176
- #
155
+ # Wraps code with filter
156
+ def apply(code)
177
157
  case @kind
178
158
  when :before
179
- # if condition # before_save :filter_name, :if => :condition
180
- # filter_name
181
- # end
182
159
  <<-RUBY_EVAL
183
160
  if !halted && #{@compiled_options}
184
161
  # This double assignment is to prevent warnings in 1.9.3 as
@@ -190,54 +167,20 @@ module ActiveSupport
190
167
  halted_callback_hook(#{@raw_filter.inspect.inspect})
191
168
  end
192
169
  end
170
+ #{code}
193
171
  RUBY_EVAL
194
- when :around
195
- # Compile around filters with conditions into proxy methods
196
- # that contain the conditions.
197
- #
198
- # For `around_save :filter_name, :if => :condition':
199
- #
200
- # def _conditional_callback_save_17
201
- # if condition
202
- # filter_name do
203
- # yield self
204
- # end
205
- # else
206
- # yield self
207
- # end
208
- # end
209
- #
210
- name = "_conditional_callback_#{@kind}_#{next_id}"
211
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
212
- def #{name}(halted)
213
- if #{@compiled_options} && !halted
214
- #{@filter} do
215
- yield self
216
- end
217
- else
218
- yield self
219
- end
220
- end
221
- RUBY_EVAL
222
- "#{name}(halted) do"
223
- end
224
- end
225
-
226
- # This will supply contents for around and after filters, but not
227
- # before filters (for the backward pass).
228
- def end(key=nil, object=nil)
229
- return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
230
-
231
- case @kind
232
172
  when :after
233
- # after_save :filter_name, :if => :condition
234
173
  <<-RUBY_EVAL
235
- if #{@compiled_options}
174
+ #{code}
175
+ if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
236
176
  #{@filter}
237
177
  end
238
178
  RUBY_EVAL
239
179
  when :around
180
+ name = define_conditional_callback
240
181
  <<-RUBY_EVAL
182
+ #{name}(halted) do
183
+ #{code}
241
184
  value
242
185
  end
243
186
  RUBY_EVAL
@@ -246,46 +189,74 @@ module ActiveSupport
246
189
 
247
190
  private
248
191
 
192
+ # Compile around filters with conditions into proxy methods
193
+ # that contain the conditions.
194
+ #
195
+ # For `set_callback :save, :around, :filter_name, if: :condition':
196
+ #
197
+ # def _conditional_callback_save_17
198
+ # if condition
199
+ # filter_name do
200
+ # yield self
201
+ # end
202
+ # else
203
+ # yield self
204
+ # end
205
+ # end
206
+ def define_conditional_callback
207
+ name = "_conditional_callback_#{@kind}_#{next_id}"
208
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
209
+ def #{name}(halted)
210
+ if #{@compiled_options} && !halted
211
+ #{@filter} do
212
+ yield self
213
+ end
214
+ else
215
+ yield self
216
+ end
217
+ end
218
+ RUBY_EVAL
219
+ name
220
+ end
221
+
249
222
  # Options support the same options as filters themselves (and support
250
223
  # symbols, string, procs, and objects), so compile a conditional
251
- # expression based on the options
252
- def _compile_options(options)
224
+ # expression based on the options.
225
+ def recompile_options!
253
226
  conditions = ["true"]
254
227
 
255
228
  unless options[:if].empty?
256
- conditions << Array.wrap(_compile_filter(options[:if]))
229
+ conditions << Array(_compile_filter(options[:if]))
257
230
  end
258
231
 
259
232
  unless options[:unless].empty?
260
- conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
233
+ conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
261
234
  end
262
235
 
263
- conditions.flatten.join(" && ")
236
+ @compiled_options = conditions.flatten.join(" && ")
264
237
  end
265
238
 
266
239
  # Filters support:
267
240
  #
268
241
  # Arrays:: Used in conditions. This is used to specify
269
242
  # multiple conditions. Used internally to
270
- # merge conditions from skip_* filters
271
- # Symbols:: A method to call
272
- # Strings:: Some content to evaluate
273
- # Procs:: A proc to call with the object
274
- # Objects:: An object with a before_foo method on it to call
243
+ # merge conditions from skip_* filters.
244
+ # Symbols:: A method to call.
245
+ # Strings:: Some content to evaluate.
246
+ # Procs:: A proc to call with the object.
247
+ # Objects:: An object with a <tt>before_foo</tt> method on it to call.
275
248
  #
276
249
  # All of these objects are compiled into methods and handled
277
250
  # the same after this point:
278
251
  #
279
- # Arrays:: Merged together into a single filter
280
- # Symbols:: Already methods
281
- # Strings:: class_eval'ed into methods
282
- # Procs:: define_method'ed into methods
252
+ # Arrays:: Merged together into a single filter.
253
+ # Symbols:: Already methods.
254
+ # Strings:: class_eval'ed into methods.
255
+ # Procs:: define_method'ed into methods.
283
256
  # Objects::
284
257
  # a method is created that calls the before_foo method
285
258
  # on the object.
286
- #
287
259
  def _compile_filter(filter)
288
- method_name = "_callback_#{@kind}_#{next_id}"
289
260
  case filter
290
261
  when Array
291
262
  filter.map {|f| _compile_filter(f)}
@@ -294,15 +265,17 @@ module ActiveSupport
294
265
  when String
295
266
  "(#{filter})"
296
267
  when Proc
268
+ method_name = "_callback_#{@kind}_#{next_id}"
297
269
  @klass.send(:define_method, method_name, &filter)
298
270
  return method_name if filter.arity <= 0
299
271
 
300
272
  method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
301
273
  else
274
+ method_name = "_callback_#{@kind}_#{next_id}"
302
275
  @klass.send(:define_method, "#{method_name}_object") { filter }
303
276
 
304
277
  _normalize_legacy_filter(kind, filter)
305
- scopes = Array.wrap(chain.config[:scope])
278
+ scopes = Array(chain.config[:scope])
306
279
  method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
307
280
 
308
281
  @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -317,10 +290,15 @@ module ActiveSupport
317
290
 
318
291
  def _normalize_legacy_filter(kind, filter)
319
292
  if !filter.respond_to?(kind) && filter.respond_to?(:filter)
293
+ message = "Filter object with #filter method is deprecated. Define method corresponding " \
294
+ "to filter type (#before, #after or #around)."
295
+ ActiveSupport::Deprecation.warn message
320
296
  filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
321
297
  def #{kind}(context, &block) filter(context, &block) end
322
298
  RUBY_EVAL
323
- elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
299
+ elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
300
+ message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
301
+ ActiveSupport::Deprecation.warn message
324
302
  def filter.around(context)
325
303
  should_continue = before(context)
326
304
  yield if should_continue
@@ -330,7 +308,7 @@ module ActiveSupport
330
308
  end
331
309
  end
332
310
 
333
- # An Array with a compile method
311
+ # An Array with a compile method.
334
312
  class CallbackChain < Array #:nodoc:#
335
313
  attr_reader :name, :config
336
314
 
@@ -338,87 +316,86 @@ module ActiveSupport
338
316
  @name = name
339
317
  @config = {
340
318
  :terminator => "false",
341
- :rescuable => false,
342
319
  :scope => [ :kind ]
343
- }.merge(config)
320
+ }.merge!(config)
344
321
  end
345
322
 
346
- def compile(key=nil, object=nil)
347
- method = []
348
- method << "value = nil"
349
- method << "halted = false"
350
-
351
- each do |callback|
352
- method << callback.start(key, object)
323
+ def compile
324
+ method = ["value = nil", "halted = false"]
325
+ callbacks = "value = !halted && (!block_given? || yield)"
326
+ reverse_each do |callback|
327
+ callbacks = callback.apply(callbacks)
353
328
  end
329
+ method << callbacks
354
330
 
355
- if config[:rescuable]
356
- method << "rescued_error = nil"
357
- method << "begin"
358
- end
331
+ method << "value"
332
+ method.join("\n")
333
+ end
359
334
 
360
- method << "value = yield if block_given? && !halted"
335
+ def append(*callbacks)
336
+ callbacks.each { |c| append_one(c) }
337
+ end
361
338
 
362
- if config[:rescuable]
363
- method << "rescue Exception => e"
364
- method << "rescued_error = e"
365
- method << "end"
366
- end
339
+ def prepend(*callbacks)
340
+ callbacks.each { |c| prepend_one(c) }
341
+ end
367
342
 
368
- reverse_each do |callback|
369
- method << callback.end(key, object)
370
- end
343
+ private
371
344
 
372
- method << "raise rescued_error if rescued_error" if config[:rescuable]
373
- method << "halted ? false : (block_given? ? value : true)"
374
- method.compact.join("\n")
345
+ def append_one(callback)
346
+ remove_duplicates(callback)
347
+ push(callback)
375
348
  end
349
+
350
+ def prepend_one(callback)
351
+ remove_duplicates(callback)
352
+ unshift(callback)
353
+ end
354
+
355
+ def remove_duplicates(callback)
356
+ delete_if { |c| callback.duplicates?(c) }
357
+ end
358
+
376
359
  end
377
360
 
378
361
  module ClassMethods
379
- # Generate the internal runner method called by +run_callbacks+.
380
- def __define_runner(symbol) #:nodoc:
381
- runner_method = "_run_#{symbol}_callbacks"
382
- unless private_method_defined?(runner_method)
383
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
384
- def #{runner_method}(key = nil, &blk)
385
- self.class.__run_callback(key, :#{symbol}, self, &blk)
386
- end
387
- private :#{runner_method}
388
- RUBY_EVAL
389
- end
390
- end
391
362
 
392
- # This method calls the callback method for the given key.
393
- # If this called first time it creates a new callback method for the key,
394
- # calculating which callbacks can be omitted because of per_key conditions.
395
- #
396
- def __run_callback(key, kind, object, &blk) #:nodoc:
397
- name = __callback_runner_name(key, kind)
363
+ # This method defines callback chain method for the given kind
364
+ # if it was not yet defined.
365
+ # This generated method plays caching role.
366
+ def __define_callbacks(kind, object) #:nodoc:
367
+ name = __callback_runner_name(kind)
398
368
  unless object.respond_to?(name, true)
399
- str = object.send("_#{kind}_callbacks").compile(key, object)
369
+ str = object.send("_#{kind}_callbacks").compile
400
370
  class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
401
371
  def #{name}() #{str} end
402
372
  protected :#{name}
403
373
  RUBY_EVAL
404
374
  end
405
- object.send(name, &blk)
375
+ name
406
376
  end
407
377
 
408
378
  def __reset_runner(symbol)
409
- name = __callback_runner_name(nil, symbol)
379
+ name = __callback_runner_name(symbol)
410
380
  undef_method(name) if method_defined?(name)
411
381
  end
412
382
 
413
- def __callback_runner_name(key, kind)
414
- "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks"
383
+ def __callback_runner_name_cache
384
+ @__callback_runner_name_cache ||= ThreadSafe::Cache.new {|cache, kind| cache[kind] = __generate_callback_runner_name(kind) }
385
+ end
386
+
387
+ def __generate_callback_runner_name(kind)
388
+ "_run__#{self.name.hash.abs}__#{kind}__callbacks"
389
+ end
390
+
391
+ def __callback_runner_name(kind)
392
+ __callback_runner_name_cache[kind]
415
393
  end
416
394
 
417
395
  # This is used internally to append, prepend and skip callbacks to the
418
396
  # CallbackChain.
419
- #
420
397
  def __update_callbacks(name, filters = [], block = nil) #:nodoc:
421
- type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
398
+ type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
422
399
  options = filters.last.is_a?(Hash) ? filters.pop : {}
423
400
  filters.unshift(block) if block
424
401
 
@@ -432,8 +409,8 @@ module ActiveSupport
432
409
  # Install a callback for the given event.
433
410
  #
434
411
  # set_callback :save, :before, :before_meth
435
- # set_callback :save, :after, :after_meth, :if => :condition
436
- # set_callback :save, :around, lambda { |r| stuff; result = yield; stuff }
412
+ # set_callback :save, :after, :after_meth, if: :condition
413
+ # set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
437
414
  #
438
415
  # The second arguments indicates whether the callback is to be run +:before+,
439
416
  # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
@@ -441,53 +418,29 @@ module ActiveSupport
441
418
  #
442
419
  # set_callback :save, :before_meth
443
420
  #
444
- # The callback can specified as a symbol naming an instance method; as a proc,
445
- # lambda, or block; as a string to be instance evaluated; or as an object that
446
- # responds to a certain method determined by the <tt>:scope</tt> argument to
447
- # +define_callback+.
421
+ # The callback can specified as a symbol naming an instance method; as a
422
+ # proc, lambda, or block; as a string to be instance evaluated; or as an
423
+ # object that responds to a certain method determined by the <tt>:scope</tt>
424
+ # argument to +define_callback+.
448
425
  #
449
426
  # If a proc, lambda, or block is given, its body is evaluated in the context
450
427
  # of the current object. It can also optionally accept the current object as
451
428
  # an argument.
452
429
  #
453
- # Before and around callbacks are called in the order that they are set; after
454
- # callbacks are called in the reverse order.
455
- #
430
+ # Before and around callbacks are called in the order that they are set;
431
+ # after callbacks are called in the reverse order.
432
+ #
456
433
  # Around callbacks can access the return value from the event, if it
457
434
  # wasn't halted, from the +yield+ call.
458
435
  #
459
436
  # ===== Options
460
437
  #
461
- # * <tt>:if</tt> - A symbol naming an instance method or a proc; the callback
462
- # will be called only when it returns a true value.
463
- # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the callback
464
- # will be called only when it returns a false value.
465
- # * <tt>:prepend</tt> - If true, the callback will be prepended to the existing
466
- # chain rather than appended.
467
- # * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options;
468
- # see "Per-key conditions" below.
469
- #
470
- # ===== Per-key conditions
471
- #
472
- # When creating or skipping callbacks, you can specify conditions that
473
- # are always the same for a given key. For instance, in Action Pack,
474
- # we convert :only and :except conditions into per-key conditions.
475
- #
476
- # before_filter :authenticate, :except => "index"
477
- #
478
- # becomes
479
- #
480
- # set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
481
- #
482
- # Per-key conditions are evaluated only once per use of a given key.
483
- # In the case of the above example, you would do:
484
- #
485
- # run_callbacks(:process_action, action_name) { ... dispatch stuff ... }
486
- #
487
- # In that case, each action_name would get its own compiled callback
488
- # method that took into consideration the per_key conditions. This
489
- # is a speed improvement for ActionPack.
490
- #
438
+ # * <tt>:if</tt> - A symbol naming an instance method or a proc; the
439
+ # callback will be called only when it returns a +true+ value.
440
+ # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the
441
+ # callback will be called only when it returns a +false+ value.
442
+ # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
443
+ # existing chain rather than appended.
491
444
  def set_callback(name, *filter_list, &block)
492
445
  mapped = nil
493
446
 
@@ -496,23 +449,19 @@ module ActiveSupport
496
449
  Callback.new(chain, filter, type, options.dup, self)
497
450
  end
498
451
 
499
- filters.each do |filter|
500
- chain.delete_if {|c| c.matches?(type, filter) }
501
- end
502
-
503
- options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
452
+ options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
504
453
 
505
454
  target.send("_#{name}_callbacks=", chain)
506
455
  end
507
456
  end
508
457
 
509
- # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or <tt>:unless</tt>
510
- # options may be passed in order to control when the callback is skipped.
458
+ # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
459
+ # <tt>:unless</tt> options may be passed in order to control when the
460
+ # callback is skipped.
511
461
  #
512
462
  # class Writer < Person
513
- # skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 }
463
+ # skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
514
464
  # end
515
- #
516
465
  def skip_callback(name, *filter_list, &block)
517
466
  __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
518
467
  filters.each do |filter|
@@ -521,7 +470,7 @@ module ActiveSupport
521
470
  if filter && options.any?
522
471
  new_filter = filter.clone(chain, self)
523
472
  chain.insert(chain.index(filter), new_filter)
524
- new_filter.recompile!(options, options[:per_key] || {})
473
+ new_filter.recompile!(options)
525
474
  end
526
475
 
527
476
  chain.delete(filter)
@@ -531,7 +480,6 @@ module ActiveSupport
531
480
  end
532
481
 
533
482
  # Remove all set callbacks for the given event.
534
- #
535
483
  def reset_callbacks(symbol)
536
484
  callbacks = send("_#{symbol}_callbacks")
537
485
 
@@ -554,24 +502,25 @@ module ActiveSupport
554
502
  #
555
503
  # ===== Options
556
504
  #
557
- # * <tt>:terminator</tt> - Determines when a before filter will halt the callback
558
- # chain, preventing following callbacks from being called and the event from being
559
- # triggered. This is a string to be eval'ed. The result of the callback is available
560
- # in the <tt>result</tt> variable.
505
+ # * <tt>:terminator</tt> - Determines when a before filter will halt the
506
+ # callback chain, preventing following callbacks from being called and
507
+ # the event from being triggered. This is a string to be eval'ed. The
508
+ # result of the callback is available in the +result+ variable.
561
509
  #
562
- # define_callbacks :validate, :terminator => "result == false"
510
+ # define_callbacks :validate, terminator: 'result == false'
563
511
  #
564
512
  # In this example, if any before validate callbacks returns +false+,
565
- # other callbacks are not executed. Defaults to "false", meaning no value
513
+ # other callbacks are not executed. Defaults to +false+, meaning no value
566
514
  # halts the chain.
567
515
  #
568
- # * <tt>:rescuable</tt> - By default, after filters are not executed if
569
- # the given block or a before filter raises an error. By setting this option
570
- # to <tt>true</tt> exception raised by given block is stored and after
571
- # executing all the after callbacks the stored exception is raised.
516
+ # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
517
+ # callbacks should be terminated by the <tt>:terminator</tt> option. By
518
+ # default after callbacks executed no matter if callback chain was
519
+ # terminated or not. Option makes sense only when <tt>:terminator</tt>
520
+ # option is specified.
572
521
  #
573
- # * <tt>:scope</tt> - Indicates which methods should be executed when an object
574
- # is used as a callback.
522
+ # * <tt>:scope</tt> - Indicates which methods should be executed when an
523
+ # object is used as a callback.
575
524
  #
576
525
  # class Audit
577
526
  # def before(caller)
@@ -596,29 +545,28 @@ module ActiveSupport
596
545
  # end
597
546
  # end
598
547
  #
599
- # In the above case whenever you save an account the method <tt>Audit#before</tt> will
600
- # be called. On the other hand
548
+ # In the above case whenever you save an account the method
549
+ # <tt>Audit#before</tt> will be called. On the other hand
601
550
  #
602
- # define_callbacks :save, :scope => [:kind, :name]
551
+ # define_callbacks :save, scope: [:kind, :name]
603
552
  #
604
- # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
605
- # <tt>#{kind}_#{name}</tt> on the given instance. In this case "kind" is "before" and
606
- # "name" is "save". In this context +:kind+ and +:name+ have special meanings: +:kind+
607
- # refers to the kind of callback (before/after/around) and +:name+ refers to the
608
- # method on which callbacks are being defined.
553
+ # would trigger <tt>Audit#before_save</tt> instead. That's constructed
554
+ # by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
555
+ # case "kind" is "before" and "name" is "save". In this context +:kind+
556
+ # and +:name+ have special meanings: +:kind+ refers to the kind of
557
+ # callback (before/after/around) and +:name+ refers to the method on
558
+ # which callbacks are being defined.
609
559
  #
610
560
  # A declaration like
611
561
  #
612
- # define_callbacks :save, :scope => [:name]
562
+ # define_callbacks :save, scope: [:name]
613
563
  #
614
564
  # would call <tt>Audit#save</tt>.
615
- #
616
565
  def define_callbacks(*callbacks)
617
566
  config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
618
567
  callbacks.each do |callback|
619
568
  class_attribute "_#{callback}_callbacks"
620
569
  send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
621
- __define_runner(callback)
622
570
  end
623
571
  end
624
572
  end