activesupport 5.0.7.2 → 5.1.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +464 -694
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support.rb +8 -4
  6. data/lib/active_support/all.rb +3 -3
  7. data/lib/active_support/array_inquirer.rb +7 -5
  8. data/lib/active_support/backtrace_cleaner.rb +4 -4
  9. data/lib/active_support/benchmarkable.rb +3 -3
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache.rb +41 -48
  12. data/lib/active_support/cache/file_store.rb +11 -20
  13. data/lib/active_support/cache/mem_cache_store.rb +30 -40
  14. data/lib/active_support/cache/memory_store.rb +13 -13
  15. data/lib/active_support/cache/null_store.rb +4 -4
  16. data/lib/active_support/cache/strategy/local_cache.rb +13 -22
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -5
  18. data/lib/active_support/callbacks.rb +649 -584
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  20. data/lib/active_support/concurrency/share_lock.rb +20 -21
  21. data/lib/active_support/configurable.rb +5 -5
  22. data/lib/active_support/core_ext.rb +1 -2
  23. data/lib/active_support/core_ext/array.rb +7 -7
  24. data/lib/active_support/core_ext/array/access.rb +1 -1
  25. data/lib/active_support/core_ext/array/conversions.rb +15 -15
  26. data/lib/active_support/core_ext/array/grouping.rb +1 -1
  27. data/lib/active_support/core_ext/array/inquiry.rb +1 -1
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +1 -1
  29. data/lib/active_support/core_ext/benchmark.rb +1 -1
  30. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  31. data/lib/active_support/core_ext/big_decimal/conversions.rb +4 -6
  32. data/lib/active_support/core_ext/class.rb +2 -2
  33. data/lib/active_support/core_ext/class/attribute.rb +5 -5
  34. data/lib/active_support/core_ext/class/attribute_accessors.rb +1 -1
  35. data/lib/active_support/core_ext/class/subclasses.rb +18 -4
  36. data/lib/active_support/core_ext/date.rb +5 -5
  37. data/lib/active_support/core_ext/date/acts_like.rb +1 -1
  38. data/lib/active_support/core_ext/date/blank.rb +1 -1
  39. data/lib/active_support/core_ext/date/calculations.rb +8 -8
  40. data/lib/active_support/core_ext/date/conversions.rb +12 -12
  41. data/lib/active_support/core_ext/date/zones.rb +2 -2
  42. data/lib/active_support/core_ext/date_and_time/calculations.rb +27 -22
  43. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  44. data/lib/active_support/core_ext/date_and_time/zones.rb +7 -8
  45. data/lib/active_support/core_ext/date_time.rb +5 -5
  46. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -2
  47. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  48. data/lib/active_support/core_ext/date_time/calculations.rb +20 -10
  49. data/lib/active_support/core_ext/date_time/compatibility.rb +2 -2
  50. data/lib/active_support/core_ext/date_time/conversions.rb +12 -12
  51. data/lib/active_support/core_ext/digest/uuid.rb +4 -4
  52. data/lib/active_support/core_ext/enumerable.rb +23 -12
  53. data/lib/active_support/core_ext/file.rb +1 -1
  54. data/lib/active_support/core_ext/file/atomic.rb +4 -4
  55. data/lib/active_support/core_ext/hash.rb +9 -9
  56. data/lib/active_support/core_ext/hash/compact.rb +12 -9
  57. data/lib/active_support/core_ext/hash/conversions.rb +36 -37
  58. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -2
  59. data/lib/active_support/core_ext/hash/keys.rb +6 -6
  60. data/lib/active_support/core_ext/hash/reverse_merge.rb +1 -1
  61. data/lib/active_support/core_ext/hash/slice.rb +4 -4
  62. data/lib/active_support/core_ext/hash/transform_values.rb +1 -0
  63. data/lib/active_support/core_ext/integer.rb +3 -3
  64. data/lib/active_support/core_ext/integer/inflections.rb +1 -1
  65. data/lib/active_support/core_ext/integer/time.rb +2 -2
  66. data/lib/active_support/core_ext/kernel.rb +4 -4
  67. data/lib/active_support/core_ext/kernel/concern.rb +1 -1
  68. data/lib/active_support/core_ext/kernel/reporting.rb +1 -1
  69. data/lib/active_support/core_ext/load_error.rb +1 -18
  70. data/lib/active_support/core_ext/module.rb +11 -12
  71. data/lib/active_support/core_ext/module/aliasing.rb +3 -48
  72. data/lib/active_support/core_ext/module/attr_internal.rb +4 -4
  73. data/lib/active_support/core_ext/module/attribute_accessors.rb +11 -5
  74. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +20 -13
  75. data/lib/active_support/core_ext/module/concerning.rb +1 -1
  76. data/lib/active_support/core_ext/module/delegation.rb +85 -16
  77. data/lib/active_support/core_ext/module/introspection.rb +3 -11
  78. data/lib/active_support/core_ext/module/reachable.rb +2 -2
  79. data/lib/active_support/core_ext/numeric.rb +4 -4
  80. data/lib/active_support/core_ext/numeric/conversions.rb +3 -9
  81. data/lib/active_support/core_ext/numeric/inquiry.rb +21 -21
  82. data/lib/active_support/core_ext/numeric/time.rb +5 -5
  83. data/lib/active_support/core_ext/object.rb +12 -12
  84. data/lib/active_support/core_ext/object/blank.rb +3 -1
  85. data/lib/active_support/core_ext/object/conversions.rb +4 -4
  86. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  87. data/lib/active_support/core_ext/object/duplicable.rb +34 -4
  88. data/lib/active_support/core_ext/object/inclusion.rb +1 -1
  89. data/lib/active_support/core_ext/object/json.rb +26 -12
  90. data/lib/active_support/core_ext/object/to_param.rb +1 -1
  91. data/lib/active_support/core_ext/object/to_query.rb +8 -5
  92. data/lib/active_support/core_ext/object/try.rb +1 -1
  93. data/lib/active_support/core_ext/object/with_options.rb +12 -1
  94. data/lib/active_support/core_ext/range.rb +4 -4
  95. data/lib/active_support/core_ext/range/conversions.rb +1 -1
  96. data/lib/active_support/core_ext/regexp.rb +4 -0
  97. data/lib/active_support/core_ext/securerandom.rb +3 -3
  98. data/lib/active_support/core_ext/string.rb +13 -13
  99. data/lib/active_support/core_ext/string/access.rb +6 -6
  100. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  101. data/lib/active_support/core_ext/string/filters.rb +3 -3
  102. data/lib/active_support/core_ext/string/indent.rb +4 -4
  103. data/lib/active_support/core_ext/string/inflections.rb +10 -14
  104. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  105. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  106. data/lib/active_support/core_ext/string/output_safety.rb +19 -20
  107. data/lib/active_support/core_ext/string/strip.rb +1 -1
  108. data/lib/active_support/core_ext/string/zones.rb +2 -2
  109. data/lib/active_support/core_ext/time.rb +5 -5
  110. data/lib/active_support/core_ext/time/acts_like.rb +1 -1
  111. data/lib/active_support/core_ext/time/calculations.rb +46 -29
  112. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  113. data/lib/active_support/core_ext/time/zones.rb +3 -3
  114. data/lib/active_support/core_ext/uri.rb +2 -2
  115. data/lib/active_support/dependencies.rb +45 -46
  116. data/lib/active_support/dependencies/interlock.rb +1 -1
  117. data/lib/active_support/deprecation.rb +9 -8
  118. data/lib/active_support/deprecation/behaviors.rb +3 -3
  119. data/lib/active_support/deprecation/constant_accessor.rb +50 -0
  120. data/lib/active_support/deprecation/instance_delegator.rb +2 -2
  121. data/lib/active_support/deprecation/method_wrappers.rb +10 -3
  122. data/lib/active_support/deprecation/proxy_wrappers.rb +6 -4
  123. data/lib/active_support/deprecation/reporting.rb +7 -7
  124. data/lib/active_support/duration.rb +221 -28
  125. data/lib/active_support/duration/iso8601_parser.rb +66 -65
  126. data/lib/active_support/duration/iso8601_serializer.rb +11 -9
  127. data/lib/active_support/evented_file_update_checker.rb +59 -55
  128. data/lib/active_support/execution_wrapper.rb +3 -3
  129. data/lib/active_support/executor.rb +1 -1
  130. data/lib/active_support/file_update_checker.rb +54 -50
  131. data/lib/active_support/gem_version.rb +2 -2
  132. data/lib/active_support/gzip.rb +4 -4
  133. data/lib/active_support/hash_with_indifferent_access.rb +40 -28
  134. data/lib/active_support/i18n.rb +5 -5
  135. data/lib/active_support/i18n_railtie.rb +14 -9
  136. data/lib/active_support/inflections.rb +11 -11
  137. data/lib/active_support/inflector.rb +5 -5
  138. data/lib/active_support/inflector/inflections.rb +11 -9
  139. data/lib/active_support/inflector/methods.rb +52 -51
  140. data/lib/active_support/inflector/transliterate.rb +8 -11
  141. data/lib/active_support/json.rb +2 -2
  142. data/lib/active_support/json/decoding.rb +3 -3
  143. data/lib/active_support/json/encoding.rb +8 -7
  144. data/lib/active_support/key_generator.rb +17 -17
  145. data/lib/active_support/lazy_load_hooks.rb +2 -2
  146. data/lib/active_support/log_subscriber.rb +9 -7
  147. data/lib/active_support/log_subscriber/test_helper.rb +9 -9
  148. data/lib/active_support/logger.rb +3 -3
  149. data/lib/active_support/logger_silence.rb +3 -3
  150. data/lib/active_support/logger_thread_safe_level.rb +1 -1
  151. data/lib/active_support/message_encryptor.rb +77 -35
  152. data/lib/active_support/message_verifier.rb +7 -7
  153. data/lib/active_support/multibyte.rb +2 -2
  154. data/lib/active_support/multibyte/chars.rb +23 -21
  155. data/lib/active_support/multibyte/unicode.rb +68 -89
  156. data/lib/active_support/notifications.rb +7 -5
  157. data/lib/active_support/notifications/fanout.rb +3 -3
  158. data/lib/active_support/notifications/instrumenter.rb +5 -5
  159. data/lib/active_support/number_helper.rb +5 -4
  160. data/lib/active_support/number_helper/number_converter.rb +11 -11
  161. data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -3
  162. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -2
  163. data/lib/active_support/number_helper/number_to_human_converter.rb +8 -10
  164. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -11
  165. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  166. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -3
  167. data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -32
  168. data/lib/active_support/number_helper/rounding_helper.rb +64 -0
  169. data/lib/active_support/option_merger.rb +1 -1
  170. data/lib/active_support/ordered_hash.rb +3 -3
  171. data/lib/active_support/ordered_options.rb +6 -4
  172. data/lib/active_support/per_thread_registry.rb +5 -5
  173. data/lib/active_support/rails.rb +12 -6
  174. data/lib/active_support/railtie.rb +3 -3
  175. data/lib/active_support/reloader.rb +1 -1
  176. data/lib/active_support/rescuable.rb +6 -6
  177. data/lib/active_support/security_utils.rb +1 -1
  178. data/lib/active_support/string_inquirer.rb +8 -2
  179. data/lib/active_support/subscriber.rb +9 -5
  180. data/lib/active_support/tagged_logging.rb +4 -4
  181. data/lib/active_support/test_case.rb +12 -29
  182. data/lib/active_support/testing/assertions.rb +100 -2
  183. data/lib/active_support/testing/autorun.rb +2 -2
  184. data/lib/active_support/testing/constant_lookup.rb +0 -1
  185. data/lib/active_support/testing/declarative.rb +1 -1
  186. data/lib/active_support/testing/deprecation.rb +3 -2
  187. data/lib/active_support/testing/isolation.rb +15 -22
  188. data/lib/active_support/testing/method_call_assertions.rb +1 -1
  189. data/lib/active_support/testing/setup_and_teardown.rb +2 -2
  190. data/lib/active_support/testing/stream.rb +28 -28
  191. data/lib/active_support/testing/tagged_logging.rb +1 -1
  192. data/lib/active_support/testing/time_helpers.rb +45 -11
  193. data/lib/active_support/time.rb +12 -12
  194. data/lib/active_support/time_with_zone.rb +16 -14
  195. data/lib/active_support/values/time_zone.rb +100 -31
  196. data/lib/active_support/values/unicode_tables.dat +0 -0
  197. data/lib/active_support/version.rb +1 -1
  198. data/lib/active_support/xml_mini.rb +34 -36
  199. data/lib/active_support/xml_mini/jdom.rb +112 -112
  200. data/lib/active_support/xml_mini/libxml.rb +12 -11
  201. data/lib/active_support/xml_mini/libxmlsax.rb +13 -14
  202. data/lib/active_support/xml_mini/nokogiri.rb +10 -10
  203. data/lib/active_support/xml_mini/nokogirisax.rb +12 -13
  204. data/lib/active_support/xml_mini/rexml.rb +9 -9
  205. metadata +8 -9
  206. data/lib/active_support/concurrency/latch.rb +0 -26
  207. data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
  208. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
  209. data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
  210. data/lib/active_support/core_ext/struct.rb +0 -3
  211. data/lib/active_support/core_ext/time/marshal.rb +0 -3
