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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +325 -136
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -2
- data/lib/active_support.rb +8 -21
- data/lib/active_support/backtrace_cleaner.rb +33 -25
- data/lib/active_support/basic_object.rb +7 -17
- data/lib/active_support/benchmarkable.rb +19 -15
- data/lib/active_support/buffered_logger.rb +9 -113
- data/lib/active_support/cache.rb +203 -171
- data/lib/active_support/cache/file_store.rb +12 -12
- data/lib/active_support/cache/mem_cache_store.rb +24 -30
- data/lib/active_support/cache/memory_store.rb +2 -0
- data/lib/active_support/callbacks.rb +195 -247
- data/lib/active_support/concern.rb +16 -23
- data/lib/active_support/concurrency/latch.rb +27 -0
- data/lib/active_support/configurable.rb +69 -12
- data/lib/active_support/core_ext.rb +1 -0
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/array/access.rb +17 -9
- data/lib/active_support/core_ext/array/conversions.rb +113 -55
- data/lib/active_support/core_ext/array/extract_options.rb +2 -2
- data/lib/active_support/core_ext/array/grouping.rb +21 -22
- data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
- data/lib/active_support/core_ext/array/wrap.rb +11 -14
- data/lib/active_support/core_ext/big_decimal/conversions.rb +7 -24
- data/lib/active_support/core_ext/class/attribute.rb +12 -8
- data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
- data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
- data/lib/active_support/core_ext/class/subclasses.rb +11 -5
- data/lib/active_support/core_ext/date.rb +6 -0
- data/lib/active_support/core_ext/date/calculations.rb +34 -188
- data/lib/active_support/core_ext/date/conversions.rb +16 -38
- data/lib/active_support/core_ext/date/infinite_comparable.rb +5 -0
- data/lib/active_support/core_ext/date/zones.rb +25 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
- data/lib/active_support/core_ext/date_time.rb +5 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +73 -65
- data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
- data/lib/active_support/core_ext/date_time/infinite_comparable.rb +5 -0
- data/lib/active_support/core_ext/date_time/zones.rb +11 -8
- data/lib/active_support/core_ext/enumerable.rb +26 -73
- data/lib/active_support/core_ext/file.rb +0 -1
- data/lib/active_support/core_ext/file/atomic.rb +27 -11
- data/lib/active_support/core_ext/hash.rb +0 -1
- data/lib/active_support/core_ext/hash/conversions.rb +145 -79
- data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
- data/lib/active_support/core_ext/hash/diff.rb +5 -4
- data/lib/active_support/core_ext/hash/except.rb +1 -9
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
- data/lib/active_support/core_ext/hash/keys.rb +108 -24
- data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
- data/lib/active_support/core_ext/hash/slice.rb +12 -12
- data/lib/active_support/core_ext/infinite_comparable.rb +35 -0
- data/lib/active_support/core_ext/integer/inflections.rb +13 -1
- data/lib/active_support/core_ext/integer/time.rb +17 -12
- data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
- data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
- data/lib/active_support/core_ext/load_error.rb +7 -5
- data/lib/active_support/core_ext/logger.rb +7 -23
- data/lib/active_support/core_ext/marshal.rb +19 -0
- data/lib/active_support/core_ext/module.rb +1 -3
- data/lib/active_support/core_ext/module/aliasing.rb +8 -9
- data/lib/active_support/core_ext/module/anonymous.rb +2 -7
- data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
- data/lib/active_support/core_ext/module/delegation.rb +57 -40
- data/lib/active_support/core_ext/module/deprecation.rb +19 -3
- data/lib/active_support/core_ext/module/introspection.rb +17 -27
- data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
- data/lib/active_support/core_ext/module/remove_method.rb +1 -5
- data/lib/active_support/core_ext/numeric.rb +2 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
- data/lib/active_support/core_ext/numeric/infinite_comparable.rb +9 -0
- data/lib/active_support/core_ext/numeric/time.rb +6 -6
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/object/acts_like.rb +4 -4
- data/lib/active_support/core_ext/object/blank.rb +7 -23
- data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
- data/lib/active_support/core_ext/object/duplicable.rb +1 -30
- data/lib/active_support/core_ext/object/inclusion.rb +6 -6
- data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
- data/lib/active_support/core_ext/object/to_json.rb +8 -0
- data/lib/active_support/core_ext/object/to_param.rb +5 -2
- data/lib/active_support/core_ext/object/try.rb +46 -25
- data/lib/active_support/core_ext/object/with_options.rb +7 -8
- data/lib/active_support/core_ext/proc.rb +3 -0
- data/lib/active_support/core_ext/range.rb +0 -2
- data/lib/active_support/core_ext/range/conversions.rb +0 -2
- data/lib/active_support/core_ext/range/include_range.rb +1 -1
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/string.rb +2 -2
- data/lib/active_support/core_ext/string/access.rb +95 -90
- data/lib/active_support/core_ext/string/conversions.rb +29 -38
- data/lib/active_support/core_ext/string/encoding.rb +6 -9
- data/lib/active_support/core_ext/string/filters.rb +24 -18
- data/lib/active_support/core_ext/string/indent.rb +43 -0
- data/lib/active_support/core_ext/string/inflections.rb +70 -60
- data/lib/active_support/core_ext/string/inquiry.rb +2 -2
- data/lib/active_support/core_ext/string/multibyte.rb +41 -64
- data/lib/active_support/core_ext/string/output_safety.rb +59 -51
- data/lib/active_support/core_ext/string/zones.rb +13 -0
- data/lib/active_support/core_ext/struct.rb +6 -0
- data/lib/active_support/core_ext/thread.rb +74 -0
- data/lib/active_support/core_ext/time.rb +6 -0
- data/lib/active_support/core_ext/time/calculations.rb +105 -193
- data/lib/active_support/core_ext/time/conversions.rb +27 -51
- data/lib/active_support/core_ext/time/infinite_comparable.rb +5 -0
- data/lib/active_support/core_ext/time/marshal.rb +0 -27
- data/lib/active_support/core_ext/time/zones.rb +27 -17
- data/lib/active_support/core_ext/uri.rb +13 -17
- data/lib/active_support/dependencies.rb +160 -141
- data/lib/active_support/dependencies/autoload.rb +47 -20
- data/lib/active_support/deprecation.rb +39 -14
- data/lib/active_support/deprecation/behaviors.rb +44 -30
- data/lib/active_support/deprecation/instance_delegator.rb +24 -0
- data/lib/active_support/deprecation/method_wrappers.rb +33 -18
- data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
- data/lib/active_support/deprecation/reporting.rb +40 -11
- data/lib/active_support/descendants_tracker.rb +34 -19
- data/lib/active_support/duration.rb +6 -8
- data/lib/active_support/file_update_checker.rb +63 -47
- data/lib/active_support/gzip.rb +11 -5
- data/lib/active_support/hash_with_indifferent_access.rb +112 -37
- data/lib/active_support/i18n.rb +4 -0
- data/lib/active_support/i18n_railtie.rb +5 -22
- data/lib/active_support/inflections.rb +14 -12
- data/lib/active_support/inflector/inflections.rb +108 -71
- data/lib/active_support/inflector/methods.rb +181 -160
- data/lib/active_support/inflector/transliterate.rb +16 -17
- data/lib/active_support/json/decoding.rb +18 -17
- data/lib/active_support/json/encoding.rb +93 -39
- data/lib/active_support/json/variable.rb +10 -1
- data/lib/active_support/key_generator.rb +75 -0
- data/lib/active_support/lazy_load_hooks.rb +21 -19
- data/lib/active_support/locale/en.yml +100 -3
- data/lib/active_support/log_subscriber.rb +56 -36
- data/lib/active_support/log_subscriber/test_helper.rb +18 -15
- data/lib/active_support/logger.rb +57 -0
- data/lib/active_support/logger_silence.rb +24 -0
- data/lib/active_support/message_encryptor.rb +32 -29
- data/lib/active_support/message_verifier.rb +8 -14
- data/lib/active_support/multibyte.rb +5 -28
- data/lib/active_support/multibyte/chars.rb +80 -333
- data/lib/active_support/multibyte/unicode.rb +74 -64
- data/lib/active_support/notifications.rb +57 -25
- data/lib/active_support/notifications/fanout.rb +105 -18
- data/lib/active_support/notifications/instrumenter.rb +32 -13
- data/lib/active_support/number_helper.rb +636 -0
- data/lib/active_support/ordered_hash.rb +8 -190
- data/lib/active_support/ordered_options.rb +21 -23
- data/lib/active_support/proxy_object.rb +13 -0
- data/lib/active_support/rails.rb +27 -0
- data/lib/active_support/railtie.rb +12 -32
- data/lib/active_support/rescuable.rb +9 -4
- data/lib/active_support/string_inquirer.rb +13 -8
- data/lib/active_support/tagged_logging.rb +51 -73
- data/lib/active_support/test_case.rb +46 -17
- data/lib/active_support/testing/assertions.rb +56 -26
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/constant_lookup.rb +52 -0
- data/lib/active_support/testing/declarative.rb +1 -1
- data/lib/active_support/testing/deprecation.rb +0 -19
- data/lib/active_support/testing/isolation.rb +25 -58
- data/lib/active_support/testing/pending.rb +5 -43
- data/lib/active_support/testing/setup_and_teardown.rb +6 -92
- data/lib/active_support/testing/tagged_logging.rb +25 -0
- data/lib/active_support/time.rb +6 -21
- data/lib/active_support/time_with_zone.rb +78 -43
- data/lib/active_support/values/time_zone.rb +77 -58
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +4 -4
- data/lib/active_support/xml_mini.rb +35 -17
- data/lib/active_support/xml_mini/jdom.rb +9 -17
- data/lib/active_support/xml_mini/libxml.rb +1 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -2
- data/lib/active_support/xml_mini/nokogiri.rb +1 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -2
- data/lib/active_support/xml_mini/rexml.rb +6 -8
- metadata +107 -77
- data/lib/active_support/base64.rb +0 -54
- data/lib/active_support/core_ext/array/random_access.rb +0 -30
- data/lib/active_support/core_ext/date/freeze.rb +0 -33
- data/lib/active_support/core_ext/exception.rb +0 -3
- data/lib/active_support/core_ext/file/path.rb +0 -5
- data/lib/active_support/core_ext/float.rb +0 -1
- data/lib/active_support/core_ext/float/rounding.rb +0 -19
- data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
- data/lib/active_support/core_ext/io.rb +0 -15
- data/lib/active_support/core_ext/module/method_names.rb +0 -14
- data/lib/active_support/core_ext/module/synchronization.rb +0 -45
- data/lib/active_support/core_ext/process.rb +0 -1
- data/lib/active_support/core_ext/process/daemon.rb +0 -23
- data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
- data/lib/active_support/core_ext/range/cover.rb +0 -3
- data/lib/active_support/core_ext/rexml.rb +0 -46
- data/lib/active_support/core_ext/string/interpolation.rb +0 -2
- data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
- data/lib/active_support/memoizable.rb +0 -116
- data/lib/active_support/multibyte/exceptions.rb +0 -8
- data/lib/active_support/multibyte/utils.rb +0 -60
- data/lib/active_support/ruby/shim.rb +0 -22
- data/lib/active_support/security_utils.rb +0 -27
- data/lib/active_support/testing/mochaing.rb +0 -7
- data/lib/active_support/testing/performance.rb +0 -317
- data/lib/active_support/testing/performance/jruby.rb +0 -115
- data/lib/active_support/testing/performance/rubinius.rb +0 -113
- data/lib/active_support/testing/performance/ruby.rb +0 -152
- data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
- data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
- data/lib/active_support/time/autoload.rb +0 -5
- 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 '
|
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 =
|
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|
|
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
|
-
|
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 =
|
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.
|
148
|
-
|
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|
|
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
|
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 '
|
2
|
+
require 'dalli'
|
3
3
|
rescue LoadError => e
|
4
|
-
$stderr.puts "You don't have
|
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/
|
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
|
-
|
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
|
-
|
87
|
+
instrument(:increment, name, :amount => amount) do
|
95
88
|
@data.incr(escape_key(namespaced_key(name, options)), amount)
|
96
89
|
end
|
97
|
-
|
98
|
-
|
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
|
-
|
101
|
+
instrument(:decrement, name, :amount => amount) do
|
109
102
|
@data.decr(escape_key(namespaced_key(name, options)), amount)
|
110
103
|
end
|
111
|
-
|
112
|
-
|
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),
|
131
|
-
rescue
|
132
|
-
logger.error("
|
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
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
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(
|
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
|
-
#
|
11
|
-
# The typical use case is to have a base class define a set of callbacks
|
12
|
-
# to the other functionality it supplies, so that subclasses can
|
13
|
-
# that enhance or modify the base functionality without
|
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
|
17
|
-
# that will support callbacks (via +ClassMethods.define_callbacks+),
|
18
|
-
# methods, procs, or callback objects to be called (via
|
19
|
-
# and run the installed callbacks at the
|
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
|
22
|
-
# after callbacks, run after the event; and around callbacks,
|
23
|
-
# event, triggering it when they yield. Callback code
|
24
|
-
# methods, procs or lambdas, or callback objects
|
25
|
-
# methods. See +ClassMethods.set_callback+
|
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
|
70
|
-
#
|
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
|
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
|
-
|
81
|
-
send(
|
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
|
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, :
|
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
|
-
|
105
|
-
|
101
|
+
recompile_options!
|
102
|
+
end
|
106
103
|
|
107
|
-
|
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
|
125
|
-
options[:unless] = Array
|
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
|
145
|
-
|
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
|
150
|
-
|
151
|
-
|
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
|
160
|
-
|
146
|
+
def recompile!(_options)
|
147
|
+
deprecate_per_key_option(_options)
|
148
|
+
_update_filter(self.options, _options)
|
161
149
|
|
162
|
-
|
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
|
-
#
|
170
|
-
|
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
|
-
|
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
|
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
|
227
|
+
conditions << Array(_compile_filter(options[:if]))
|
257
228
|
end
|
258
229
|
|
259
230
|
unless options[:unless].empty?
|
260
|
-
conditions << Array
|
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
|
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
|
320
|
+
def compile
|
347
321
|
method = []
|
348
322
|
method << "value = nil"
|
349
323
|
method << "halted = false"
|
350
324
|
|
351
|
-
|
352
|
-
|
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
|
-
|
356
|
-
|
357
|
-
|
358
|
-
end
|
331
|
+
method << "value"
|
332
|
+
method.join("\n")
|
333
|
+
end
|
359
334
|
|
360
|
-
|
335
|
+
def append(*callbacks)
|
336
|
+
callbacks.each { |c| append_one(c) }
|
337
|
+
end
|
361
338
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
method << "end"
|
366
|
-
end
|
339
|
+
def prepend(*callbacks)
|
340
|
+
callbacks.each { |c| prepend_one(c) }
|
341
|
+
end
|
367
342
|
|
368
|
-
|
369
|
-
method << callback.end(key, object)
|
370
|
-
end
|
343
|
+
private
|
371
344
|
|
372
|
-
|
373
|
-
|
374
|
-
|
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
|
393
|
-
#
|
394
|
-
#
|
395
|
-
|
396
|
-
|
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
|
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
|
-
|
375
|
+
name
|
406
376
|
end
|
407
377
|
|
408
378
|
def __reset_runner(symbol)
|
409
|
-
name = __callback_runner_name(
|
379
|
+
name = __callback_runner_name(symbol)
|
410
380
|
undef_method(name) if method_defined?(name)
|
411
381
|
end
|
412
382
|
|
413
|
-
def
|
414
|
-
|
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 =
|
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, :
|
436
|
-
# set_callback :save, :around,
|
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
|
445
|
-
# lambda, or block; as a string to be instance evaluated; or as an
|
446
|
-
# responds to a certain method determined by the <tt>:scope</tt>
|
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;
|
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
|
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
|
464
|
-
# will be called only when it returns a false value.
|
465
|
-
# * <tt>:prepend</tt> - If true
|
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
|
-
|
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
|
510
|
-
# options may be passed in order to control when the
|
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, :
|
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
|
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
|
558
|
-
# chain, preventing following callbacks from being called and
|
559
|
-
# triggered. This is a string to be eval'ed. The
|
560
|
-
# in the
|
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, :
|
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
|
513
|
+
# other callbacks are not executed. Defaults to +false+, meaning no value
|
566
514
|
# halts the chain.
|
567
515
|
#
|
568
|
-
# * <tt>:
|
569
|
-
#
|
570
|
-
#
|
571
|
-
#
|
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
|
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
|
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, :
|
551
|
+
# define_callbacks :save, scope: [:kind, :name]
|
603
552
|
#
|
604
|
-
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
|
605
|
-
# <tt>#{kind}_#{name}</tt> on the given instance. In this
|
606
|
-
# "name" is "save". In this context +:kind+
|
607
|
-
#
|
608
|
-
#
|
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, :
|
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
|