activesupport 3.2.22.5 → 4.0.0.beta1

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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -136
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/active_support.rb +8 -21
  6. data/lib/active_support/backtrace_cleaner.rb +33 -25
  7. data/lib/active_support/basic_object.rb +7 -17
  8. data/lib/active_support/benchmarkable.rb +19 -15
  9. data/lib/active_support/buffered_logger.rb +9 -113
  10. data/lib/active_support/cache.rb +203 -171
  11. data/lib/active_support/cache/file_store.rb +12 -12
  12. data/lib/active_support/cache/mem_cache_store.rb +24 -30
  13. data/lib/active_support/cache/memory_store.rb +2 -0
  14. data/lib/active_support/callbacks.rb +195 -247
  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.rb +1 -0
  19. data/lib/active_support/core_ext/array.rb +0 -1
  20. data/lib/active_support/core_ext/array/access.rb +17 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +113 -55
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +21 -22
  24. data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
  25. data/lib/active_support/core_ext/array/wrap.rb +11 -14
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +7 -24
  27. data/lib/active_support/core_ext/class/attribute.rb +12 -8
  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.rb +6 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +34 -188
  33. data/lib/active_support/core_ext/date/conversions.rb +16 -38
  34. data/lib/active_support/core_ext/date/infinite_comparable.rb +5 -0
  35. data/lib/active_support/core_ext/date/zones.rb +25 -2
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
  37. data/lib/active_support/core_ext/date_time.rb +5 -0
  38. data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
  39. data/lib/active_support/core_ext/date_time/calculations.rb +73 -65
  40. data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
  41. data/lib/active_support/core_ext/date_time/infinite_comparable.rb +5 -0
  42. data/lib/active_support/core_ext/date_time/zones.rb +11 -8
  43. data/lib/active_support/core_ext/enumerable.rb +26 -73
  44. data/lib/active_support/core_ext/file.rb +0 -1
  45. data/lib/active_support/core_ext/file/atomic.rb +27 -11
  46. data/lib/active_support/core_ext/hash.rb +0 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +145 -79
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
  49. data/lib/active_support/core_ext/hash/diff.rb +5 -4
  50. data/lib/active_support/core_ext/hash/except.rb +1 -9
  51. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
  52. data/lib/active_support/core_ext/hash/keys.rb +108 -24
  53. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  54. data/lib/active_support/core_ext/hash/slice.rb +12 -12
  55. data/lib/active_support/core_ext/infinite_comparable.rb +35 -0
  56. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  57. data/lib/active_support/core_ext/integer/time.rb +17 -12
  58. data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
  59. data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
  60. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  61. data/lib/active_support/core_ext/load_error.rb +7 -5
  62. data/lib/active_support/core_ext/logger.rb +7 -23
  63. data/lib/active_support/core_ext/marshal.rb +19 -0
  64. data/lib/active_support/core_ext/module.rb +1 -3
  65. data/lib/active_support/core_ext/module/aliasing.rb +8 -9
  66. data/lib/active_support/core_ext/module/anonymous.rb +2 -7
  67. data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
  68. data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
  69. data/lib/active_support/core_ext/module/delegation.rb +57 -40
  70. data/lib/active_support/core_ext/module/deprecation.rb +19 -3
  71. data/lib/active_support/core_ext/module/introspection.rb +17 -27
  72. data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
  73. data/lib/active_support/core_ext/module/remove_method.rb +1 -5
  74. data/lib/active_support/core_ext/numeric.rb +2 -0
  75. data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
  76. data/lib/active_support/core_ext/numeric/infinite_comparable.rb +9 -0
  77. data/lib/active_support/core_ext/numeric/time.rb +6 -6
  78. data/lib/active_support/core_ext/object.rb +1 -0
  79. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  80. data/lib/active_support/core_ext/object/blank.rb +7 -23
  81. data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
  82. data/lib/active_support/core_ext/object/duplicable.rb +1 -30
  83. data/lib/active_support/core_ext/object/inclusion.rb +6 -6
  84. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  85. data/lib/active_support/core_ext/object/to_json.rb +8 -0
  86. data/lib/active_support/core_ext/object/to_param.rb +5 -2
  87. data/lib/active_support/core_ext/object/try.rb +46 -25
  88. data/lib/active_support/core_ext/object/with_options.rb +7 -8
  89. data/lib/active_support/core_ext/proc.rb +3 -0
  90. data/lib/active_support/core_ext/range.rb +0 -2
  91. data/lib/active_support/core_ext/range/conversions.rb +0 -2
  92. data/lib/active_support/core_ext/range/include_range.rb +1 -1
  93. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  94. data/lib/active_support/core_ext/string.rb +2 -2
  95. data/lib/active_support/core_ext/string/access.rb +95 -90
  96. data/lib/active_support/core_ext/string/conversions.rb +29 -38
  97. data/lib/active_support/core_ext/string/encoding.rb +6 -9
  98. data/lib/active_support/core_ext/string/filters.rb +24 -18
  99. data/lib/active_support/core_ext/string/indent.rb +43 -0
  100. data/lib/active_support/core_ext/string/inflections.rb +70 -60
  101. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  102. data/lib/active_support/core_ext/string/multibyte.rb +41 -64
  103. data/lib/active_support/core_ext/string/output_safety.rb +59 -51
  104. data/lib/active_support/core_ext/string/zones.rb +13 -0
  105. data/lib/active_support/core_ext/struct.rb +6 -0
  106. data/lib/active_support/core_ext/thread.rb +74 -0
  107. data/lib/active_support/core_ext/time.rb +6 -0
  108. data/lib/active_support/core_ext/time/calculations.rb +105 -193
  109. data/lib/active_support/core_ext/time/conversions.rb +27 -51
  110. data/lib/active_support/core_ext/time/infinite_comparable.rb +5 -0
  111. data/lib/active_support/core_ext/time/marshal.rb +0 -27
  112. data/lib/active_support/core_ext/time/zones.rb +27 -17
  113. data/lib/active_support/core_ext/uri.rb +13 -17
  114. data/lib/active_support/dependencies.rb +160 -141
  115. data/lib/active_support/dependencies/autoload.rb +47 -20
  116. data/lib/active_support/deprecation.rb +39 -14
  117. data/lib/active_support/deprecation/behaviors.rb +44 -30
  118. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  119. data/lib/active_support/deprecation/method_wrappers.rb +33 -18
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
  121. data/lib/active_support/deprecation/reporting.rb +40 -11
  122. data/lib/active_support/descendants_tracker.rb +34 -19
  123. data/lib/active_support/duration.rb +6 -8
  124. data/lib/active_support/file_update_checker.rb +63 -47
  125. data/lib/active_support/gzip.rb +11 -5
  126. data/lib/active_support/hash_with_indifferent_access.rb +112 -37
  127. data/lib/active_support/i18n.rb +4 -0
  128. data/lib/active_support/i18n_railtie.rb +5 -22
  129. data/lib/active_support/inflections.rb +14 -12
  130. data/lib/active_support/inflector/inflections.rb +108 -71
  131. data/lib/active_support/inflector/methods.rb +181 -160
  132. data/lib/active_support/inflector/transliterate.rb +16 -17
  133. data/lib/active_support/json/decoding.rb +18 -17
  134. data/lib/active_support/json/encoding.rb +93 -39
  135. data/lib/active_support/json/variable.rb +10 -1
  136. data/lib/active_support/key_generator.rb +75 -0
  137. data/lib/active_support/lazy_load_hooks.rb +21 -19
  138. data/lib/active_support/locale/en.yml +100 -3
  139. data/lib/active_support/log_subscriber.rb +56 -36
  140. data/lib/active_support/log_subscriber/test_helper.rb +18 -15
  141. data/lib/active_support/logger.rb +57 -0
  142. data/lib/active_support/logger_silence.rb +24 -0
  143. data/lib/active_support/message_encryptor.rb +32 -29
  144. data/lib/active_support/message_verifier.rb +8 -14
  145. data/lib/active_support/multibyte.rb +5 -28
  146. data/lib/active_support/multibyte/chars.rb +80 -333
  147. data/lib/active_support/multibyte/unicode.rb +74 -64
  148. data/lib/active_support/notifications.rb +57 -25
  149. data/lib/active_support/notifications/fanout.rb +105 -18
  150. data/lib/active_support/notifications/instrumenter.rb +32 -13
  151. data/lib/active_support/number_helper.rb +636 -0
  152. data/lib/active_support/ordered_hash.rb +8 -190
  153. data/lib/active_support/ordered_options.rb +21 -23
  154. data/lib/active_support/proxy_object.rb +13 -0
  155. data/lib/active_support/rails.rb +27 -0
  156. data/lib/active_support/railtie.rb +12 -32
  157. data/lib/active_support/rescuable.rb +9 -4
  158. data/lib/active_support/string_inquirer.rb +13 -8
  159. data/lib/active_support/tagged_logging.rb +51 -73
  160. data/lib/active_support/test_case.rb +46 -17
  161. data/lib/active_support/testing/assertions.rb +56 -26
  162. data/lib/active_support/testing/autorun.rb +5 -0
  163. data/lib/active_support/testing/constant_lookup.rb +52 -0
  164. data/lib/active_support/testing/declarative.rb +1 -1
  165. data/lib/active_support/testing/deprecation.rb +0 -19
  166. data/lib/active_support/testing/isolation.rb +25 -58
  167. data/lib/active_support/testing/pending.rb +5 -43
  168. data/lib/active_support/testing/setup_and_teardown.rb +6 -92
  169. data/lib/active_support/testing/tagged_logging.rb +25 -0
  170. data/lib/active_support/time.rb +6 -21
  171. data/lib/active_support/time_with_zone.rb +78 -43
  172. data/lib/active_support/values/time_zone.rb +77 -58
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/version.rb +4 -4
  175. data/lib/active_support/xml_mini.rb +35 -17
  176. data/lib/active_support/xml_mini/jdom.rb +9 -17
  177. data/lib/active_support/xml_mini/libxml.rb +1 -2
  178. data/lib/active_support/xml_mini/libxmlsax.rb +1 -2
  179. data/lib/active_support/xml_mini/nokogiri.rb +1 -2
  180. data/lib/active_support/xml_mini/nokogirisax.rb +1 -2
  181. data/lib/active_support/xml_mini/rexml.rb +6 -8
  182. metadata +107 -77
  183. data/lib/active_support/base64.rb +0 -54
  184. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  185. data/lib/active_support/core_ext/date/freeze.rb +0 -33
  186. data/lib/active_support/core_ext/exception.rb +0 -3
  187. data/lib/active_support/core_ext/file/path.rb +0 -5
  188. data/lib/active_support/core_ext/float.rb +0 -1
  189. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  190. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
  191. data/lib/active_support/core_ext/io.rb +0 -15
  192. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  193. data/lib/active_support/core_ext/module/synchronization.rb +0 -45
  194. data/lib/active_support/core_ext/process.rb +0 -1
  195. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  196. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  197. data/lib/active_support/core_ext/range/cover.rb +0 -3
  198. data/lib/active_support/core_ext/rexml.rb +0 -46
  199. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  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.rb +0 -317
  208. data/lib/active_support/testing/performance/jruby.rb +0 -115
  209. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  210. data/lib/active_support/testing/performance/ruby.rb +0 -152
  211. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  212. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  213. data/lib/active_support/time/autoload.rb +0 -5
  214. data/lib/active_support/whiny_nil.rb +0 -24