@@ -1,13 +1,13 @@
1
1
  begin
2
- require 'dalli'
2
+ require "dalli"
3
3
  rescue LoadError => e
4
4
  $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
5
5
  raise e
6
6
  end
7
7
 
8
- require 'digest/md5'
9
- require 'active_support/core_ext/marshal'
10
- require 'active_support/core_ext/array/extract_options'
8
+ require "digest/md5"
9
+ require "active_support/core_ext/marshal"
10
+ require "active_support/core_ext/array/extract_options"
11
11
 
12
12
  module ActiveSupport
13
13
  module Cache
@@ -26,24 +26,24 @@ module ActiveSupport
26
26
  class MemCacheStore < Store
27
27
  # Provide support for raw values in the local cache strategy.
28
28
  module LocalCacheWithRaw # :nodoc:
29
- protected
30
- def read_entry(key, options)
31
- entry = super
32
- if options[:raw] && local_cache && entry
33
- entry = deserialize_entry(entry.value)
29
+ private
30
+ def read_entry(key, options)
31
+ entry = super
32
+ if options[:raw] && local_cache && entry
33
+ entry = deserialize_entry(entry.value)
34
+ end
35
+ entry
34
36
  end
35
- entry
36
- end
37
37
 
38
- def write_entry(key, entry, options) # :nodoc:
39
- if options[:raw] && local_cache
40
- raw_entry = Entry.new(entry.value.to_s)
41
- raw_entry.expires_at = entry.expires_at
42
- super(key, raw_entry, options)
43
- else
44
- super
38
+ def write_entry(key, entry, options)
39
+ if options[:raw] && local_cache
40
+ raw_entry = Entry.new(entry.value.to_s)
41
+ raw_entry.expires_at = entry.expires_at
42
+ super(key, raw_entry, options)
43
+ else
44
+ super
45
+ end
45
46
  end
46
- end
47
47
  end
48
48
 
49
49
  prepend Strategy::LocalCache
@@ -85,7 +85,7 @@ module ActiveSupport
85
85
  @data = addresses.first
86
86
  else
87
87
  mem_cache_options = options.dup
88
- UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
88
+ UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
89
89
  @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
90
90
  end
91
91
  end
@@ -96,7 +96,7 @@ module ActiveSupport
96
96
  options = names.extract_options!
97
97
  options = merged_options(options)
98
98
 
99
- keys_to_names = Hash[names.map{|name| [normalize_key(name, options), name]}]
99
+ keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
100
100
  raw_values = @data.get_multi(keys_to_names.keys)
101
101
  values = {}
102
102
  raw_values.each do |key, value|
@@ -110,9 +110,9 @@ module ActiveSupport
110
110
  # operator and can only be used on values written with the :raw option.
111
111
  # Calling it on a value not stored with :raw will initialize that value
112
112
  # to zero.
113
- def increment(name, amount = 1, options = nil) # :nodoc:
113
+ def increment(name, amount = 1, options = nil)
114
114
  options = merged_options(options)
115
- instrument(:increment, name, :amount => amount) do
115
+ instrument(:increment, name, amount: amount) do
116
116
  rescue_error_with nil do
117
117
  @data.incr(normalize_key(name, options), amount)
118
118
  end
@@ -123,9 +123,9 @@ module ActiveSupport
123
123
  # operator and can only be used on values written with the :raw option.
124
124
  # Calling it on a value not stored with :raw will initialize that value
125
125
  # to zero.
126
- def decrement(name, amount = 1, options = nil) # :nodoc:
126
+ def decrement(name, amount = 1, options = nil)
127
127
  options = merged_options(options)
128
- instrument(:decrement, name, :amount => amount) do
128
+ instrument(:decrement, name, amount: amount) do
129
129
  rescue_error_with nil do
130
130
  @data.decr(normalize_key(name, options), amount)
131
131
  end
@@ -143,14 +143,14 @@ module ActiveSupport
143
143
  @data.stats
144
144
  end
145
145
 
146
- protected
146
+ private
147
147
  # Read an entry from the cache.
148
- def read_entry(key, options) # :nodoc:
148
+ def read_entry(key, options)
149
149
  rescue_error_with(nil) { deserialize_entry(@data.get(key, options)) }
150
150
  end
151
151
 
152
152
  # Write an entry to the cache.
153
- def write_entry(key, entry, options) # :nodoc:
153
+ def write_entry(key, entry, options)
154
154
  method = options && options[:unless_exist] ? :add : :set
155
155
  value = options[:raw] ? entry.value.to_s : entry
156
156
  expires_in = options[:expires_in].to_i
@@ -164,31 +164,21 @@ module ActiveSupport
164
164
  end
165
165
 
166
166
  # Delete an entry from the cache.
167
- def delete_entry(key, options) # :nodoc:
167
+ def delete_entry(key, options)
168
168
  rescue_error_with(false) { @data.delete(key) }
