activesupport 3.0.20 → 3.1.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.
- data/CHANGELOG +14 -66
- data/README.rdoc +1 -1
- data/lib/active_support/backtrace_cleaner.rb +1 -1
- data/lib/active_support/buffered_logger.rb +4 -11
- data/lib/active_support/cache.rb +12 -15
- data/lib/active_support/cache/file_store.rb +3 -2
- data/lib/active_support/cache/mem_cache_store.rb +11 -3
- data/lib/active_support/cache/strategy/local_cache.rb +28 -23
- data/lib/active_support/callbacks.rb +195 -175
- data/lib/active_support/concern.rb +105 -35
- data/lib/active_support/configurable.rb +41 -2
- data/lib/active_support/core_ext/array/access.rb +2 -2
- data/lib/active_support/core_ext/array/random_access.rb +10 -7
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -2
- data/lib/active_support/core_ext/class/attribute.rb +27 -17
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +12 -86
- data/lib/active_support/core_ext/class/subclasses.rb +20 -34
- data/lib/active_support/core_ext/date/calculations.rb +14 -2
- data/lib/active_support/core_ext/date/conversions.rb +6 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +26 -9
- data/lib/active_support/core_ext/date_time/conversions.rb +1 -1
- data/lib/active_support/core_ext/date_time/zones.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +2 -5
- data/lib/active_support/core_ext/float/rounding.rb +1 -1
- data/lib/active_support/core_ext/hash.rb +1 -0
- data/lib/active_support/core_ext/hash/conversions.rb +23 -36
- data/lib/active_support/core_ext/hash/deep_dup.rb +11 -0
- data/lib/active_support/core_ext/hash/keys.rb +6 -4
- data/lib/active_support/core_ext/hash/reverse_merge.rb +9 -14
- data/lib/active_support/core_ext/kernel/reporting.rb +19 -0
- data/lib/active_support/core_ext/logger.rb +11 -38
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +11 -12
- data/lib/active_support/core_ext/module/attr_internal.rb +13 -6
- data/lib/active_support/core_ext/module/deprecation.rb +2 -0
- data/lib/active_support/core_ext/object.rb +1 -1
- data/lib/active_support/core_ext/object/blank.rb +42 -9
- data/lib/active_support/core_ext/object/duplicable.rb +48 -9
- data/lib/active_support/core_ext/object/inclusion.rb +15 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -35
- data/lib/active_support/core_ext/object/to_param.rb +13 -8
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +27 -10
- data/lib/active_support/core_ext/object/with_options.rb +19 -5
- data/lib/active_support/core_ext/range.rb +1 -0
- data/lib/active_support/core_ext/range/cover.rb +3 -0
- data/lib/active_support/core_ext/string.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/inquiry.rb +13 -0
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +33 -89
- data/lib/active_support/core_ext/time/calculations.rb +14 -13
- data/lib/active_support/core_ext/time/conversions.rb +2 -24
- data/lib/active_support/core_ext/time/marshal.rb +0 -1
- data/lib/active_support/core_ext/time/zones.rb +32 -21
- data/lib/active_support/core_ext/uri.rb +8 -0
- data/lib/active_support/dependencies.rb +93 -37
- data/lib/active_support/deprecation.rb +2 -2
- data/lib/active_support/deprecation/behaviors.rb +7 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
- data/lib/active_support/deprecation/reporting.rb +4 -0
- data/lib/active_support/duration.rb +4 -0
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/file_watcher.rb +36 -0
- data/lib/active_support/gzip.rb +0 -1
- data/lib/active_support/hash_with_indifferent_access.rb +6 -4
- data/lib/active_support/i18n.rb +0 -1
- data/lib/active_support/i18n_railtie.rb +2 -2
- data/lib/active_support/inflector/inflections.rb +1 -1
- data/lib/active_support/json/decoding.rb +37 -23
- data/lib/active_support/json/encoding.rb +8 -3
- data/lib/active_support/lazy_load_hooks.rb +7 -7
- data/lib/active_support/log_subscriber.rb +3 -3
- data/lib/active_support/log_subscriber/test_helper.rb +4 -3
- data/lib/active_support/message_encryptor.rb +1 -1
- data/lib/active_support/message_verifier.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +4 -3
- data/lib/active_support/multibyte/unicode.rb +1 -1
- data/lib/active_support/notifications.rb +9 -8
- data/lib/active_support/notifications/fanout.rb +3 -3
- data/lib/active_support/ordered_hash.rb +19 -2
- data/lib/active_support/ordered_options.rb +33 -3
- data/lib/active_support/railtie.rb +1 -1
- data/lib/active_support/rescuable.rb +1 -0
- data/lib/active_support/secure_random.rb +11 -5
- data/lib/active_support/test_case.rb +2 -10
- data/lib/active_support/testing/assertions.rb +17 -6
- data/lib/active_support/testing/mochaing.rb +7 -0
- data/lib/active_support/testing/pending.rb +27 -23
- data/lib/active_support/testing/setup_and_teardown.rb +8 -11
- data/lib/active_support/time_with_zone.rb +6 -3
- data/lib/active_support/version.rb +3 -3
- data/lib/active_support/whiny_nil.rb +1 -1
- data/lib/active_support/xml_mini.rb +4 -2
- data/lib/active_support/xml_mini/jdom.rb +9 -16
- data/lib/active_support/xml_mini/libxml.rb +1 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +2 -1
- data/lib/active_support/xml_mini/nokogiri.rb +1 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
- data/lib/active_support/xml_mini/rexml.rb +1 -0
- metadata +50 -32
- checksums.yaml +0 -7
- data/lib/active_support/core_ext/cgi.rb +0 -1
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -19
- data/lib/active_support/core_ext/object/returning.rb +0 -43
- data/lib/active_support/json/backends/jsongem.rb +0 -47
- data/lib/active_support/json/backends/okjson.rb +0 -644
- data/lib/active_support/json/backends/yajl.rb +0 -44
- data/lib/active_support/json/backends/yaml.rb +0 -19
- data/lib/active_support/testing/default.rb +0 -9
data/CHANGELOG
CHANGED
@@ -1,83 +1,31 @@
|
|
1
|
-
|
1
|
+
*Rails 3.1.0 (unreleased)*
|
2
2
|
|
3
|
-
|
3
|
+
* Add String#inquiry as a convenience method for turning a string into a StringInquirer object [DHH]
|
4
4
|
|
5
|
-
*
|
6
|
-
Use Hash.from_trusted_xml to parse this XML.
|
5
|
+
* Add Object#in? to test if an object is included in another object [Prem Sichanugrist, Brian Morearty, John Reitano]
|
7
6
|
|
8
|
-
|
7
|
+
* LocalCache strategy is now a real middleware class, not an anonymous class
|
8
|
+
posing for pictures.
|
9
9
|
|
10
|
-
|
10
|
+
* ActiveSupport::Dependencies::ClassCache class has been introduced for
|
11
|
+
holding references to reloadable classes.
|
11
12
|
|
12
|
-
|
13
|
+
* ActiveSupport::Dependencies::Reference has been refactored to take direct
|
14
|
+
advantage of the new ClassCache.
|
13
15
|
|
14
|
-
*
|
16
|
+
* Backports Range#cover? as an alias for Range#include? in Ruby 1.8 [Diego Carrion, fxn]
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
* ERB::Util.html_escape now escapes single quotes. [Santiago Pastorino]
|
19
|
-
|
20
|
-
## Rails 3.0.16 (Jul 26, 2012)
|
21
|
-
|
22
|
-
* No changes.
|
23
|
-
|
24
|
-
## Rails 3.0.14 (Jun 12, 2012)
|
25
|
-
|
26
|
-
* No changes.
|
27
|
-
|
28
|
-
* Rails 3.0.13 (May 31, 2012)
|
29
|
-
|
30
|
-
* Stop SafeBuffer#clone_empty from issuing warnings
|
31
|
-
|
32
|
-
* Rails 3.0.10 (August 16, 2011)
|
33
|
-
|
34
|
-
* Delayed backtrace scrubbing in `load_missing_constant` until we actually
|
35
|
-
raise the exception
|
36
|
-
|
37
|
-
|
38
|
-
*Rails 3.0.9 (June 16, 2011)*
|
39
|
-
|
40
|
-
*No changes.
|
41
|
-
|
42
|
-
|
43
|
-
*Rails 3.0.8 (June 7, 2011)*
|
44
|
-
|
45
|
-
* No changes.
|
46
|
-
|
47
|
-
*Rails 3.0.7 (April 18, 2011)*
|
48
|
-
|
49
|
-
* Hash.from_xml no longer loses attributes on tags containing only whitespace [André Arko]
|
50
|
-
|
51
|
-
|
52
|
-
*Rails 3.0.6 (April 5, 2011)
|
53
|
-
|
54
|
-
* No changes.
|
55
|
-
|
56
|
-
|
57
|
-
*Rails 3.0.5 (February 26, 2011)*
|
58
|
-
|
59
|
-
* No changes.
|
60
|
-
|
61
|
-
|
62
|
-
*Rails 3.0.4 (February 8, 2011)*
|
63
|
-
|
64
|
-
* No changes.
|
65
|
-
|
66
|
-
|
67
|
-
*Rails 3.0.3 (November 16, 2010)*
|
68
|
-
|
69
|
-
* No changes.
|
18
|
+
* Added weeks_ago and prev_week to Date/DateTime/Time. [Rob Zolkos, fxn]
|
70
19
|
|
20
|
+
* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
|
71
21
|
|
72
|
-
*Rails 3.0.2 (
|
22
|
+
*Rails 3.0.2 (unreleased)*
|
73
23
|
|
74
24
|
* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
|
75
25
|
|
76
|
-
|
77
26
|
*Rails 3.0.1 (October 15, 2010)*
|
78
27
|
|
79
|
-
* No
|
80
|
-
|
28
|
+
* No Changes, just a version bump.
|
81
29
|
|
82
30
|
*Rails 3.0.0 (August 29, 2010)*
|
83
31
|
|
data/README.rdoc
CHANGED
@@ -14,7 +14,7 @@ The latest version of Active Support can be installed with Rubygems:
|
|
14
14
|
|
15
15
|
Source code can be downloaded as part of the Rails project on GitHub
|
16
16
|
|
17
|
-
*
|
17
|
+
* https://github.com/rails/rails/tree/master/activesupport/
|
18
18
|
|
19
19
|
|
20
20
|
== License
|
@@ -8,7 +8,7 @@ module ActiveSupport
|
|
8
8
|
# filter or modify the paths of any lines of the backtrace, you can call BacktraceCleaner#remove_filters! These two methods
|
9
9
|
# will give you a completely untouched backtrace.
|
10
10
|
#
|
11
|
-
# Example:
|
11
|
+
# ==== Example:
|
12
12
|
#
|
13
13
|
# bc = BacktraceCleaner.new
|
14
14
|
# bc.add_filter { |line| line.gsub(Rails.root, '') }
|
@@ -48,10 +48,12 @@ module ActiveSupport
|
|
48
48
|
if log.respond_to?(:write)
|
49
49
|
@log = log
|
50
50
|
elsif File.exist?(log)
|
51
|
-
@log =
|
51
|
+
@log = open(log, (File::WRONLY | File::APPEND))
|
52
|
+
@log.sync = true
|
52
53
|
else
|
53
54
|
FileUtils.mkdir_p(File.dirname(log))
|
54
|
-
@log =
|
55
|
+
@log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
|
56
|
+
@log.sync = true
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -126,14 +128,5 @@ module ActiveSupport
|
|
126
128
|
def clear_buffer
|
127
129
|
@buffer.delete(Thread.current)
|
128
130
|
end
|
129
|
-
|
130
|
-
private
|
131
|
-
|
132
|
-
def open_log(log, mode)
|
133
|
-
open(log, mode).tap do |log|
|
134
|
-
log.set_encoding(Encoding::BINARY) if log.respond_to?(:set_encoding)
|
135
|
-
log.sync = true
|
136
|
-
end
|
137
|
-
end
|
138
131
|
end
|
139
132
|
end
|
data/lib/active_support/cache.rb
CHANGED
@@ -345,7 +345,7 @@ module ActiveSupport
|
|
345
345
|
entry = read_entry(key, options)
|
346
346
|
if entry
|
347
347
|
if entry.expired?
|
348
|
-
delete_entry(key
|
348
|
+
delete_entry(key)
|
349
349
|
else
|
350
350
|
results[name] = entry.value
|
351
351
|
end
|
@@ -484,19 +484,20 @@ module ActiveSupport
|
|
484
484
|
# object responds to +cache_key+. Otherwise, to_param method will be
|
485
485
|
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
486
486
|
def expanded_key(key) # :nodoc:
|
487
|
-
if key.respond_to?(:cache_key)
|
488
|
-
|
489
|
-
|
487
|
+
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
488
|
+
|
489
|
+
case key
|
490
|
+
when Array
|
490
491
|
if key.size > 1
|
491
|
-
key.collect{|element| expanded_key(element)}
|
492
|
+
key = key.collect{|element| expanded_key(element)}
|
492
493
|
else
|
493
|
-
key.first
|
494
|
+
key = key.first
|
494
495
|
end
|
495
|
-
|
496
|
-
key = key.
|
497
|
-
else
|
498
|
-
key = key.to_param
|
496
|
+
when Hash
|
497
|
+
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
|
499
498
|
end
|
499
|
+
|
500
|
+
key.to_param
|
500
501
|
end
|
501
502
|
|
502
503
|
# Prefix a key with the namespace. Namespace and key will be delimited with a colon.
|
@@ -589,11 +590,7 @@ module ActiveSupport
|
|
589
590
|
# Check if the entry is expired. The +expires_in+ parameter can override the
|
590
591
|
# value set when the entry was created.
|
591
592
|
def expired?
|
592
|
-
|
593
|
-
true
|
594
|
-
else
|
595
|
-
false
|
596
|
-
end
|
593
|
+
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
597
594
|
end
|
598
595
|
|
599
596
|
# Set a new time when the entry will expire.
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/file/atomic'
|
2
2
|
require 'active_support/core_ext/string/conversions'
|
3
|
+
require 'active_support/core_ext/object/inclusion'
|
3
4
|
require 'rack/utils'
|
4
5
|
|
5
6
|
module ActiveSupport
|
@@ -20,7 +21,7 @@ module ActiveSupport
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def clear(options = nil)
|
23
|
-
root_dirs = Dir.entries(cache_path).reject{|f| ['.', '..']
|
24
|
+
root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])}
|
24
25
|
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
|
25
26
|
end
|
26
27
|
|
@@ -161,7 +162,7 @@ module ActiveSupport
|
|
161
162
|
# Delete empty directories in the cache.
|
162
163
|
def delete_empty_directories(dir)
|
163
164
|
return if dir == cache_path
|
164
|
-
if Dir.entries(dir).reject{|f| ['.', '..']
|
165
|
+
if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty?
|
165
166
|
File.delete(dir) rescue nil
|
166
167
|
delete_empty_directories(File.dirname(dir))
|
167
168
|
end
|
@@ -4,7 +4,9 @@ rescue LoadError => e
|
|
4
4
|
$stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
|
5
5
|
raise e
|
6
6
|
end
|
7
|
+
|
7
8
|
require 'digest/md5'
|
9
|
+
require 'active_support/core_ext/string/encoding'
|
8
10
|
|
9
11
|
module ActiveSupport
|
10
12
|
module Cache
|
@@ -29,7 +31,7 @@ module ActiveSupport
|
|
29
31
|
DELETED = "DELETED\r\n"
|
30
32
|
end
|
31
33
|
|
32
|
-
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
|
34
|
+
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
|
33
35
|
|
34
36
|
def self.build_mem_cache(*addresses)
|
35
37
|
addresses = addresses.flatten
|
@@ -73,7 +75,7 @@ module ActiveSupport
|
|
73
75
|
def read_multi(*names)
|
74
76
|
options = names.extract_options!
|
75
77
|
options = merged_options(options)
|
76
|
-
keys_to_names = names.
|
78
|
+
keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
|
77
79
|
raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
|
78
80
|
values = {}
|
79
81
|
raw_values.each do |key, value|
|
@@ -157,8 +159,14 @@ module ActiveSupport
|
|
157
159
|
end
|
158
160
|
|
159
161
|
private
|
162
|
+
|
163
|
+
# Memcache keys are binaries. So we need to force their encoding to binary
|
164
|
+
# before applying the regular expression to ensure we are escaping all
|
165
|
+
# characters properly.
|
160
166
|
def escape_key(key)
|
161
|
-
key = key.to_s.
|
167
|
+
key = key.to_s.dup
|
168
|
+
key = key.force_encoding("BINARY") if key.encoding_aware?
|
169
|
+
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
162
170
|
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
|
163
171
|
key
|
164
172
|
end
|
@@ -50,34 +50,39 @@ module ActiveSupport
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
59
|
-
class << self
|
60
|
-
def name
|
61
|
-
"ActiveSupport::Cache::Strategy::LocalCache"
|
62
|
-
end
|
63
|
-
alias :to_s :name
|
64
|
-
end
|
53
|
+
#--
|
54
|
+
# This class wraps up local storage for middlewares. Only the middleware method should
|
55
|
+
# construct them.
|
56
|
+
class Middleware # :nodoc:
|
57
|
+
attr_reader :name, :thread_local_key
|
65
58
|
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
def initialize(name, thread_local_key)
|
60
|
+
@name = name
|
61
|
+
@thread_local_key = thread_local_key
|
62
|
+
@app = nil
|
63
|
+
end
|
69
64
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
65
|
+
def new(app)
|
66
|
+
@app = app
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def call(env)
|
71
|
+
Thread.current[thread_local_key] = LocalStore.new
|
72
|
+
@app.call(env)
|
73
|
+
ensure
|
74
|
+
Thread.current[thread_local_key] = nil
|
78
75
|
end
|
79
76
|
end
|
80
77
|
|
78
|
+
# Middleware class can be inserted as a Rack handler to be local cache for the
|
79
|
+
# duration of request.
|
80
|
+
def middleware
|
81
|
+
@middleware ||= Middleware.new(
|
82
|
+
"ActiveSupport::Cache::Strategy::LocalCache",
|
83
|
+
thread_local_key)
|
84
|
+
end
|
85
|
+
|
81
86
|
def clear(options = nil) # :nodoc:
|
82
87
|
local_cache.clear(options) if local_cache
|
83
88
|
super
|
@@ -1,32 +1,34 @@
|
|
1
1
|
require 'active_support/concern'
|
2
2
|
require 'active_support/descendants_tracker'
|
3
3
|
require 'active_support/core_ext/array/wrap'
|
4
|
-
require 'active_support/core_ext/class/
|
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'
|
7
8
|
|
8
9
|
module ActiveSupport
|
9
|
-
# Callbacks are hooks
|
10
|
-
#
|
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.
|
11
15
|
#
|
12
|
-
# Mixing in this module allows you to define
|
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+).
|
13
20
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# end
|
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.
|
20
26
|
#
|
21
|
-
#
|
22
|
-
# set_callback :save, :before, :saving_message
|
23
|
-
# def saving_message
|
24
|
-
# puts "saving..."
|
25
|
-
# end
|
27
|
+
# ==== Example
|
26
28
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
29
|
+
# class Record
|
30
|
+
# include ActiveSupport::Callbacks
|
31
|
+
# define_callbacks :save
|
30
32
|
#
|
31
33
|
# def save
|
32
34
|
# run_callbacks :save do
|
@@ -35,29 +37,7 @@ module ActiveSupport
|
|
35
37
|
# end
|
36
38
|
# end
|
37
39
|
#
|
38
|
-
#
|
39
|
-
# config.save
|
40
|
-
#
|
41
|
-
# Output:
|
42
|
-
# saving...
|
43
|
-
# - save
|
44
|
-
# saved
|
45
|
-
#
|
46
|
-
# Callbacks from parent classes are inherited.
|
47
|
-
#
|
48
|
-
# Example:
|
49
|
-
# class Storage
|
50
|
-
# include ActiveSupport::Callbacks
|
51
|
-
#
|
52
|
-
# define_callbacks :save
|
53
|
-
#
|
54
|
-
# set_callback :save, :before, :prepare
|
55
|
-
# def prepare
|
56
|
-
# puts "preparing save"
|
57
|
-
# end
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
# class ConfigStorage < Storage
|
40
|
+
# class PersonRecord < Record
|
61
41
|
# set_callback :save, :before, :saving_message
|
62
42
|
# def saving_message
|
63
43
|
# puts "saving..."
|
@@ -66,19 +46,12 @@ module ActiveSupport
|
|
66
46
|
# set_callback :save, :after do |object|
|
67
47
|
# puts "saved"
|
68
48
|
# end
|
69
|
-
#
|
70
|
-
# def save
|
71
|
-
# run_callbacks :save do
|
72
|
-
# puts "- save"
|
73
|
-
# end
|
74
|
-
# end
|
75
49
|
# end
|
76
50
|
#
|
77
|
-
#
|
78
|
-
#
|
51
|
+
# person = PersonRecord.new
|
52
|
+
# person.save
|
79
53
|
#
|
80
54
|
# Output:
|
81
|
-
# preparing save
|
82
55
|
# saving...
|
83
56
|
# - save
|
84
57
|
# saved
|
@@ -90,16 +63,25 @@ module ActiveSupport
|
|
90
63
|
extend ActiveSupport::DescendantsTracker
|
91
64
|
end
|
92
65
|
|
66
|
+
# Runs the callbacks for the given event.
|
67
|
+
#
|
68
|
+
# Calls the before and around callbacks in the order they were set, yields
|
69
|
+
# the block (if given one), and then runs the after callbacks in reverse order.
|
70
|
+
# Optionally accepts a key, which will be used to compile an optimized callback
|
71
|
+
# method for each key. See +ClassMethods.define_callbacks+ for more information.
|
72
|
+
#
|
73
|
+
# If the callback chain was halted, returns +false+. Otherwise returns the result
|
74
|
+
# of the block, or +true+ if no block is given.
|
75
|
+
#
|
76
|
+
# run_callbacks :save do
|
77
|
+
# save
|
78
|
+
# end
|
79
|
+
#
|
93
80
|
def run_callbacks(kind, *args, &block)
|
94
81
|
send("_run_#{kind}_callbacks", *args, &block)
|
95
82
|
end
|
96
83
|
|
97
|
-
|
98
|
-
ActiveSupport::Deprecation.warn("callback is deprecated. Please use run_callbacks")
|
99
|
-
send("_run_#{kind}_callbacks")
|
100
|
-
end
|
101
|
-
|
102
|
-
class Callback
|
84
|
+
class Callback #:nodoc:#
|
103
85
|
@@_callback_sequence = 0
|
104
86
|
|
105
87
|
attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
|
@@ -184,49 +166,52 @@ module ActiveSupport
|
|
184
166
|
# options[0] is the compiled form of supplied conditions
|
185
167
|
# options[1] is the "end" for the conditional
|
186
168
|
#
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
169
|
+
case @kind
|
170
|
+
when :before
|
171
|
+
# if condition # before_save :filter_name, :if => :condition
|
172
|
+
# filter_name
|
173
|
+
# end
|
174
|
+
filter = <<-RUBY_EVAL
|
175
|
+
unless halted
|
176
|
+
# This double assignment is to prevent warnings in 1.9.3. I would
|
177
|
+
# remove the `result` variable, but apparently some other
|
178
|
+
# generated code is depending on this variable being set sometimes
|
179
|
+
# and sometimes not.
|
180
|
+
result = result = #{@filter}
|
181
|
+
halted = (#{chain.config[:terminator]})
|
182
|
+
end
|
183
|
+
RUBY_EVAL
|
184
|
+
|
185
|
+
[@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
|
186
|
+
when :around
|
187
|
+
# Compile around filters with conditions into proxy methods
|
188
|
+
# that contain the conditions.
|
189
|
+
#
|
190
|
+
# For `around_save :filter_name, :if => :condition':
|
191
|
+
#
|
192
|
+
# def _conditional_callback_save_17
|
193
|
+
# if condition
|
194
|
+
# filter_name do
|
195
|
+
# yield self
|
196
|
+
# end
|
197
|
+
# else
|
198
|
+
# yield self
|
199
|
+
# end
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
name = "_conditional_callback_#{@kind}_#{next_id}"
|
203
|
+
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
204
|
+
def #{name}(halted)
|
205
|
+
#{@compiled_options[0] || "if true"} && !halted
|
206
|
+
#{@filter} do
|
224
207
|
yield self
|
225
208
|
end
|
209
|
+
else
|
210
|
+
yield self
|
226
211
|
end
|
227
|
-
|
228
|
-
|
229
|
-
|
212
|
+
end
|
213
|
+
RUBY_EVAL
|
214
|
+
"#{name}(halted) do"
|
230
215
|
end
|
231
216
|
end
|
232
217
|
|
@@ -235,15 +220,17 @@ module ActiveSupport
|
|
235
220
|
def end(key=nil, object=nil)
|
236
221
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
237
222
|
|
238
|
-
|
223
|
+
case @kind
|
224
|
+
when :after
|
239
225
|
# if condition # after_save :filter_name, :if => :condition
|
240
226
|
# filter_name
|
241
227
|
# end
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
228
|
+
[@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
|
229
|
+
when :around
|
230
|
+
<<-RUBY_EVAL
|
231
|
+
value
|
246
232
|
end
|
233
|
+
RUBY_EVAL
|
247
234
|
end
|
248
235
|
end
|
249
236
|
|
@@ -336,7 +323,7 @@ module ActiveSupport
|
|
336
323
|
end
|
337
324
|
|
338
325
|
# An Array with a compile method
|
339
|
-
class CallbackChain < Array
|
326
|
+
class CallbackChain < Array #:nodoc:#
|
340
327
|
attr_reader :name, :config
|
341
328
|
|
342
329
|
def initialize(name, config)
|
@@ -381,20 +368,9 @@ module ActiveSupport
|
|
381
368
|
end
|
382
369
|
|
383
370
|
module ClassMethods
|
384
|
-
#
|
385
|
-
# a block that it'll yield to. It'll call the before and around filters
|
386
|
-
# in order, yield the block, and then run the after filters.
|
387
|
-
#
|
388
|
-
# run_callbacks :save do
|
389
|
-
# save
|
390
|
-
# end
|
391
|
-
#
|
392
|
-
# The run_callbacks :save method can optionally take a key, which
|
393
|
-
# will be used to compile an optimized callback method for each
|
394
|
-
# key. See #define_callbacks for more information.
|
395
|
-
#
|
371
|
+
# Generate the internal runner method called by +run_callbacks+.
|
396
372
|
def __define_runner(symbol) #:nodoc:
|
397
|
-
body = send("_#{symbol}_callbacks").compile
|
373
|
+
body = send("_#{symbol}_callbacks").compile
|
398
374
|
|
399
375
|
silence_warnings do
|
400
376
|
undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
|
@@ -437,25 +413,56 @@ module ActiveSupport
|
|
437
413
|
# CallbackChain.
|
438
414
|
#
|
439
415
|
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
|
440
|
-
type = [:before, :after, :around]
|
416
|
+
type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
|
441
417
|
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
442
418
|
filters.unshift(block) if block
|
443
419
|
|
444
|
-
([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
|
420
|
+
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
|
445
421
|
chain = target.send("_#{name}_callbacks")
|
446
|
-
yield chain, type, filters, options
|
422
|
+
yield target, chain.dup, type, filters, options
|
447
423
|
target.__define_runner(name)
|
448
424
|
end
|
449
425
|
end
|
450
426
|
|
451
|
-
#
|
427
|
+
# Install a callback for the given event.
|
452
428
|
#
|
453
|
-
# Syntax:
|
454
429
|
# set_callback :save, :before, :before_meth
|
455
430
|
# set_callback :save, :after, :after_meth, :if => :condition
|
456
|
-
# set_callback :save, :around, lambda { |r| stuff; yield; stuff }
|
431
|
+
# set_callback :save, :around, lambda { |r| stuff; result = yield; stuff }
|
432
|
+
#
|
433
|
+
# The second arguments indicates whether the callback is to be run +:before+,
|
434
|
+
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
|
435
|
+
# means the first example above can also be written as:
|
436
|
+
#
|
437
|
+
# set_callback :save, :before_meth
|
438
|
+
#
|
439
|
+
# The callback can specified as a symbol naming an instance method; as a proc,
|
440
|
+
# lambda, or block; as a string to be instance evaluated; or as an object that
|
441
|
+
# responds to a certain method determined by the <tt>:scope</tt> argument to
|
442
|
+
# +define_callback+.
|
443
|
+
#
|
444
|
+
# If a proc, lambda, or block is given, its body is evaluated in the context
|
445
|
+
# of the current object. It can also optionally accept the current object as
|
446
|
+
# an argument.
|
457
447
|
#
|
458
|
-
#
|
448
|
+
# Before and around callbacks are called in the order that they are set; after
|
449
|
+
# callbacks are called in the reverse order.
|
450
|
+
#
|
451
|
+
# Around callbacks can access the return value from the event, if it
|
452
|
+
# wasn't halted, from the +yield+ call.
|
453
|
+
#
|
454
|
+
# ===== Options
|
455
|
+
#
|
456
|
+
# * <tt>:if</tt> - A symbol naming an instance method or a proc; the callback
|
457
|
+
# will be called only when it returns a true value.
|
458
|
+
# * <tt>:unless</tt> - A symbol naming an instance method or a proc; the callback
|
459
|
+
# will be called only when it returns a false value.
|
460
|
+
# * <tt>:prepend</tt> - If true, the callback will be prepended to the existing
|
461
|
+
# chain rather than appended.
|
462
|
+
# * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options;
|
463
|
+
# see "Per-key conditions" below.
|
464
|
+
#
|
465
|
+
# ===== Per-key conditions
|
459
466
|
#
|
460
467
|
# When creating or skipping callbacks, you can specify conditions that
|
461
468
|
# are always the same for a given key. For instance, in Action Pack,
|
@@ -465,12 +472,12 @@ module ActiveSupport
|
|
465
472
|
#
|
466
473
|
# becomes
|
467
474
|
#
|
468
|
-
#
|
475
|
+
# set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
|
469
476
|
#
|
470
|
-
# Per-
|
477
|
+
# Per-key conditions are evaluated only once per use of a given key.
|
471
478
|
# In the case of the above example, you would do:
|
472
479
|
#
|
473
|
-
# run_callbacks(:
|
480
|
+
# run_callbacks(:process_action, action_name) { ... dispatch stuff ... }
|
474
481
|
#
|
475
482
|
# In that case, each action_name would get its own compiled callback
|
476
483
|
# method that took into consideration the per_key conditions. This
|
@@ -479,7 +486,7 @@ module ActiveSupport
|
|
479
486
|
def set_callback(name, *filter_list, &block)
|
480
487
|
mapped = nil
|
481
488
|
|
482
|
-
__update_callbacks(name, filter_list, block) do |chain, type, filters, options|
|
489
|
+
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
|
483
490
|
mapped ||= filters.map do |filter|
|
484
491
|
Callback.new(chain, filter, type, options.dup, self)
|
485
492
|
end
|
@@ -489,13 +496,20 @@ module ActiveSupport
|
|
489
496
|
end
|
490
497
|
|
491
498
|
options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
|
499
|
+
|
500
|
+
target.send("_#{name}_callbacks=", chain)
|
492
501
|
end
|
493
502
|
end
|
494
503
|
|
495
|
-
# Skip a previously
|
504
|
+
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or <tt>:unless</tt>
|
505
|
+
# options may be passed in order to control when the callback is skipped.
|
506
|
+
#
|
507
|
+
# class Writer < Person
|
508
|
+
# skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 }
|
509
|
+
# end
|
496
510
|
#
|
497
511
|
def skip_callback(name, *filter_list, &block)
|
498
|
-
__update_callbacks(name, filter_list, block) do |chain, type, filters, options|
|
512
|
+
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
|
499
513
|
filters.each do |filter|
|
500
514
|
filter = chain.find {|c| c.matches?(type, filter) }
|
501
515
|
|
@@ -507,92 +521,98 @@ module ActiveSupport
|
|
507
521
|
|
508
522
|
chain.delete(filter)
|
509
523
|
end
|
524
|
+
target.send("_#{name}_callbacks=", chain)
|
510
525
|
end
|
511
526
|
end
|
512
527
|
|
513
|
-
#
|
528
|
+
# Remove all set callbacks for the given event.
|
514
529
|
#
|
515
530
|
def reset_callbacks(symbol)
|
516
531
|
callbacks = send("_#{symbol}_callbacks")
|
517
532
|
|
518
533
|
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
|
519
|
-
chain = target.send("_#{symbol}_callbacks")
|
534
|
+
chain = target.send("_#{symbol}_callbacks").dup
|
520
535
|
callbacks.each { |c| chain.delete(c) }
|
536
|
+
target.send("_#{symbol}_callbacks=", chain)
|
521
537
|
target.__define_runner(symbol)
|
522
538
|
end
|
523
539
|
|
524
|
-
callbacks.clear
|
540
|
+
self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
|
541
|
+
|
525
542
|
__define_runner(symbol)
|
526
543
|
end
|
527
544
|
|
528
|
-
#
|
545
|
+
# Define sets of events in the object lifecycle that support callbacks.
|
529
546
|
#
|
530
547
|
# define_callbacks :validate
|
548
|
+
# define_callbacks :initialize, :save, :destroy
|
531
549
|
#
|
532
|
-
#
|
550
|
+
# ===== Options
|
533
551
|
#
|
534
|
-
# * <tt>:terminator</tt> -
|
535
|
-
#
|
552
|
+
# * <tt>:terminator</tt> - Determines when a before filter will halt the callback
|
553
|
+
# chain, preventing following callbacks from being called and the event from being
|
554
|
+
# triggered. This is a string to be eval'ed. The result of the callback is available
|
555
|
+
# in the <tt>result</tt> variable.
|
536
556
|
#
|
537
|
-
#
|
557
|
+
# define_callbacks :validate, :terminator => "result == false"
|
538
558
|
#
|
539
|
-
#
|
540
|
-
#
|
541
|
-
#
|
559
|
+
# In this example, if any before validate callbacks returns +false+,
|
560
|
+
# other callbacks are not executed. Defaults to "false", meaning no value
|
561
|
+
# halts the chain.
|
542
562
|
#
|
543
563
|
# * <tt>:rescuable</tt> - By default, after filters are not executed if
|
544
|
-
#
|
545
|
-
# true
|
564
|
+
# the given block or a before filter raises an error. By setting this option
|
565
|
+
# to <tt>true</tt> exception raised by given block is stored and after
|
566
|
+
# executing all the after callbacks the stored exception is raised.
|
546
567
|
#
|
547
|
-
# * <tt>:scope</tt> - Indicates which methods should be executed when
|
548
|
-
#
|
568
|
+
# * <tt>:scope</tt> - Indicates which methods should be executed when an object
|
569
|
+
# is used as a callback.
|
549
570
|
#
|
550
|
-
#
|
551
|
-
#
|
552
|
-
#
|
553
|
-
#
|
571
|
+
# class Audit
|
572
|
+
# def before(caller)
|
573
|
+
# puts 'Audit: before is called'
|
574
|
+
# end
|
554
575
|
#
|
555
|
-
#
|
556
|
-
#
|
557
|
-
#
|
558
|
-
#
|
576
|
+
# def before_save(caller)
|
577
|
+
# puts 'Audit: before_save is called'
|
578
|
+
# end
|
579
|
+
# end
|
559
580
|
#
|
560
|
-
#
|
561
|
-
#
|
581
|
+
# class Account
|
582
|
+
# include ActiveSupport::Callbacks
|
562
583
|
#
|
563
|
-
#
|
564
|
-
#
|
584
|
+
# define_callbacks :save
|
585
|
+
# set_callback :save, :before, Audit.new
|
565
586
|
#
|
566
|
-
#
|
567
|
-
#
|
568
|
-
#
|
569
|
-
#
|
570
|
-
#
|
571
|
-
#
|
587
|
+
# def save
|
588
|
+
# run_callbacks :save do
|
589
|
+
# puts 'save in main'
|
590
|
+
# end
|
591
|
+
# end
|
592
|
+
# end
|
572
593
|
#
|
573
|
-
#
|
574
|
-
#
|
594
|
+
# In the above case whenever you save an account the method <tt>Audit#before</tt> will
|
595
|
+
# be called. On the other hand
|
575
596
|
#
|
576
|
-
#
|
597
|
+
# define_callbacks :save, :scope => [:kind, :name]
|
577
598
|
#
|
578
|
-
#
|
579
|
-
#
|
580
|
-
#
|
581
|
-
#
|
582
|
-
#
|
599
|
+
# would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
|
600
|
+
# <tt>#{kind}_#{name}</tt> on the given instance. In this case "kind" is "before" and
|
601
|
+
# "name" is "save". In this context +:kind+ and +:name+ have special meanings: +:kind+
|
602
|
+
# refers to the kind of callback (before/after/around) and +:name+ refers to the
|
603
|
+
# method on which callbacks are being defined.
|
583
604
|
#
|
584
|
-
#
|
605
|
+
# A declaration like
|
585
606
|
#
|
586
|
-
#
|
607
|
+
# define_callbacks :save, :scope => [:name]
|
587
608
|
#
|
588
|
-
#
|
609
|
+
# would call <tt>Audit#save</tt>.
|
589
610
|
#
|
590
611
|
def define_callbacks(*callbacks)
|
591
612
|
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
|
592
613
|
callbacks.each do |callback|
|
593
|
-
|
594
|
-
|
595
|
-
end
|
614
|
+
class_attribute "_#{callback}_callbacks"
|
615
|
+
send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
|
596
616
|
__define_runner(callback)
|
597
617
|
end
|
598
618
|
end
|