@@ -1,7 +1,7 @@
1
+ require 'active_support/core_ext/marshal'
1
2
  require 'active_support/core_ext/file/atomic'
2
3
  require 'active_support/core_ext/string/conversions'
3
- require 'active_support/core_ext/object/inclusion'
4
- require 'rack/utils'
4
+ require 'uri/common'
5
5
 
6
6
  module ActiveSupport
7
7
  module Cache
@@ -13,7 +13,7 @@ module ActiveSupport
13
13
  attr_reader :cache_path
14
14
 
15
15
  DIR_FORMATTER = "%03X"
16
- FILENAME_MAX_SIZE = 230 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
16
+ FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
17
17
  EXCLUDED_DIRS = ['.', '..'].freeze
18
18
 
19
19
  def initialize(cache_path, options = nil)
@@ -23,14 +23,13 @@ module ActiveSupport
23
23
  end
24
24
 
25
25
  def clear(options = nil)
26
- root_dirs = Dir.entries(cache_path).reject{|f| f.in?(EXCLUDED_DIRS)}
26
+ root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
27
27
  FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
28
28
  end
29
29
 
30
30
  def cleanup(options = nil)
31
31
  options = merged_options(options)
32
- search_dir(cache_path) do |fname|
33
- key = file_path_key(fname)
32
+ each_key(options) do |key|
34
33
  entry = read_entry(key, options)