169
169
  end
170
170
 
171
- private
172
-
173
171
  # Memcache keys are binaries. So we need to force their encoding to binary
174
172
  # before applying the regular expression to ensure we are escaping all
175
173
  # characters properly.
176
174
  def normalize_key(key, options)
177
175
  key = super.dup
178
176
  key = key.force_encoding(Encoding::ASCII_8BIT)
179
- key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
177
+ key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
180
178
  key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
181
179
  key
182
180
  end
183
181
 
184
- def escape_key(key)
185
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
186
- `escape_key` is deprecated and will be removed from Rails 5.1.
187
- Please use `normalize_key` which will return a fully resolved key or nothing.
188
- MESSAGE
189
- key
190
- end
191
-
192
182
  def deserialize_entry(raw_value)
193
183
  if raw_value
194
184
  entry = Marshal.load(raw_value) rescue raw_value
@@ -1,10 +1,10 @@
1
- require 'monitor'
1
+ require "monitor"
2
2
 
3
3
  module ActiveSupport
4
4
  module Cache
5
5
  # A cache store implementation which stores everything into memory in the
6
6
  # same process. If you're running multiple Ruby on Rails server processes
7
- # (which is the case if you're using mongrel_cluster or Phusion Passenger),
7
+ # (which is the case if you're using Phusion Passenger or puma clustered mode),
8
8
  # then this means that Rails server process instances won't be able
9
9
  # to share cache data with each other and this may not be the most
10
10
  # appropriate cache in that scenario.
@@ -28,6 +28,7 @@ module ActiveSupport
28
28
  @pruning = false
29
29
  end
30
30
 
31
+ # Delete all data stored in a given cache store.
31
32
  def clear(options = nil)
32
33
  synchronize do
33
34
  @data.clear
@@ -39,8 +40,8 @@ module ActiveSupport
39
40
  # Preemptively iterates through all stored keys and removes the ones which have expired.
40
41
  def cleanup(options = nil)
41
42
  options = merged_options(options)
42
- instrument(:cleanup, :size => @data.size) do
43
- keys = synchronize{ @data.keys }
43
+ instrument(:cleanup, size: @data.size) do
44
+ keys = synchronize { @data.keys }
44
45
  keys.each do |key|
45
46
  entry = @data[key]
46
47
  delete_entry(key, options) if entry && entry.expired?
@@ -56,8 +57,8 @@ module ActiveSupport
56
57
  begin
57
58
  start_time = Time.now
58
59
  cleanup
59
- instrument(:prune, target_size, :from => @cache_size) do
60
- keys = synchronize{ @key_access.keys.sort{|a,b| @key_access[a].to_f <=> @key_access[b].to_f} }
60
+ instrument(:prune, target_size, from: @cache_size) do
61
+ keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
61
62
  keys.each do |key|
62
63
  delete_entry(key, options)
63
64
  return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
@@ -83,6 +84,7 @@ module ActiveSupport
83
84
  modify_value(name, -amount, options)
84
85
  end
85
86
 
87
+ # Deletes cache entries if the cache key matches a given pattern.
86
88
  def delete_matched(matcher, options = nil)
87
89
  options = merged_options(options)
88
90
  instrument(:delete_matched, matcher.inspect) do
@@ -104,15 +106,15 @@ module ActiveSupport
104
106
  @monitor.synchronize(&block)
105
107
  end
106
108
 
107
- protected
109
+ private
108
110
 
109
111
  PER_ENTRY_OVERHEAD = 240
110
112
 
111
- def cached_size(key, entry) # :nodoc:
113
+ def cached_size(key, entry)
112
114
  key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
113
115
  end
114
116
 
115
- def read_entry(key, options) # :nodoc:
117
+ def read_entry(key, options)
116
118
  entry = @data[key]
117
119
  synchronize do
118
120
  if entry
@@ -124,7 +126,7 @@ module ActiveSupport
124
126
  entry
125
127
  end
126
128
 
127
- def write_entry(key, entry, options) # :nodoc:
129
+ def write_entry(key, entry, options)
128
130
  entry.dup_value!
129
131
  synchronize do
130
132
  old_entry = @data[key]
@@ -141,7 +143,7 @@ module ActiveSupport
141
143
  end
142
144
  end
143
145
 
144
- def delete_entry(key, options) # :nodoc:
146
+ def delete_entry(key, options)
145
147
  synchronize do
146
148
  @key_access.delete(key)
147
149
  entry = @data.delete(key)
@@ -150,8 +152,6 @@ module ActiveSupport
150
152
  end
151
153
  end
152
154
 
153
- private
154
-
155
155
  def modify_value(name, amount, options)
156
156
  synchronize do
157
157
  options = merged_options(options)
@@ -25,15 +25,15 @@ module ActiveSupport
25
25
  def delete_matched(matcher, options = nil)
26
26
  end
27
27
 
28
- protected
29
- def read_entry(key, options) # :nodoc:
28
+ private
29
+ def read_entry(key, options)
30
30
  end
31
31
 
32
- def write_entry(key, entry, options) # :nodoc:
32
+ def write_entry(key, entry, options)
33
33
  true
34
34
  end
35
35
 
36
- def delete_entry(key, options) # :nodoc:
36
+ def delete_entry(key, options)
37
37
  false
38
38
  end
39
39
  end
@@ -1,6 +1,6 @@
1
- require 'active_support/core_ext/object/duplicable'
2
- require 'active_support/core_ext/string/inflections'
3
- require 'active_support/per_thread_registry'
1
+ require "active_support/core_ext/object/duplicable"
2
+ require "active_support/core_ext/string/inflections"
3
+ require "active_support/per_thread_registry"
4
4
 
5
5
  module ActiveSupport
6
6
  module Cache
@@ -9,7 +9,7 @@ module ActiveSupport
9
9
  # duration of a block. Repeated calls to the cache for the same key will hit the
10
10
  # in-memory cache for faster access.
11
11
  module LocalCache
12
- autoload :Middleware, 'active_support/cache/strategy/local_cache_middleware'
12
+ autoload :Middleware, "active_support/cache/strategy/local_cache_middleware"
13
13
 
14
14
  # Class for storing and registering the local caches.
15
15
  class LocalCacheRegistry # :nodoc:
@@ -70,6 +70,7 @@ module ActiveSupport
70
70
  def with_local_cache
71
71
  use_temporary_local_cache(LocalStore.new) { yield }
72
72
  end
73
+
73
74
  # Middleware class can be inserted as a Rack handler to be local cache for the
74
75
  # duration of request.
75
76
  def middleware
@@ -92,20 +93,20 @@ module ActiveSupport
92
93
 
93
94
  def increment(name, amount = 1, options = nil) # :nodoc:
94
95
  return super unless local_cache
95
- value = bypass_local_cache{super}
96
+ value = bypass_local_cache { super }
96
97
  write_cache_value(name, value, options)
97
98
  value
98
99
  end
99
100
 
100
101
  def decrement(name, amount = 1, options = nil) # :nodoc:
101
102
  return super unless local_cache
102
- value = bypass_local_cache{super}
103
+ value = bypass_local_cache { super }
103
104
  write_cache_value(name, value, options)
104
105
  value
105
106
  end
106
107
 
107
- protected
108
- def read_entry(key, options) # :nodoc:
108
+ private
109
+ def read_entry(key, options)
109
110
  if cache = local_cache
110
111
  cache.fetch_entry(key) { super }
111
112
  else
@@ -113,25 +114,17 @@ module ActiveSupport
113
114
  end
114
115
  end
115
116
 
116
- def write_entry(key, entry, options) # :nodoc:
117
+ def write_entry(key, entry, options)
117
118
  local_cache.write_entry(key, entry, options) if local_cache
118
119
  super
119
120
  end
120
121
 
121
- def delete_entry(key, options) # :nodoc:
122
+ def delete_entry(key, options)
122
123
  local_cache.delete_entry(key, options) if local_cache
123
124
  super
124
125
  end
125
126
 
126
- def set_cache_value(value, name, amount, options) # :nodoc:
127
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
128
- `set_cache_value` is deprecated and will be removed from Rails 5.1.
129
- Please use `write_cache_value` instead.
130
- MESSAGE
131
- write_cache_value name, value, options
132
- end
133
-
134
- def write_cache_value(name, value, options) # :nodoc:
127
+ def write_cache_value(name, value, options)
135
128
  name = normalize_key(name, options)
136
129
  cache = local_cache
137
130
  cache.mute do
@@ -143,10 +136,8 @@ module ActiveSupport
143
136
  end
144
137
  end
145
138
 
146
- private
147
-
148
139
  def local_cache_key
149
- @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
140
+ @local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, "_").to_sym
150
141
  end
151
142
 
152
143
  def local_cache
@@ -1,11 +1,10 @@
1
- require 'rack/body_proxy'
2
- require 'rack/utils'
1
+ require "rack/body_proxy"
2
+ require "rack/utils"
3
3
 
4
4
  module ActiveSupport
5
5
  module Cache
6
6
  module Strategy
7
7
  module LocalCache
8
-
9
8
  #--
10
9
  # This class wraps up local storage for middlewares. Only the middleware method should
11
10
  # construct them.
@@ -13,9 +12,9 @@ module ActiveSupport
13
12
  attr_reader :name, :local_cache_key
14
13
 
15
14
  def initialize(name, local_cache_key)
16
- @name = name
15
+ @name = name
17
16
  @local_cache_key = local_cache_key
18
- @app = nil
17
+ @app = nil
19
18
  end
20
19
 
21
20
  def new(app)
@@ -1,13 +1,12 @@
1
- require 'active_support/concern'
2
- require 'active_support/descendants_tracker'
3
- require 'active_support/core_ext/array/extract_options'
4
- require 'active_support/core_ext/class/attribute'
5
- require 'active_support/core_ext/kernel/reporting'
6
- require 'active_support/core_ext/kernel/singleton_class'
7
- require 'active_support/core_ext/module/attribute_accessors'
8
- require 'active_support/core_ext/string/filters'
9
- require 'active_support/deprecation'
10
- require 'thread'
1
+ require "active_support/concern"
2
+ require "active_support/descendants_tracker"
3
+ require "active_support/core_ext/array/extract_options"
4
+ require "active_support/core_ext/class/attribute"
5
+ require "active_support/core_ext/kernel/reporting"
6
+ require "active_support/core_ext/kernel/singleton_class"
7
+ require "active_support/core_ext/string/filters"
8
+ require "active_support/deprecation"
9
+ require "thread"
11
10
 
12
11
  module ActiveSupport
13
12
  # Callbacks are code hooks that are run at key points in an object's life cycle.
@@ -63,16 +62,12 @@ module ActiveSupport
63
62
 
64
63
  included do
65
64
  extend ActiveSupport::DescendantsTracker
65
+ class_attribute :__callbacks, instance_writer: false
66
+ self.__callbacks ||= {}
66
67
  end
67
68
 
68
69
  CALLBACK_FILTER_TYPES = [:before, :after, :around]
69
70
 
70
- # If true, Active Record and Active Model callbacks returning +false+ will
71
- # halt the entire callback chain and display a deprecation message.
72
- # If false, callback chains will only be halted by calling +throw :abort+.
73
- # Defaults to +true+.
74
- mattr_accessor(:halt_and_display_warning_on_return_false, instance_writer: false) { true }
75
-
76
71
  # Runs the callbacks for the given event.
77
72
  #
78
73
  # Calls the before and around callbacks in the order they were set, yields
@@ -86,706 +81,776 @@ module ActiveSupport
86
81
  # run_callbacks :save do
87
82
  # save
88
83
  # end
89
- def run_callbacks(kind, &block)
90
- send "_run_#{kind}_callbacks", &block
91
- end
92
-
93
- private
84
+ #
85
+ #--
86
+ #
87
+ # As this method is used in many places, and often wraps large portions of
88
+ # user code, it has an additional design goal of minimizing its impact on
89
+ # the visible call stack. An exception from inside a :before or :after
90
+ # callback can be as noisy as it likes -- but when control has passed
91
+ # smoothly through and into the supplied block, we want as little evidence
92
+ # as possible that we were here.
93
+ def run_callbacks(kind)
94
+ callbacks = __callbacks[kind.to_sym]
94
95
 
95
- def __run_callbacks__(callbacks, &block)
96
96
  if callbacks.empty?
97
97
  yield if block_given?
98
98
  else
99
- runner = callbacks.compile
100
- e = Filters::Environment.new(self, false, nil, block)
101
- runner.call(e).value
102
- end
103
- end
104
-
105
- # A hook invoked every time a before callback is halted.
106
- # This can be overridden in AS::Callback implementors in order
107
- # to provide better debugging/logging.
108
- def halted_callback_hook(filter)
109
- end
99
+ env = Filters::Environment.new(self, false, nil)
100
+ next_sequence = callbacks.compile
101
+
102
+ invoke_sequence = Proc.new do
103
+ skipped = nil
104
+ while true
105
+ current = next_sequence
106
+ current.invoke_before(env)
107
+ if current.final?
108
+ env.value = !env.halted && (!block_given? || yield)
109
+ elsif current.skip?(env)
110
+ (skipped ||= []) << current
111
+ next_sequence = next_sequence.nested
112
+ next
113
+ else
114
+ next_sequence = next_sequence.nested
115
+ begin
116
+ target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
117
+ target.send(method, *arguments, &block)
118
+ ensure
119
+ next_sequence = current
120
+ end
121
+ end
122
+ current.invoke_after(env)
123
+ skipped.pop.invoke_after(env) while skipped && skipped.first
124
+ break env.value
125
+ end
126
+ end
110
127
 
111
- module Conditionals # :nodoc:
112
- class Value
113
- def initialize(&block)
114
- @block = block
128
+ # Common case: no 'around' callbacks defined
129
+ if next_sequence.final?
130
+ next_sequence.invoke_before(env)
131
+ env.value = !env.halted && (!block_given? || yield)
132
+ next_sequence.invoke_after(env)
133
+ env.value
134
+ else
135
+ invoke_sequence.call
115
136
  end
116
- def call(target, value); @block.call(value); end
117
137
  end
118
138
  end
119
139
 
120
- module Filters
121
- Environment = Struct.new(:target, :halted, :value, :run_block)
140
+ private
122
141
 
123
- class End
124
- def call(env)
125
- block = env.run_block
126
- env.value = !env.halted && (!block || block.call)
127
- env
128
- end
142
+ # A hook invoked every time a before callback is halted.
143
+ # This can be overridden in ActiveSupport::Callbacks implementors in order
144
+ # to provide better debugging/logging.
145
+ def halted_callback_hook(filter)
129
146
  end
130
- ENDING = End.new
131
147
 
132
- class Before
133
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
134
- halted_lambda = chain_config[:terminator]
135
-
136
- if user_conditions.any?
137
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
138
- else
139
- halting(callback_sequence, user_callback, halted_lambda, filter)
148
+ module Conditionals # :nodoc:
149
+ class Value
150
+ def initialize(&block)
151
+ @block = block
140
152
  end
153
+ def call(target, value); @block.call(value); end
141
154
  end
155
+ end
142
156
 
143
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
144
- callback_sequence.before do |env|
145
- target = env.target
146
- value = env.value
147
- halted = env.halted
157
+ module Filters
158
+ Environment = Struct.new(:target, :halted, :value)
148
159
 
149
- if !halted && user_conditions.all? { |c| c.call(target, value) }
150
- result_lambda = -> { user_callback.call target, value }
151
- env.halted = halted_lambda.call(target, result_lambda)
152
- if env.halted
153
- target.send :halted_callback_hook, filter
154
- end
160
+ class Before
161
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
162
+ halted_lambda = chain_config[:terminator]
163
+
164
+ if user_conditions.any?
165
+ halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
166
+ else
167
+ halting(callback_sequence, user_callback, halted_lambda, filter)
155
168
  end
169
+ end
170
+
171
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
172
+ callback_sequence.before do |env|
173
+ target = env.target
174
+ value = env.value
175
+ halted = env.halted
176
+
177
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
178
+ result_lambda = -> { user_callback.call target, value }
179
+ env.halted = halted_lambda.call(target, result_lambda)
180
+ if env.halted
181
+ target.send :halted_callback_hook, filter
182
+ end
183
+ end
156
184
 
157
- env
185
+ env
186
+ end
158
187
  end
159
- end
160
- private_class_method :halting_and_conditional
188
+ private_class_method :halting_and_conditional
161
189
 
162
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
163
- callback_sequence.before do |env|
164
- target = env.target
165
- value = env.value
166
- halted = env.halted
190
+ def self.halting(callback_sequence, user_callback, halted_lambda, filter)
191
+ callback_sequence.before do |env|
192
+ target = env.target
193
+ value = env.value
194
+ halted = env.halted
167
195
 
168
- unless halted
169
- result_lambda = -> { user_callback.call target, value }
170
- env.halted = halted_lambda.call(target, result_lambda)
196
+ unless halted
197
+ result_lambda = -> { user_callback.call target, value }
198
+ env.halted = halted_lambda.call(target, result_lambda)
171
199
 
172
- if env.halted
173
- target.send :halted_callback_hook, filter
200
+ if env.halted
201
+ target.send :halted_callback_hook, filter
202
+ end
174
203
  end
175
- end
176
204
 
177
- env
205
+ env
206
+ end
178
207
  end
208
+ private_class_method :halting
179
209
  end
180
- private_class_method :halting
181
- end
182
210
 
183
- class After
184
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
185
- if chain_config[:skip_after_callbacks_if_terminated]
186
- if user_conditions.any?
187
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
211
+ class After
212
+ def self.build(callback_sequence, user_callback, user_conditions, chain_config)
213
+ if chain_config[:skip_after_callbacks_if_terminated]
214
+ if user_conditions.any?
215
+ halting_and_conditional(callback_sequence, user_callback, user_conditions)
216
+ else
217
+ halting(callback_sequence, user_callback)
218
+ end
188
219
  else