35
34
  delete_entry(key, options) if entry && entry.expired?
36
35
  end
@@ -82,7 +81,8 @@ module ActiveSupport
82
81
  if File.exist?(file_name)
83
82
  File.open(file_name) { |f| Marshal.load(f) }
84
83
  end
85
- rescue
84
+ rescue => e
85
+ logger.error("FileStoreError (#{e}): #{e.message}") if logger
86
86
  nil
87
87
  end
88
88
 
@@ -127,7 +127,7 @@ module ActiveSupport
127
127
 
128
128
  # Translate a key into a file path.
129
129
  def key_file_path(key)
130
- fname = Rack::Utils.escape(key)
130
+ fname = URI.encode_www_form_component(key)
131
131
  hash = Zlib.adler32(fname)
132
132
  hash, dir_1 = hash.divmod(0x1000)
133
133
  dir_2 = hash.modulo(0x1000)
@@ -144,14 +144,14 @@ module ActiveSupport
144
144
 
145
145
  # Translate a file path into a key.
146
146
  def file_path_key(path)
147
- fname = path[cache_path.size, path.size].split(File::SEPARATOR, 4).last
148
- Rack::Utils.unescape(fname)
147
+ fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
148
+ URI.decode_www_form_component(fname, Encoding::UTF_8)
149
149
  end