189
- halting(callback_sequence, user_callback)
220
+ if user_conditions.any?
221
+ conditional callback_sequence, user_callback, user_conditions
222
+ else
223
+ simple callback_sequence, user_callback
224
+ end
190
225
  end
191
- else
192
- if user_conditions.any?
193
- conditional callback_sequence, user_callback, user_conditions
194
- else
195
- simple callback_sequence, user_callback
226
+ end
227
+
228
+ def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
229
+ callback_sequence.after do |env|
230
+ target = env.target
231
+ value = env.value
232
+ halted = env.halted
233
+
234
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
235
+ user_callback.call target, value
236
+ end
237
+
238
+ env
196
239
  end
197
240
  end
198
- end
241
+ private_class_method :halting_and_conditional
199
242
 
200
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
201
- callback_sequence.after do |env|
202
- target = env.target
203
- value = env.value
204
- halted = env.halted
243
+ def self.halting(callback_sequence, user_callback)
244
+ callback_sequence.after do |env|
245
+ unless env.halted
246
+ user_callback.call env.target, env.value
247
+ end
205
248
 
206
- if !halted && user_conditions.all? { |c| c.call(target, value) }
207
- user_callback.call target, value
249
+ env
208
250
  end
251
+ end
252
+ private_class_method :halting
253
+
254
+ def self.conditional(callback_sequence, user_callback, user_conditions)
255
+ callback_sequence.after do |env|
256
+ target = env.target
257
+ value = env.value
258
+
259
+ if user_conditions.all? { |c| c.call(target, value) }
260
+ user_callback.call target, value
261
+ end
209
262
 
210
- env
263
+ env
264
+ end
211
265
  end
212
- end
213
- private_class_method :halting_and_conditional
266
+ private_class_method :conditional
214
267
 
215
- def self.halting(callback_sequence, user_callback)
216
- callback_sequence.after do |env|
217
- unless env.halted
268
+ def self.simple(callback_sequence, user_callback)
269
+ callback_sequence.after do |env|
218
270
  user_callback.call env.target, env.value
219
- end
220
271
 
221
- env
272
+ env
273
+ end
222
274
  end
275
+ private_class_method :simple
223
276
  end
224
- private_class_method :halting
277
+ end
225
278
 
226
- def self.conditional(callback_sequence, user_callback, user_conditions)
227
- callback_sequence.after do |env|
228
- target = env.target
229
- value = env.value
279
+ class Callback #:nodoc:#
280
+ def self.build(chain, filter, kind, options)
281
+ if filter.is_a?(String)
282
+ raise ArgumentError, <<-MSG.squish
283
+ Passing string to define a callback is not supported. See the `.set_callback`
284
+ documentation to see supported values.
285
+ MSG
286
+ end
230
287
 
231
- if user_conditions.all? { |c| c.call(target, value) }
232
- user_callback.call target, value
233
- end
288
+ new chain.name, filter, kind, options, chain.config
289
+ end
234
290
 
235
- env
236
- end
291
+ attr_accessor :kind, :name
292
+ attr_reader :chain_config
293
+
294
+ def initialize(name, filter, kind, options, chain_config)
295
+ @chain_config = chain_config
296
+ @name = name
297
+ @kind = kind
298
+ @filter = filter
299
+ @key = compute_identifier filter
300
+ @if = Array(options[:if])
301
+ @unless = Array(options[:unless])
237
302
  end
238
- private_class_method :conditional
239
303
 
240
- def self.simple(callback_sequence, user_callback)
241
- callback_sequence.after do |env|
242
- user_callback.call env.target, env.value
304
+ def filter; @key; end
305
+ def raw_filter; @filter; end
243
306
 
244
- env
245
- end
307
+ def merge_conditional_options(chain, if_option:, unless_option:)
308
+ options = {
309
+ if: @if.dup,
310
+ unless: @unless.dup
311
+ }
312
+
313
+ options[:if].concat Array(unless_option)
314
+ options[:unless].concat Array(if_option)
315
+
316
+ self.class.build chain, @filter, @kind, options
246
317
  end
247
- private_class_method :simple
248
- end
249
318
 
250
- class Around
251
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
252
- if user_conditions.any?
253
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
319
+ def matches?(_kind, _filter)
320
+ @kind == _kind && filter == _filter
321
+ end
322
+
323
+ def duplicates?(other)
324
+ case @filter
325
+ when Symbol, String
326
+ matches?(other.kind, other.filter)
254
327
  else
255
- halting(callback_sequence, user_callback)
328
+ false
256
329
  end
257
330
  end
258
331
 
259
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
260
- callback_sequence.around do |env, &run|
261
- target = env.target
262
- value = env.value
263
- halted = env.halted
264
-
265
- if !halted && user_conditions.all? { |c| c.call(target, value) }
266
- user_callback.call(target, value) {
267
- run.call.value
268
- }
269
- env
270
- else
271
- run.call
272
- end
332
+ # Wraps code with filter
333
+ def apply(callback_sequence)
334
+ user_conditions = conditions_lambdas
335
+ user_callback = CallTemplate.build(@filter, self)
336
+
337
+ case kind
338
+ when :before
339
+ Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
340
+ when :after
341
+ Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
342
+ when :around
343
+ callback_sequence.around(user_callback, user_conditions)
273
344
  end
274
345
  end
275
- private_class_method :halting_and_conditional
276
346
 
277
- def self.halting(callback_sequence, user_callback)
278
- callback_sequence.around do |env, &run|
279
- target = env.target
280
- value = env.value
347
+ def current_scopes
348
+ Array(chain_config[:scope]).map { |s| public_send(s) }
349
+ end
281
350
 
282
- if env.halted
283
- run.call
351
+ private
352
+ def compute_identifier(filter)
353
+ case filter
354
+ when String, ::Proc
355
+ filter.object_id
284
356
  else
285
- user_callback.call(target, value) {
286
- run.call.value
287
- }
288
- env
357
+ filter
289
358
  end
290
359
  end
291
- end
292
- private_class_method :halting
360
+
361
+ def conditions_lambdas
362
+ @if.map { |c| CallTemplate.build(c, self).make_lambda } +
363
+ @unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
364
+ end
293
365
  end
294
- end
295
366
 
296
- class Callback #:nodoc:#
297
- def self.build(chain, filter, kind, options)
298
- if filter.is_a?(String)
299
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
300
- Passing string to define callback is deprecated and will be removed
301
- in Rails 5.1 without replacement.
302
- MSG
367
+ # A future invocation of user-supplied code (either as a callback,
368
+ # or a condition filter).
369
+ class CallTemplate # :nodoc:
370
+ def initialize(target, method, arguments, block)
371
+ @override_target = target
372
+ @method_name = method
373
+ @arguments = arguments
374
+ @override_block = block
303
375
  end
304
376
 
305
- new chain.name, filter, kind, options, chain.config
306
- end
377
+ # Return the parts needed to make this call, with the given
378
+ # input values.
379
+ #
380
+ # Returns an array of the form:
381
+ #
382
+ # [target, block, method, *arguments]
383
+ #
384
+ # This array can be used as such:
385
+ #
386
+ # target.send(method, *arguments, &block)
387
+ #
388
+ # The actual invocation is left up to the caller to minimize
389
+ # call stack pollution.
390
+ def expand(target, value, block)
391
+ result = @arguments.map { |arg|
392
+ case arg
393
+ when :value; value
394
+ when :target; target
395
+ when :block; block || raise(ArgumentError)
396
+ end
397
+ }
307
398
 
308
- attr_accessor :kind, :name
309
- attr_reader :chain_config
310
-
311
- def initialize(name, filter, kind, options, chain_config)
312
- @chain_config = chain_config
313
- @name = name
314
- @kind = kind
315
- @filter = filter
316
- @key = compute_identifier filter
317
- @if = Array(options[:if])
318
- @unless = Array(options[:unless])
319
- end
399
+ result.unshift @method_name
400
+ result.unshift @override_block || block
401
+ result.unshift @override_target || target
320
402
 
321
- def filter; @key; end
322
- def raw_filter; @filter; end
403
+ # target, block, method, *arguments = result
404
+ # target.send(method, *arguments, &block)
405
+ result
406
+ end
323
407
 
324
- def merge_conditional_options(chain, if_option:, unless_option:)
325
- options = {
326
- :if => @if.dup,
327
- :unless => @unless.dup
328
- }
408
+ # Return a lambda that will make this call when given the input
409
+ # values.
410
+ def make_lambda
411
+ lambda do |target, value, &block|
412
+ target, block, method, *arguments = expand(target, value, block)
413
+ target.send(method, *arguments, &block)
414
+ end
415
+ end
329
416
 
330
- options[:if].concat Array(unless_option)
331
- options[:unless].concat Array(if_option)
417
+ # Return a lambda that will make this call when given the input
418
+ # values, but then return the boolean inverse of that result.
419
+ def inverted_lambda
420
+ lambda do |target, value, &block|
421
+ target, block, method, *arguments = expand(target, value, block)
422
+ ! target.send(method, *arguments, &block)
423
+ end
424
+ end
332
425
 
333
- self.class.build chain, @filter, @kind, options
334
- end
426
+ # Filters support:
427
+ #
428
+ # Symbols:: A method to call.
429
+ # Strings:: Some content to evaluate.
430
+ # Procs:: A proc to call with the object.
431
+ # Objects:: An object with a <tt>before_foo</tt> method on it to call.
432
+ #
433
+ # All of these objects are converted into a CallTemplate and handled
434
+ # the same after this point.
435
+ def self.build(filter, callback)
436
+ case filter
437
+ when Symbol
438
+ new(nil, filter, [], nil)
439
+ when String
440
+ new(nil, :instance_exec, [:value], compile_lambda(filter))
441
+ when Conditionals::Value
442
+ new(filter, :call, [:target, :value], nil)
443
+ when ::Proc
444
+ if filter.arity > 1
445
+ new(nil, :instance_exec, [:target, :block], filter)
446
+ elsif filter.arity > 0
447
+ new(nil, :instance_exec, [:target], filter)
448
+ else
449
+ new(nil, :instance_exec, [], filter)
450
+ end
451
+ else
452
+ method_to_call = callback.current_scopes.join("_")
335
453
 
336
- def matches?(_kind, _filter)
337
- @kind == _kind && filter == _filter
338
- end
454
+ new(filter, method_to_call, [:target], nil)
455
+ end
456
+ end
339
457
 
340
- def duplicates?(other)
341
- case @filter
342
- when Symbol, String
343
- matches?(other.kind, other.filter)
344
- else
345
- false
458
+ def self.compile_lambda(filter)
459
+ eval("lambda { |value| #{filter} }")
346
460
  end
347
461
  end
348
462
 
349
- # Wraps code with filter
350
- def apply(callback_sequence)
351
- user_conditions = conditions_lambdas
352
- user_callback = make_lambda @filter
463
+ # Execute before and after filters in a sequence instead of
464
+ # chaining them with nested lambda calls, see:
465
+ # https://github.com/rails/rails/issues/18011
466
+ class CallbackSequence # :nodoc:
467
+ def initialize(nested = nil, call_template = nil, user_conditions = nil)
468
+ @nested = nested
469
+ @call_template = call_template
470
+ @user_conditions = user_conditions
353
471
 
354
- case kind
355
- when :before
356
- Filters::Before.build(callback_sequence, user_callback, user_conditions, chain_config, @filter)
357
- when :after
358
- Filters::After.build(callback_sequence, user_callback, user_conditions, chain_config)
359
- when :around
360
- Filters::Around.build(callback_sequence, user_callback, user_conditions, chain_config)
472
+ @before = []
473
+ @after = []
361
474
  end
362
- end
363
475
 
364
- private
365
-
366
- def invert_lambda(l)
367
- lambda { |*args, &blk| !l.call(*args, &blk) }
368
- end
476
+ def before(&before)
477
+ @before.unshift(before)
478
+ self
479
+ end
369
480
 
370
- # Filters support:
371
- #
372
- # Symbols:: A method to call.
373
- # Strings:: Some content to evaluate.
374
- # Procs:: A proc to call with the object.
375
- # Objects:: An object with a <tt>before_foo</tt> method on it to call.
376
- #
377
- # All of these objects are converted into a lambda and handled
378
- # the same after this point.
379
- def make_lambda(filter)
380
- case filter
381
- when Symbol
382
- lambda { |target, _, &blk| target.send filter, &blk }
383
- when String
384
- l = eval "lambda { |value| #{filter} }"
385
- lambda { |target, value| target.instance_exec(value, &l) }
386
- when Conditionals::Value then filter
387
- when ::Proc
388
- if filter.arity > 1
389
- return lambda { |target, _, &block|
390
- raise ArgumentError unless block
391
- target.instance_exec(target, block, &filter)
392
- }
393
- end
394
-
395
- if filter.arity <= 0
396
- lambda { |target, _| target.instance_exec(&filter) }
397
- else
398
- lambda { |target, _| target.instance_exec(target, &filter) }
399
- end
400
- else
401
- scopes = Array(chain_config[:scope])
402
- method_to_call = scopes.map{ |s| public_send(s) }.join("_")
481
+ def after(&after)
482
+ @after.push(after)
483
+ self
484
+ end
403
485
 
404
- lambda { |target, _, &blk|
405
- filter.public_send method_to_call, target, &blk
406
- }
486
+ def around(call_template, user_conditions)
487
+ CallbackSequence.new(self, call_template, user_conditions)
407
488
  end
408
- end
409
489
 
410
- def compute_identifier(filter)
411
- case filter
412
- when String, ::Proc
413
- filter.object_id
414
- else
415
- filter
490
+ def skip?(arg)
491
+ arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
416
492
  end
417
- end
418
493
 
419
- def conditions_lambdas
420
- @if.map { |c| make_lambda c } +
421
- @unless.map { |c| invert_lambda make_lambda c }
422
- end
423
- end
494
+ def nested
495
+ @nested
496
+ end
424
497
 
425
- # Execute before and after filters in a sequence instead of
426
- # chaining them with nested lambda calls, see:
427
- # https://github.com/rails/rails/issues/18011
428
- class CallbackSequence
429
- def initialize(&call)
430
- @call = call
431
- @before = []
432
- @after = []
433
- end
498
+ def final?
499
+ !@call_template
500
+ end
434
501
 
435
- def before(&before)
436
- @before.unshift(before)
437
- self
438
- end
502
+ def expand_call_template(arg, block)
503
+ @call_template.expand(arg.target, arg.value, block)
504
+ end
439
505
 
440
- def after(&after)
441
- @after.push(after)
442
- self
443
- end
506
+ def invoke_before(arg)
507
+ @before.each { |b| b.call(arg) }
508
+ end
444
509
 
445
- def around(&around)
446
- CallbackSequence.new do |arg|
447
- around.call(arg) {
448
- self.call(arg)
449
- }
510
+ def invoke_after(arg)
511
+ @after.each { |a| a.call(arg) }
450
512
  end
451
513
  end
452
514
 
453
- def call(arg)
454
- @before.each { |b| b.call(arg) }
455
- value = @call.call(arg)
456
- @after.each { |a| a.call(arg) }
457
- value
458
- end
459
- end
515
+ # An Array with a compile method.
516
+ class CallbackChain #:nodoc:#
517
+ include Enumerable
460
518
 
461
- # An Array with a compile method.
462
- class CallbackChain #:nodoc:#
463
- include Enumerable
464
-
465
- attr_reader :name, :config
466
-
467
- def initialize(name, config)
468
- @name = name
469
- @config = {
470
- scope: [:kind],
471
- terminator: default_terminator
472
- }.merge!(config)
473
- @chain = []
474
- @callbacks = nil
475
- @mutex = Mutex.new
476
- end
519
+ attr_reader :name, :config
477
520
 
478
- def each(&block); @chain.each(&block); end
479
- def index(o); @chain.index(o); end
480
- def empty?; @chain.empty?; end
521
+ def initialize(name, config)
522
+ @name = name
523
+ @config = {
524
+ scope: [:kind],
525
+ terminator: default_terminator
526
+ }.merge!(config)
527
+ @chain = []
528
+ @callbacks = nil
529
+ @mutex = Mutex.new
530
+ end
481
531
 
482
- def insert(index, o)
483
- @callbacks = nil
484
- @chain.insert(index, o)
485
- end
532
+ def each(&block); @chain.each(&block); end
533
+ def index(o); @chain.index(o); end
534
+ def empty?; @chain.empty?; end
486
535
 
487
- def delete(o)
488
- @callbacks = nil
489
- @chain.delete(o)
490
- end
536
+ def insert(index, o)
537
+ @callbacks = nil
538
+ @chain.insert(index, o)
539
+ end
491
540
 
492
- def clear
493
- @callbacks = nil
494
- @chain.clear
495
- self
496
- end
541
+ def delete(o)
542
+ @callbacks = nil
543
+ @chain.delete(o)
544
+ end
497
545
 
498
- def initialize_copy(other)
499
- @callbacks = nil
500
- @chain = other.chain.dup
501
- @mutex = Mutex.new
502
- end
546
+ def clear
547
+ @callbacks = nil
548
+ @chain.clear
549
+ self
550
+ end
551
+
552
+ def initialize_copy(other)
553
+ @callbacks = nil
554
+ @chain = other.chain.dup
555
+ @mutex = Mutex.new
556
+ end
503
557
 
504
- def compile
505
- @callbacks || @mutex.synchronize do
506
- final_sequence = CallbackSequence.new { |env| Filters::ENDING.call(env) }
507
- @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
508
- callback.apply callback_sequence
558
+ def compile
559
+ @callbacks || @mutex.synchronize do
560
+ final_sequence = CallbackSequence.new
561
+ @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
562
+ callback.apply callback_sequence
563
+ end
509
564
  end
510
565
  end
511
- end
512
566
 
513
- def append(*callbacks)
514
- callbacks.each { |c| append_one(c) }
515
- end
567
+ def append(*callbacks)
568
+ callbacks.each { |c| append_one(c) }
569
+ end
516
570
 
517
- def prepend(*callbacks)
518
- callbacks.each { |c| prepend_one(c) }
519
- end
571
+ def prepend(*callbacks)
572
+ callbacks.each { |c| prepend_one(c) }
573
+ end
520
574
 