150
150
 
151
151
  # Delete empty directories in the cache.
152
152
  def delete_empty_directories(dir)
153
153
  return if dir == cache_path
154
- if Dir.entries(dir).reject{|f| f.in?(EXCLUDED_DIRS)}.empty?
154
+ if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
155
155
  File.delete(dir) rescue nil
156
156
  delete_empty_directories(File.dirname(dir))
157
157
  end
@@ -165,7 +165,7 @@ module ActiveSupport
165
165
  def search_dir(dir, &callback)
166
166
  return if !File.exist?(dir)
167
167
  Dir.foreach(dir) do |d|
168
- next if d.in?(EXCLUDED_DIRS)
168
+ next if EXCLUDED_DIRS.include?(d)
169
169
  name = File.join(dir, d)
170
170
  if File.directory?(name)
171
171
  search_dir(name, &callback)
@@ -1,12 +1,13 @@
1
1
  begin
2
- require 'memcache'
2
+ require 'dalli'
3
3
  rescue LoadError => e
4
- $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
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
8
  require 'digest/md5'
9
- require 'active_support/core_ext/string/encoding'
9
+ require 'active_support/core_ext/marshal'
10
+ require 'active_support/core_ext/array/extract_options'
10
11
 
11
12
  module ActiveSupport
12
13
  module Cache
@@ -23,21 +24,13 @@ module ActiveSupport
23
24
  # MemCacheStore implements the Strategy::LocalCache strategy which implements
24
25
  # an in-memory cache inside of a block.
25
26
  class MemCacheStore < Store
26
- module Response # :nodoc:
27
- STORED = "STORED\r\n"
28
- NOT_STORED = "NOT_STORED\r\n"
29
- EXISTS = "EXISTS\r\n"
30
- NOT_FOUND = "NOT_FOUND\r\n"
31
- DELETED = "DELETED\r\n"
32
- end
33
-
34
27
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
35
28
 
36
29
  def self.build_mem_cache(*addresses)
37
30
  addresses = addresses.flatten
38
31
  options = addresses.extract_options!
39
32
  addresses = ["localhost:11211"] if addresses.empty?
40
- MemCache.new(addresses, options)
33
+ Dalli::Client.new(addresses, options)
41
34
  end
42
35
 
43
36
  # Creates a new MemCacheStore object, with the given memcached server
@@ -91,11 +84,11 @@ module ActiveSupport
91
84
  # to zero.
92
85
  def increment(name, amount = 1, options = nil) # :nodoc:
93
86
  options = merged_options(options)
94
- response = instrument(:increment, name, :amount => amount) do
87
+ instrument(:increment, name, :amount => amount) do
95
88
  @data.incr(escape_key(namespaced_key(name, options)), amount)
96
89
  end
97
- response == Response::NOT_FOUND ? nil : response.to_i
98
- rescue MemCache::MemCacheError
90
+ rescue Dalli::DalliError
91
+ logger.error("DalliError (#{e}): #{e.message}") if logger
99
92
  nil