521
- protected
522
- def chain; @chain; end
575
+ protected
576
+ def chain; @chain; end
523
577
 
524
- private
578
+ private
525
579
 
526
- def append_one(callback)
527
- @callbacks = nil
528
- remove_duplicates(callback)
529
- @chain.push(callback)
530
- end
531
-
532
- def prepend_one(callback)
533
- @callbacks = nil
534
- remove_duplicates(callback)
535
- @chain.unshift(callback)
536
- end
580
+ def append_one(callback)
581
+ @callbacks = nil
582
+ remove_duplicates(callback)
583
+ @chain.push(callback)
584
+ end
537
585
 
538
- def remove_duplicates(callback)
539
- @callbacks = nil
540
- @chain.delete_if { |c| callback.duplicates?(c) }
541
- end
586
+ def prepend_one(callback)
587
+ @callbacks = nil
588
+ remove_duplicates(callback)
589
+ @chain.unshift(callback)
590
+ end
542
591
 
543
- def default_terminator
544
- Proc.new do |target, result_lambda|
545
- terminate = true
546
- catch(:abort) do
547
- result_lambda.call if result_lambda.is_a?(Proc)
548
- terminate = false
592
+ def remove_duplicates(callback)
593
+ @callbacks = nil
594
+ @chain.delete_if { |c| callback.duplicates?(c) }
549
595
  end
550
- terminate
551
- end
552
- end
553
- end
554
596
 
555
- module ClassMethods
556
- def normalize_callback_params(filters, block) # :nodoc:
557
- type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
558
- options = filters.extract_options!
559
- filters.unshift(block) if block
560
- [type, filters, options.dup]
597
+ def default_terminator
598
+ Proc.new do |target, result_lambda|
599
+ terminate = true
600
+ catch(:abort) do
601
+ result_lambda.call if result_lambda.is_a?(Proc)
602
+ terminate = false
603
+ end
604
+ terminate
605
+ end
606
+ end
561
607
  end
562
608
 
563
- # This is used internally to append, prepend and skip callbacks to the
564
- # CallbackChain.
565
- def __update_callbacks(name) #:nodoc:
566
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
567
- chain = target.get_callbacks name
568
- yield target, chain.dup
609
+ module ClassMethods
610
+ def normalize_callback_params(filters, block) # :nodoc:
611
+ type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
612
+ options = filters.extract_options!
613
+ filters.unshift(block) if block
614
+ [type, filters, options.dup]
569
615
  end
570
- end
571
616
 
572
- # Install a callback for the given event.
573
- #
574
- # set_callback :save, :before, :before_method
575
- # set_callback :save, :after, :after_method, if: :condition
576
- # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
577
- #
578
- # The second argument indicates whether the callback is to be run +:before+,
579
- # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
580
- # means the first example above can also be written as:
581
- #
582
- # set_callback :save, :before_method
583
- #
584
- # The callback can be specified as a symbol naming an instance method; as a
585
- # proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an
586
- # object that responds to a certain method determined by the <tt>:scope</tt>
587
- # argument to +define_callbacks+.
588
- #
589
- # If a proc, lambda, or block is given, its body is evaluated in the context
590
- # of the current object. It can also optionally accept the current object as
591
- # an argument.
592
- #
593
- # Before and around callbacks are called in the order that they are set;
594
- # after callbacks are called in the reverse order.
595
- #
596
- # Around callbacks can access the return value from the event, if it
597
- # wasn't halted, from the +yield+ call.
598
- #
599
- # ===== Options
600
- #
601
- # * <tt>:if</tt> - A symbol, a string or an array of symbols and strings,
602
- # each naming an instance method or a proc; the callback will be called
603
- # only when they all return a true value.
604
- # * <tt>:unless</tt> - A symbol, a string or an array of symbols and
605
- # strings, each naming an instance method or a proc; the callback will
606
- # be called only when they all return a false value.
607
- # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
608
- # existing chain rather than appended.
609
- def set_callback(name, *filter_list, &block)
610
- type, filters, options = normalize_callback_params(filter_list, block)
611
- self_chain = get_callbacks name
612
- mapped = filters.map do |filter|
613
- Callback.build(self_chain, filter, type, options)
614
- end
615
-
616
- __update_callbacks(name) do |target, chain|
617
- options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
618
- target.set_callbacks name, chain
617
+ # This is used internally to append, prepend and skip callbacks to the
618
+ # CallbackChain.
619
+ def __update_callbacks(name) #:nodoc:
620
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
621
+ chain = target.get_callbacks name
622
+ yield target, chain.dup
623
+ end
619
624
  end
620
- end
621
625
 
622
- # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
623
- # <tt>:unless</tt> options may be passed in order to control when the
624
- # callback is skipped.
625
- #
626
- # class Writer < Person
627
- # skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
628
- # end
629
- #
630
- # An <tt>ArgumentError</tt> will be raised if the callback has not
631
- # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
632
- def skip_callback(name, *filter_list, &block)
633
- type, filters, options = normalize_callback_params(filter_list, block)
634
- options[:raise] = true unless options.key?(:raise)
635
-
636
- __update_callbacks(name) do |target, chain|
637
- filters.each do |filter|
638
- callback = chain.find {|c| c.matches?(type, filter) }
639
-
640
- if !callback && options[:raise]
641
- raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
642
- end
626
+ # Install a callback for the given event.
627
+ #
628
+ # set_callback :save, :before, :before_method
629
+ # set_callback :save, :after, :after_method, if: :condition
630
+ # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
631
+ #
632
+ # The second argument indicates whether the callback is to be run +:before+,
633
+ # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
634
+ # means the first example above can also be written as:
635
+ #
636
+ # set_callback :save, :before_method
637
+ #
638
+ # The callback can be specified as a symbol naming an instance method; as a
639
+ # proc, lambda, or block; or as an object that responds to a certain method
640
+ # determined by the <tt>:scope</tt> argument to +define_callbacks+.
641
+ #
642
+ # If a proc, lambda, or block is given, its body is evaluated in the context
643
+ # of the current object. It can also optionally accept the current object as
644
+ # an argument.
645
+ #
646
+ # Before and around callbacks are called in the order that they are set;
647
+ # after callbacks are called in the reverse order.
648
+ #
649
+ # Around callbacks can access the return value from the event, if it
650
+ # wasn't halted, from the +yield+ call.
651
+ #
652
+ # ===== Options
653
+ #
654
+ # * <tt>:if</tt> - A symbol, a string (deprecated) or an array of symbols,
655
+ # each naming an instance method or a proc; the callback will be called
656
+ # only when they all return a true value.
657
+ # * <tt>:unless</tt> - A symbol, a string (deprecated) or an array of symbols,
658
+ # each naming an instance method or a proc; the callback will be called
659
+ # only when they all return a false value.
660
+ # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
661
+ # existing chain rather than appended.
662
+ def set_callback(name, *filter_list, &block)
663
+ type, filters, options = normalize_callback_params(filter_list, block)
664
+
665
+ if options[:if].is_a?(String) || options[:unless].is_a?(String)
666
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
667
+ Passing string to be evaluated in :if and :unless conditional
668
+ options is deprecated and will be removed in Rails 5.2 without
669
+ replacement. Pass a symbol for an instance method, or a lambda,
670
+ proc or block, instead.
671
+ MSG
672
+ end
643
673
 
644
- if callback && (options.key?(:if) || options.key?(:unless))
645
- new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
646
- chain.insert(chain.index(callback), new_callback)
647
- end
674
+ self_chain = get_callbacks name
675
+ mapped = filters.map do |filter|
676
+ Callback.build(self_chain, filter, type, options)
677
+ end
648
678
 
649
- chain.delete(callback)
679
+ __update_callbacks(name) do |target, chain|
680
+ options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
681
+ target.set_callbacks name, chain
650
682
  end
651
- target.set_callbacks name, chain
652
683
  end
653
- end
654
684
 
655
- # Remove all set callbacks for the given event.
656
- def reset_callbacks(name)
657
- callbacks = get_callbacks name
685
+ # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
686
+ # <tt>:unless</tt> options may be passed in order to control when the
687
+ # callback is skipped.
688
+ #
689
+ # class Writer < Person
690
+ # skip_callback :validate, :before, :check_membership, if: -> { age > 18 }
691
+ # end
692
+ #
693
+ # An <tt>ArgumentError</tt> will be raised if the callback has not
694
+ # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
695
+ def skip_callback(name, *filter_list, &block)
696
+ type, filters, options = normalize_callback_params(filter_list, block)
697
+
698
+ if options[:if].is_a?(String) || options[:unless].is_a?(String)
699
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
700
+ Passing string to :if and :unless conditional options is deprecated
701
+ and will be removed in Rails 5.2 without replacement.
702
+ MSG
703
+ end
658
704
 
659
- ActiveSupport::DescendantsTracker.descendants(self).each do |target|
660
- chain = target.get_callbacks(name).dup
661
- callbacks.each { |c| chain.delete(c) }
662
- target.set_callbacks name, chain
663
- end
705
+ options[:raise] = true unless options.key?(:raise)
664
706
 