100
93
  end
101
94
 
@@ -105,11 +98,11 @@ module ActiveSupport
105
98
  # to zero.
106
99
  def decrement(name, amount = 1, options = nil) # :nodoc:
107
100
  options = merged_options(options)
108
- response = instrument(:decrement, name, :amount => amount) do
101
+ instrument(:decrement, name, :amount => amount) do
109
102
  @data.decr(escape_key(namespaced_key(name, options)), amount)
110
103
  end
111
- response == Response::NOT_FOUND ? nil : response.to_i
112
- rescue MemCache::MemCacheError
104
+ rescue Dalli::DalliError
105
+ logger.error("DalliError (#{e}): #{e.message}") if logger
113
106
  nil
114
107
  end
115
108
 
@@ -117,6 +110,9 @@ module ActiveSupport
117
110
  # be used with care when shared cache is being used.
118
111
  def clear(options = nil)
119
112
  @data.flush_all
113
+ rescue Dalli::DalliError => e
114
+ logger.error("DalliError (#{e}): #{e.message}") if logger
115
+ nil
120
116
  end
121
117
 
122
118
  # Get the statistics from the memcached servers.
@@ -127,9 +123,9 @@ module ActiveSupport
127
123
  protected
128
124
  # Read an entry from the cache.
129
125
  def read_entry(key, options) # :nodoc:
130
- deserialize_entry(@data.get(escape_key(key), true))
131
- rescue MemCache::MemCacheError => e
132
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
126
+ deserialize_entry(@data.get(escape_key(key), options))
127
+ rescue Dalli::DalliError => e
128
+ logger.error("DalliError (#{e}): #{e.message}") if logger
133
129
  nil
134
130
  end
135
131
 
@@ -142,19 +138,17 @@ module ActiveSupport
142
138
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
143
139
  expires_in += 5.minutes
144
140
  end
145
- response = @data.send(method, escape_key(key), value, expires_in, options[:raw])
146
- response == Response::STORED
147
- rescue MemCache::MemCacheError => e
148
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
141
+ @data.send(method, escape_key(key), value, expires_in, options)
142
+ rescue Dalli::DalliError => e
143
+ logger.error("DalliError (#{e}): #{e.message}") if logger
149
144
  false
150
145
  end
151
146
 
152
147
  # Delete an entry from the cache.
153
148
  def delete_entry(key, options) # :nodoc:
154
- response = @data.delete(escape_key(key))
155
- response == Response::DELETED
156
- rescue MemCache::MemCacheError => e
157
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
149
+ @data.delete(escape_key(key))
150
+ rescue Dalli::DalliError => e
151
+ logger.error("DalliError (#{e}): #{e.message}") if logger
158
152
  false
159
153
  end
160
154
 
@@ -165,7 +159,7 @@ module ActiveSupport
165
159
  # characters properly.
166
160
  def escape_key(key)
167
161
  key = key.to_s.dup
168
- key = key.force_encoding("BINARY") if key.encoding_aware?
162
+ key = key.force_encoding(Encoding::ASCII_8BIT)
169
163
  key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
170
164
  key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
171
165
  key
@@ -135,8 +135,10 @@ module ActiveSupport
135
135
  end
136
136
 
137
137
  def write_entry(key, entry, options) # :nodoc:
138
+ entry.dup_value!
138
139
  synchronize do
139
140
  old_entry = @data[key]
141
+ return false if @data.key?(key) && options[:unless_exist]
140
142
  @cache_size -= old_entry.size if old_entry
141
143
  @cache_size += entry.size
142
144
  @key_access[key] = Time.now.to_f
@@ -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
 
@@ -66,25 +64,24 @@ module ActiveSupport
66
64
  # Runs the callbacks for the given event.
67
65
  #
68
66
  # 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.
67
+ # the block (if given one), and then runs the after callbacks in reverse
68
+ # order.
72
69
  #
73
- # If the callback chain was halted, returns +false+. Otherwise returns the result
74
- # of the block, or +true+ if no block is given.
70
+ # If the callback chain was halted, returns +false+. Otherwise returns the
71
+ # result of the block, or +true+ if no block is given.
75
72
  #
76
73
  # run_callbacks :save do
77
74
  # save
78
75
  # end
79
- #
80
- def run_callbacks(kind, *args, &block)
81
- send("_run_#{kind}_callbacks", *args, &block)
76
+ def run_callbacks(kind, &block)
77
+ runner_name = self.class.__define_callbacks(kind, self)
78
+ send(runner_name, &block)
82
79
  end
83
80
 
84
81
  private
85
82
 
86
83
  # A hook invoked everytime a before callback is halted.
87
- # This can be overriden in AS::Callback implementors in order
84
+ # This can be overridden in AS::Callback implementors in order
88
85
  # to provide better debugging/logging.
89
86
  def halted_callback_hook(filter)
90
87
  end
@@ -92,41 +89,37 @@ module ActiveSupport
92
89
  class Callback #:nodoc:#
93
90
  @@_callback_sequence = 0
94
91
 
95
- attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
92
+ attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
96
93
 
97
94
  def initialize(chain, filter, kind, options, klass)
98
95
  @chain, @kind, @klass = chain, kind, klass
96
+ deprecate_per_key_option(options)
99
97
  normalize_options!(options)
100
98
 
101
- @per_key = options.delete(:per_key)
102
99
  @raw_filter, @options = filter, options
103
100
  @filter = _compile_filter(filter)
104
- @compiled_options = _compile_options(options)
105
- @callback_id = next_id
101
+ recompile_options!
102
+ end
106
103
 
107
- _compile_per_key_options
104
+ def deprecate_per_key_option(options)
105
+ if options[:per_key]
106
+ raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
107
+ end
108
108
  end
109
109
 
110
110
  def clone(chain, klass)
111
111
  obj = super()
112
112
  obj.chain = chain
113
113
  obj.klass = klass
114
- obj.per_key = @per_key.dup
115
114
  obj.options = @options.dup
116
- obj.per_key[:if] = @per_key[:if].dup
117
- obj.per_key[:unless] = @per_key[:unless].dup
118
115
  obj.options[:if] = @options[:if].dup
119
116
  obj.options[:unless] = @options[:unless].dup
120
117
  obj
121
118
  end
122
119
 
123
120
  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])
121
+ options[:if] = Array(options[:if])
122
+ options[:unless] = Array(options[:unless])
130
123
  end
131
124
 
132
125
  def name
@@ -141,44 +134,26 @@ module ActiveSupport
141
134
  @kind == _kind && @filter == _filter
142
135
  end
143
136
 
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)
137
+ def duplicates?(other)
138
+ matches?(other.kind, other.filter)
147
139
  end
148
140
 
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
141
+ def _update_filter(filter_options, new_options)
142
+ filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
143
+ filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
157
144
  end
158
145
 
159
- def _compile_per_key_options
160
- key_options = _compile_options(@per_key)
146
+ def recompile!(_options)
147
+ deprecate_per_key_option(_options)
148
+ _update_filter(self.options, _options)
161
149
 
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
150
+ recompile_options!
167
151
  end
168
152
 
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
- #
153
+ # Wraps code with filter
154
+ def apply(code)
177
155
  case @kind
178
156
  when :before
179
- # if condition # before_save :filter_name, :if => :condition
180
- # filter_name
181
- # end
182
157
  <<-RUBY_EVAL
183
158
  if !halted && #{@compiled_options}
184
159
  # This double assignment is to prevent warnings in 1.9.3 as