665
- self.set_callbacks name, callbacks.dup.clear
666
- end
707
+ __update_callbacks(name) do |target, chain|
708
+ filters.each do |filter|
709
+ callback = chain.find { |c| c.matches?(type, filter) }
710
+
711
+ if !callback && options[:raise]
712
+ raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
713
+ end
714
+
715
+ if callback && (options.key?(:if) || options.key?(:unless))
716
+ new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
717
+ chain.insert(chain.index(callback), new_callback)
718
+ end
667
719
 
668
- # Define sets of events in the object life cycle that support callbacks.
669
- #
670
- # define_callbacks :validate
671
- # define_callbacks :initialize, :save, :destroy
672
- #
673
- # ===== Options
674
- #
675
- # * <tt>:terminator</tt> - Determines when a before filter will halt the
676
- # callback chain, preventing following before and around callbacks from
677
- # being called and the event from being triggered.
678
- # This should be a lambda to be executed.
679
- # The current object and the result lambda of the callback will be provided
680
- # to the terminator lambda.
681
- #
682
- # define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
683
- #
684
- # In this example, if any before validate callbacks returns +false+,
685
- # any successive before and around callback is not executed.
686
- #
687
- # The default terminator halts the chain when a callback throws +:abort+.
688
- #
689
- # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
690
- # callbacks should be terminated by the <tt>:terminator</tt> option. By
691
- # default after callbacks are executed no matter if callback chain was
692
- # terminated or not. This option makes sense only when <tt>:terminator</tt>
693
- # option is specified.
694
- #
695
- # * <tt>:scope</tt> - Indicates which methods should be executed when an
696
- # object is used as a callback.
697
- #
698
- # class Audit
699
- # def before(caller)
700
- # puts 'Audit: before is called'
701
- # end
702
- #
703
- # def before_save(caller)
704
- # puts 'Audit: before_save is called'
705
- # end
706
- # end
707
- #
708
- # class Account
709
- # include ActiveSupport::Callbacks
710
- #
711
- # define_callbacks :save
712
- # set_callback :save, :before, Audit.new
713
- #
714
- # def save
715
- # run_callbacks :save do
716
- # puts 'save in main'
717
- # end
718
- # end
719
- # end
720
- #
721
- # In the above case whenever you save an account the method
722
- # <tt>Audit#before</tt> will be called. On the other hand
723
- #
724
- # define_callbacks :save, scope: [:kind, :name]
725
- #
726
- # would trigger <tt>Audit#before_save</tt> instead. That's constructed
727
- # by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
728
- # case "kind" is "before" and "name" is "save". In this context +:kind+
729
- # and +:name+ have special meanings: +:kind+ refers to the kind of
730
- # callback (before/after/around) and +:name+ refers to the method on
731
- # which callbacks are being defined.
732
- #
733
- # A declaration like
734
- #
735
- # define_callbacks :save, scope: [:name]
736
- #
737
- # would call <tt>Audit#save</tt>.
738
- #
739
- # NOTE: +method_name+ passed to `define_model_callbacks` must not end with
740
- # `!`, `?` or `=`.
741
- def define_callbacks(*names)
742
- options = names.extract_options!
743
-
744
- names.each do |name|
745
- class_attribute "_#{name}_callbacks", instance_writer: false
746
- set_callbacks name, CallbackChain.new(name, options)
747
-
748
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
749
- def _run_#{name}_callbacks(&block)
750
- __run_callbacks__(_#{name}_callbacks, &block)
720
+ chain.delete(callback)
751
721
  end
752
- RUBY
722
+ target.set_callbacks name, chain
723
+ end
753
724
  end
754
- end
755
725
 
756
- protected
726
+ # Remove all set callbacks for the given event.
727
+ def reset_callbacks(name)
728
+ callbacks = get_callbacks name
757
729
 
758
- def get_callbacks(name) # :nodoc:
759
- send "_#{name}_callbacks"
760
- end
730
+ ActiveSupport::DescendantsTracker.descendants(self).each do |target|
731
+ chain = target.get_callbacks(name).dup
732
+ callbacks.each { |c| chain.delete(c) }
733
+ target.set_callbacks name, chain
734
+ end
761
735
 
762
- def set_callbacks(name, callbacks) # :nodoc:
763
- send "_#{name}_callbacks=", callbacks
764
- end
736
+ set_callbacks(name, callbacks.dup.clear)
737
+ end
765
738
 
766
- def deprecated_false_terminator # :nodoc:
767
- Proc.new do |target, result_lambda|
768
- terminate = true
769
- catch(:abort) do
770
- result = result_lambda.call if result_lambda.is_a?(Proc)
771
- if Callbacks.halt_and_display_warning_on_return_false && result == false
772
- display_deprecation_warning_for_false_terminator
773
- else
774
- terminate = false
775
- end
739
+ # Define sets of events in the object life cycle that support callbacks.
740
+ #
741
+ # define_callbacks :validate
742
+ # define_callbacks :initialize, :save, :destroy
743
+ #
744
+ # ===== Options
745
+ #
746
+ # * <tt>:terminator</tt> - Determines when a before filter will halt the
747
+ # callback chain, preventing following before and around callbacks from
748
+ # being called and the event from being triggered.
749
+ # This should be a lambda to be executed.
750
+ # The current object and the result lambda of the callback will be provided
751
+ # to the terminator lambda.
752
+ #
753
+ # define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
754
+ #
755
+ # In this example, if any before validate callbacks returns +false+,
756
+ # any successive before and around callback is not executed.
757
+ #
758
+ # The default terminator halts the chain when a callback throws +:abort+.
759
+ #
760
+ # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
761
+ # callbacks should be terminated by the <tt>:terminator</tt> option. By
762
+ # default after callbacks are executed no matter if callback chain was
763
+ # terminated or not. This option makes sense only when <tt>:terminator</tt>
764
+ # option is specified.
765
+ #
766
+ # * <tt>:scope</tt> - Indicates which methods should be executed when an
767
+ # object is used as a callback.
768
+ #
769
+ # class Audit
770
+ # def before(caller)
771
+ # puts 'Audit: before is called'
772
+ # end
773
+ #
774
+ # def before_save(caller)
775
+ # puts 'Audit: before_save is called'
776
+ # end
777
+ # end
778
+ #
779
+ # class Account
780
+ # include ActiveSupport::Callbacks
781
+ #
782
+ # define_callbacks :save
783
+ # set_callback :save, :before, Audit.new
784
+ #
785
+ # def save
786
+ # run_callbacks :save do
787
+ # puts 'save in main'
788
+ # end
789
+ # end
790
+ # end
791
+ #
792
+ # In the above case whenever you save an account the method
793
+ # <tt>Audit#before</tt> will be called. On the other hand
794
+ #
795
+ # define_callbacks :save, scope: [:kind, :name]
796
+ #
797
+ # would trigger <tt>Audit#before_save</tt> instead. That's constructed
798
+ # by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
799
+ # case "kind" is "before" and "name" is "save". In this context +:kind+
800
+ # and +:name+ have special meanings: +:kind+ refers to the kind of
801
+ # callback (before/after/around) and +:name+ refers to the method on
802
+ # which callbacks are being defined.
803
+ #
804
+ # A declaration like
805
+ #
806
+ # define_callbacks :save, scope: [:name]
807
+ #
808
+ # would call <tt>Audit#save</tt>.
809
+ #
810
+ # ===== Notes
811
+ #
812
+ # +names+ passed to +define_callbacks+ must not end with
813
+ # <tt>!</tt>, <tt>?</tt> or <tt>=</tt>.
814
+ #
815
+ # Calling +define_callbacks+ multiple times with the same +names+ will
816
+ # overwrite previous callbacks registered with +set_callback+.
817
+ def define_callbacks(*names)
818
+ options = names.extract_options!
819
+
820
+ names.each do |name|
821
+ name = name.to_sym
822
+
823
+ set_callbacks name, CallbackChain.new(name, options)
824
+
825
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
826
+ def _run_#{name}_callbacks(&block)
827
+ run_callbacks #{name.inspect}, &block
828
+ end
829
+
830
+ def self._#{name}_callbacks
831
+ get_callbacks(#{name.inspect})
832
+ end
833
+
834
+ def self._#{name}_callbacks=(value)
835
+ set_callbacks(#{name.inspect}, value)
836
+ end
837
+
838
+ def _#{name}_callbacks
839
+ __callbacks[#{name.inspect}]
840
+ end
841
+ RUBY
776
842
  end
777
- terminate
778
843
  end
779
- end
780
844
 
781
- private
845
+ protected
846
+
847
+ def get_callbacks(name) # :nodoc:
848
+ __callbacks[name.to_sym]
849
+ end
782
850
 
783
- def display_deprecation_warning_for_false_terminator
784
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
785
- Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1.
786
- To explicitly halt the callback chain, please use `throw :abort` instead.
787
- MSG
851
+ def set_callbacks(name, callbacks) # :nodoc:
852
+ self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
853
+ end
788
854
  end
789
- end
790
855
  end
791
856
  end