@@ -190,54 +165,20 @@ module ActiveSupport
190
165
  halted_callback_hook(#{@raw_filter.inspect.inspect})
191
166
  end
192
167
  end
168
+ #{code}
193
169
  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
170
  when :after
233
- # after_save :filter_name, :if => :condition
234
171
  <<-RUBY_EVAL
235
- if #{@compiled_options}
172
+ #{code}
173
+ if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
236
174
  #{@filter}
237
175
  end
238
176
  RUBY_EVAL
239
177
  when :around
178
+ name = define_conditional_callback
240
179
  <<-RUBY_EVAL
180
+ #{name}(halted) do
181
+ #{code}
241
182
  value
242
183
  end
243
184
  RUBY_EVAL
@@ -246,44 +187,73 @@ module ActiveSupport
246
187
 
247
188
  private
248
189
 
190
+ # Compile around filters with conditions into proxy methods
191
+ # that contain the conditions.
192
+ #
193
+ # For `set_callback :save, :around, :filter_name, if: :condition':
194
+ #
195
+ # def _conditional_callback_save_17
196
+ # if condition
197
+ # filter_name do
198
+ # yield self
199
+ # end
200
+ # else
201
+ # yield self
202
+ # end
203
+ # end
204
+ def define_conditional_callback
205
+ name = "_conditional_callback_#{@kind}_#{next_id}"
206
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
207
+ def #{name}(halted)
208
+ if #{@compiled_options} && !halted
209
+ #{@filter} do
210
+ yield self
211
+ end
212
+ else
213
+ yield self
214
+ end
215
+ end
216
+ RUBY_EVAL
217
+ name
218
+ end
219
+
249
220
  # Options support the same options as filters themselves (and support
250
221
  # symbols, string, procs, and objects), so compile a conditional
251
- # expression based on the options
252
- def _compile_options(options)
222
+ # expression based on the options.
223
+ def recompile_options!
253
224
  conditions = ["true"]
254
225
 
255
226
  unless options[:if].empty?
256
- conditions << Array.wrap(_compile_filter(options[:if]))
227
+ conditions << Array(_compile_filter(options[:if]))
257
228
  end
258
229
 
259
230
  unless options[:unless].empty?
260
- conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
231
+ conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
261
232
  end
262
233
 
263
- conditions.flatten.join(" && ")
234
+ @compiled_options = conditions.flatten.join(" && ")
264
235
  end
265
236
 
266
237
  # Filters support:
267
238
  #
268
239
  # Arrays:: Used in conditions. This is used to specify
269
240
  # 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
241
+ # merge conditions from skip_* filters.
242
+ # Symbols:: A method to call.
243
+ # Strings:: Some content to evaluate.
244
+ # Procs:: A proc to call with the object.
245
+ # Objects:: An object with a <tt>before_foo</tt> method on it to call.
275
246
  #
276
247
  # All of these objects are compiled into methods and handled
277
248
  # the same after this point:
278
249
  #
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
250
+ # Arrays:: Merged together into a single filter.
251
+ # Symbols:: Already methods.
252
+ # Strings:: class_eval'ed into methods.
253
+ # Procs:: define_method'ed into methods.
283
254
  # Objects::
284
255
  # a method is created that calls the before_foo method
285
256
  # on the object.
286
- #
287
257
  def _compile_filter(filter)
288
258
  method_name = "_callback_#{@kind}_#{next_id}"
289
259
  case filter
@@ -302,7 +272,7 @@ module ActiveSupport
302
272
  @klass.send(:define_method, "#{method_name}_object") { filter }
303
273
 
304
274
  _normalize_legacy_filter(kind, filter)
305
- scopes = Array.wrap(chain.config[:scope])
275
+ scopes = Array(chain.config[:scope])
306
276
  method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
307
277
 
308
278
  @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
@@ -317,10 +287,15 @@ module ActiveSupport
317
287
 
318
288
  def _normalize_legacy_filter(kind, filter)
319
289
  if !filter.respond_to?(kind) && filter.respond_to?(:filter)
290
+ message = "Filter object with #filter method is deprecated. Define method corresponding " \
291
+ "to filter type (#before, #after or #around)."
292
+ ActiveSupport::Deprecation.warn message
320
293
  filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
321
294
  def #{kind}(context, &block) filter(context, &block) end
322
295
  RUBY_EVAL
323
- elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
296
+ elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
297
+ message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
298
+ ActiveSupport::Deprecation.warn message
324
299
  def filter.around(context)
325
300
  should_continue = before(context)
326
301
  yield if should_continue
@@ -330,7 +305,7 @@ module ActiveSupport
330
305
  end
331
306
  end
332
307
 
333
- # An Array with a compile method
308
+ # An Array with a compile method.
334
309
  class CallbackChain < Array #:nodoc:#
335
310
  attr_reader :name, :config
336
311
 
@@ -338,87 +313,89 @@ module ActiveSupport
338
313
  @name = name
339
314
  @config = {
340
315
  :terminator => "false",
341
- :rescuable => false,
342
316
  :scope => [ :kind ]
343
317
  }.merge(config)
344
318
  end
345
319
 
346
- def compile(key=nil, object=nil)
320
+ def compile
347
321
  method = []
348
322
  method << "value = nil"
349
323
  method << "halted = false"
350
324
 
351
- each do |callback|
352
- method << callback.start(key, object)
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 = [:before, :after, :around].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