activesupport 3.1.12 → 3.2.0.rc1
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.md +1539 -30
- data/README.rdoc +2 -2
- data/lib/active_support.rb +1 -1
- data/lib/active_support/benchmarkable.rb +13 -18
- data/lib/active_support/buffered_logger.rb +43 -55
- data/lib/active_support/cache.rb +109 -115
- data/lib/active_support/cache/file_store.rb +6 -17
- data/lib/active_support/cache/mem_cache_store.rb +10 -2
- data/lib/active_support/cache/null_store.rb +44 -0
- data/lib/active_support/cache/strategy/local_cache.rb +2 -2
- data/lib/active_support/callbacks.rb +38 -35
- data/lib/active_support/concern.rb +5 -10
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/array/access.rb +1 -1
- data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
- data/lib/active_support/core_ext/array/wrap.rb +1 -1
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -2
- data/lib/active_support/core_ext/date/calculations.rb +37 -14
- data/lib/active_support/core_ext/date/freeze.rb +6 -4
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +25 -8
- data/lib/active_support/core_ext/file/atomic.rb +1 -2
- data/lib/active_support/core_ext/hash/conversions.rb +7 -25
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
- data/lib/active_support/core_ext/io.rb +15 -0
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -7
- data/lib/active_support/core_ext/module.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -2
- data/lib/active_support/core_ext/module/delegation.rb +32 -21
- data/lib/active_support/core_ext/module/qualified_const.rb +64 -0
- data/lib/active_support/core_ext/module/reachable.rb +1 -3
- data/lib/active_support/core_ext/module/synchronization.rb +2 -0
- data/lib/active_support/core_ext/object/blank.rb +14 -2
- data/lib/active_support/core_ext/object/inclusion.rb +17 -7
- data/lib/active_support/core_ext/object/to_json.rb +2 -2
- data/lib/active_support/core_ext/range/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- data/lib/active_support/core_ext/string/inflections.rb +44 -6
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +22 -25
- data/lib/active_support/core_ext/time/calculations.rb +66 -12
- data/lib/active_support/dependencies.rb +51 -52
- data/lib/active_support/file_update_checker.rb +100 -15
- data/lib/active_support/hash_with_indifferent_access.rb +5 -1
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +9 -4
- data/lib/active_support/inflections.rb +3 -3
- data/lib/active_support/inflector/inflections.rb +53 -92
- data/lib/active_support/inflector/methods.rb +173 -9
- data/lib/active_support/json/decoding.rb +3 -17
- data/lib/active_support/json/encoding.rb +11 -14
- data/lib/active_support/memoizable.rb +12 -1
- data/lib/active_support/message_encryptor.rb +52 -20
- data/lib/active_support/message_verifier.rb +15 -4
- data/lib/active_support/notifications.rb +87 -14
- data/lib/active_support/notifications/instrumenter.rb +1 -2
- data/lib/active_support/ordered_hash.rb +7 -3
- data/lib/active_support/tagged_logging.rb +63 -0
- data/lib/active_support/testing/assertions.rb +1 -1
- data/lib/active_support/testing/mochaing.rb +2 -2
- data/lib/active_support/testing/performance/ruby.rb +1 -1
- data/lib/active_support/testing/setup_and_teardown.rb +4 -12
- data/lib/active_support/time_with_zone.rb +6 -3
- data/lib/active_support/values/time_zone.rb +3 -7
- data/lib/active_support/version.rb +3 -3
- data/lib/active_support/xml_mini.rb +3 -3
- data/lib/active_support/xml_mini/jdom.rb +4 -10
- metadata +28 -21
- checksums.yaml +0 -7
- data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
- data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
- data/lib/active_support/core_ext/kernel/requires.rb +0 -28
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
- data/lib/active_support/secure_random.rb +0 -6
@@ -9,13 +9,7 @@ module ActiveSupport
|
|
9
9
|
module JSON
|
10
10
|
class << self
|
11
11
|
def decode(json, options ={})
|
12
|
-
|
13
|
-
# a reserved word. Use adapter as a proxy for new features.
|
14
|
-
data = if MultiJson.respond_to?(:adapter)
|
15
|
-
MultiJson.load(json, options)
|
16
|
-
else
|
17
|
-
MultiJson.decode(json, options)
|
18
|
-
end
|
12
|
+
data = MultiJson.decode(json, options)
|
19
13
|
if ActiveSupport.parse_json_times
|
20
14
|
convert_dates_from(data)
|
21
15
|
else
|
@@ -24,20 +18,12 @@ module ActiveSupport
|
|
24
18
|
end
|
25
19
|
|
26
20
|
def engine
|
27
|
-
|
28
|
-
MultiJson.adapter
|
29
|
-
else
|
30
|
-
MultiJson.engine
|
31
|
-
end
|
21
|
+
MultiJson.engine
|
32
22
|
end
|
33
23
|
alias :backend :engine
|
34
24
|
|
35
25
|
def engine=(name)
|
36
|
-
|
37
|
-
MultiJson.use name
|
38
|
-
else
|
39
|
-
MultiJson.engine = name
|
40
|
-
end
|
26
|
+
MultiJson.engine = name
|
41
27
|
end
|
42
28
|
alias :backend= :engine=
|
43
29
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/object/to_json'
|
2
2
|
require 'active_support/core_ext/module/delegation'
|
3
|
-
require 'active_support/deprecation'
|
4
3
|
require 'active_support/json/variable'
|
5
4
|
require 'active_support/ordered_hash'
|
6
5
|
|
@@ -14,6 +13,7 @@ require 'time'
|
|
14
13
|
require 'active_support/core_ext/time/conversions'
|
15
14
|
require 'active_support/core_ext/date_time/conversions'
|
16
15
|
require 'active_support/core_ext/date/conversions'
|
16
|
+
require 'set'
|
17
17
|
|
18
18
|
module ActiveSupport
|
19
19
|
class << self
|
@@ -38,8 +38,8 @@ module ActiveSupport
|
|
38
38
|
attr_reader :options
|
39
39
|
|
40
40
|
def initialize(options = nil)
|
41
|
-
@options = options
|
42
|
-
@seen =
|
41
|
+
@options = options || {}
|
42
|
+
@seen = Set.new
|
43
43
|
end
|
44
44
|
|
45
45
|
def encode(value, use_options = true)
|
@@ -50,16 +50,16 @@ module ActiveSupport
|
|
50
50
|
end
|
51
51
|
|
52
52
|
# like encode, but only calls as_json, without encoding to string
|
53
|
-
def as_json(value)
|
53
|
+
def as_json(value, use_options = true)
|
54
54
|
check_for_circular_references(value) do
|
55
|
-
value.as_json(options_for(value))
|
55
|
+
use_options ? value.as_json(options_for(value)) : value.as_json
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def options_for(value)
|
60
60
|
if value.is_a?(Array) || value.is_a?(Hash)
|
61
61
|
# hashes and arrays need to get encoder in the options, so that they can detect circular references
|
62
|
-
|
62
|
+
options.merge(:encoder => self)
|
63
63
|
else
|
64
64
|
options
|
65
65
|
end
|
@@ -71,13 +71,12 @@ module ActiveSupport
|
|
71
71
|
|
72
72
|
private
|
73
73
|
def check_for_circular_references(value)
|
74
|
-
|
74
|
+
unless @seen.add?(value.__id__)
|
75
75
|
raise CircularReferenceError, 'object references itself'
|
76
76
|
end
|
77
|
-
@seen.unshift value
|
78
77
|
yield
|
79
78
|
ensure
|
80
|
-
@seen.
|
79
|
+
@seen.delete(value.__id__)
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
@@ -139,8 +138,6 @@ module ActiveSupport
|
|
139
138
|
self.use_standard_json_time_format = true
|
140
139
|
self.escape_html_entities_in_json = false
|
141
140
|
end
|
142
|
-
|
143
|
-
CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError)
|
144
141
|
end
|
145
142
|
end
|
146
143
|
|
@@ -215,7 +212,7 @@ class Array
|
|
215
212
|
def as_json(options = nil) #:nodoc:
|
216
213
|
# use encoder as a proxy to call as_json on all elements, to protect from circular references
|
217
214
|
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
|
218
|
-
map { |v| encoder.as_json(v) }
|
215
|
+
map { |v| encoder.as_json(v, options) }
|
219
216
|
end
|
220
217
|
|
221
218
|
def encode_json(encoder) #:nodoc:
|
@@ -242,7 +239,7 @@ class Hash
|
|
242
239
|
# use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
|
243
240
|
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
|
244
241
|
result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash
|
245
|
-
result[subset.map { |k, v| [k.to_s, encoder.as_json(v)] }]
|
242
|
+
result[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
|
246
243
|
end
|
247
244
|
|
248
245
|
def encode_json(encoder)
|
@@ -284,4 +281,4 @@ class DateTime
|
|
284
281
|
strftime('%Y/%m/%d %H:%M:%S %z')
|
285
282
|
end
|
286
283
|
end
|
287
|
-
end
|
284
|
+
end
|
@@ -1,8 +1,15 @@
|
|
1
1
|
require 'active_support/core_ext/kernel/singleton_class'
|
2
2
|
require 'active_support/core_ext/module/aliasing'
|
3
|
+
require 'active_support/deprecation'
|
3
4
|
|
4
5
|
module ActiveSupport
|
5
6
|
module Memoizable
|
7
|
+
def self.extended(base)
|
8
|
+
ActiveSupport::Deprecation.warn "ActiveSupport::Memoizable is deprecated and will be removed in future releases," \
|
9
|
+
"simply use Ruby memoization pattern instead.", caller
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
6
13
|
def self.memoized_ivar_for(symbol)
|
7
14
|
"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
|
8
15
|
end
|
@@ -79,7 +86,11 @@ module ActiveSupport
|
|
79
86
|
else # else
|
80
87
|
def #{symbol}(*args) # def mime_type(*args)
|
81
88
|
#{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
|
82
|
-
|
89
|
+
args_length = method(:#{original_method}).arity # args_length = method(:_unmemoized_mime_type).arity
|
90
|
+
if args.length == args_length + 1 && # if args.length == args_length + 1 &&
|
91
|
+
(args.last == true || args.last == :reload) # (args.last == true || args.last == :reload)
|
92
|
+
reload = args.pop # reload = args.pop
|
93
|
+
end # end
|
83
94
|
#
|
84
95
|
if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
|
85
96
|
if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
|
@@ -10,15 +10,58 @@ module ActiveSupport
|
|
10
10
|
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but where you don't
|
11
11
|
# want users to be able to determine the value of the payload.
|
12
12
|
class MessageEncryptor
|
13
|
+
module NullSerializer #:nodoc:
|
14
|
+
def self.load(value)
|
15
|
+
value
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.dump(value)
|
19
|
+
value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
13
23
|
class InvalidMessage < StandardError; end
|
14
24
|
OpenSSLCipherError = OpenSSL::Cipher.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
|
15
25
|
|
16
|
-
def initialize(secret,
|
26
|
+
def initialize(secret, options = {})
|
27
|
+
unless options.is_a?(Hash)
|
28
|
+
ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :cipher => 'algorithm' to specify the cipher algorithm."
|
29
|
+
options = { :cipher => options }
|
30
|
+
end
|
31
|
+
|
17
32
|
@secret = secret
|
18
|
-
@cipher = cipher
|
33
|
+
@cipher = options[:cipher] || 'aes-256-cbc'
|
34
|
+
@verifier = MessageVerifier.new(@secret, :serializer => NullSerializer)
|
35
|
+
@serializer = options[:serializer] || Marshal
|
19
36
|
end
|
20
37
|
|
21
38
|
def encrypt(value)
|
39
|
+
ActiveSupport::Deprecation.warn "MessageEncryptor#encrypt is deprecated as it is not safe without a signature. " \
|
40
|
+
"Please use MessageEncryptor#encrypt_and_sign instead."
|
41
|
+
_encrypt(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
def decrypt(value)
|
45
|
+
ActiveSupport::Deprecation.warn "MessageEncryptor#decrypt is deprecated as it is not safe without a signature. " \
|
46
|
+
"Please use MessageEncryptor#decrypt_and_verify instead."
|
47
|
+
_decrypt(value)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Encrypt and sign a message. We need to sign the message in order to avoid padding attacks.
|
51
|
+
# Reference: http://www.limited-entropy.com/padding-oracle-attacks
|
52
|
+
def encrypt_and_sign(value)
|
53
|
+
verifier.generate(_encrypt(value))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Decrypt and verify a message. We need to verify the message in order to avoid padding attacks.
|
57
|
+
# Reference: http://www.limited-entropy.com/padding-oracle-attacks
|
58
|
+
def decrypt_and_verify(value)
|
59
|
+
_decrypt(verifier.verify(value))
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def _encrypt(value)
|
22
65
|
cipher = new_cipher
|
23
66
|
# Rely on OpenSSL for the initialization vector
|
24
67
|
iv = cipher.random_iv
|
@@ -27,13 +70,13 @@ module ActiveSupport
|
|
27
70
|
cipher.key = @secret
|
28
71
|
cipher.iv = iv
|
29
72
|
|
30
|
-
encrypted_data = cipher.update(
|
73
|
+
encrypted_data = cipher.update(@serializer.dump(value))
|
31
74
|
encrypted_data << cipher.final
|
32
75
|
|
33
76
|
[encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--")
|
34
77
|
end
|
35
78
|
|
36
|
-
def
|
79
|
+
def _decrypt(encrypted_message)
|
37
80
|
cipher = new_cipher
|
38
81
|
encrypted_data, iv = encrypted_message.split("--").map {|v| ActiveSupport::Base64.decode64(v)}
|
39
82
|
|
@@ -44,28 +87,17 @@ module ActiveSupport
|
|
44
87
|
decrypted_data = cipher.update(encrypted_data)
|
45
88
|
decrypted_data << cipher.final
|
46
89
|
|
47
|
-
|
90
|
+
@serializer.load(decrypted_data)
|
48
91
|
rescue OpenSSLCipherError, TypeError
|
49
92
|
raise InvalidMessage
|
50
93
|
end
|
51
94
|
|
52
|
-
def
|
53
|
-
|
95
|
+
def new_cipher
|
96
|
+
OpenSSL::Cipher::Cipher.new(@cipher)
|
54
97
|
end
|
55
98
|
|
56
|
-
def
|
57
|
-
|
99
|
+
def verifier
|
100
|
+
@verifier
|
58
101
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
private
|
63
|
-
def new_cipher
|
64
|
-
OpenSSL::Cipher::Cipher.new(@cipher)
|
65
|
-
end
|
66
|
-
|
67
|
-
def verifier
|
68
|
-
MessageVerifier.new(@secret)
|
69
|
-
end
|
70
102
|
end
|
71
103
|
end
|
@@ -18,12 +18,23 @@ module ActiveSupport
|
|
18
18
|
# self.current_user = User.find(id)
|
19
19
|
# end
|
20
20
|
#
|
21
|
+
# By default it uses Marshal to serialize the message. If you want to use another
|
22
|
+
# serialization method, you can set the serializer attribute to something that responds
|
23
|
+
# to dump and load, e.g.:
|
24
|
+
#
|
25
|
+
# @verifier.serializer = YAML
|
21
26
|
class MessageVerifier
|
22
27
|
class InvalidSignature < StandardError; end
|
23
28
|
|
24
|
-
def initialize(secret,
|
29
|
+
def initialize(secret, options = {})
|
30
|
+
unless options.is_a?(Hash)
|
31
|
+
ActiveSupport::Deprecation.warn "The second parameter should be an options hash. Use :digest => 'algorithm' to specify the digest algorithm."
|
32
|
+
options = { :digest => options }
|
33
|
+
end
|
34
|
+
|
25
35
|
@secret = secret
|
26
|
-
@digest = digest
|
36
|
+
@digest = options[:digest] || 'SHA1'
|
37
|
+
@serializer = options[:serializer] || Marshal
|
27
38
|
end
|
28
39
|
|
29
40
|
def verify(signed_message)
|
@@ -31,14 +42,14 @@ module ActiveSupport
|
|
31
42
|
|
32
43
|
data, digest = signed_message.split("--")
|
33
44
|
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
|
34
|
-
|
45
|
+
@serializer.load(ActiveSupport::Base64.decode64(data))
|
35
46
|
else
|
36
47
|
raise InvalidSignature
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
40
51
|
def generate(value)
|
41
|
-
data = ActiveSupport::Base64.encode64s(
|
52
|
+
data = ActiveSupport::Base64.encode64s(@serializer.dump(value))
|
42
53
|
"#{data}--#{generate_digest(data)}"
|
43
54
|
end
|
44
55
|
|
@@ -1,36 +1,102 @@
|
|
1
1
|
module ActiveSupport
|
2
|
-
# Notifications
|
3
|
-
# action in Ruby you just need to do:
|
2
|
+
# = Notifications
|
4
3
|
#
|
5
|
-
#
|
4
|
+
# +ActiveSupport::Notifications+ provides an instrumentation API for Ruby.
|
5
|
+
#
|
6
|
+
# == Instrumenters
|
7
|
+
#
|
8
|
+
# To instrument an event you just need to do:
|
9
|
+
#
|
10
|
+
# ActiveSupport::Notifications.instrument("render", :extra => :information) do
|
6
11
|
# render :text => "Foo"
|
7
12
|
# end
|
8
13
|
#
|
14
|
+
# That executes the block first and notifies all subscribers once done.
|
15
|
+
#
|
16
|
+
# In the example above "render" is the name of the event, and the rest is called
|
17
|
+
# the _payload_. The payload is a mechanism that allows instrumenters to pass
|
18
|
+
# extra information to subscribers. Payloads consist of a hash whose contents
|
19
|
+
# are arbitrary and generally depend on the event.
|
20
|
+
#
|
21
|
+
# == Subscribers
|
22
|
+
#
|
9
23
|
# You can consume those events and the information they provide by registering
|
10
|
-
# a
|
24
|
+
# a subscriber. For instance, let's store all "render" events in an array:
|
11
25
|
#
|
12
|
-
#
|
26
|
+
# events = []
|
13
27
|
#
|
14
|
-
# ActiveSupport::Notifications.subscribe do |*args|
|
15
|
-
#
|
28
|
+
# ActiveSupport::Notifications.subscribe("render") do |*args|
|
29
|
+
# events << ActiveSupport::Notifications::Event.new(*args)
|
16
30
|
# end
|
17
31
|
#
|
18
|
-
#
|
32
|
+
# That code returns right away, you are just subscribing to "render" events.
|
33
|
+
# The block will be called asynchronously whenever someone instruments "render":
|
34
|
+
#
|
35
|
+
# ActiveSupport::Notifications.instrument("render", :extra => :information) do
|
19
36
|
# render :text => "Foo"
|
20
37
|
# end
|
21
38
|
#
|
22
|
-
# event =
|
23
|
-
# event.name # =>
|
39
|
+
# event = events.first
|
40
|
+
# event.name # => "render"
|
24
41
|
# event.duration # => 10 (in milliseconds)
|
25
42
|
# event.payload # => { :extra => :information }
|
26
43
|
#
|
27
|
-
#
|
28
|
-
#
|
44
|
+
# The block in the +subscribe+ call gets the name of the event, start
|
45
|
+
# timestamp, end timestamp, a string with a unique identifier for that event
|
46
|
+
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
|
47
|
+
# that order.
|
29
48
|
#
|
30
|
-
#
|
31
|
-
#
|
49
|
+
# If an exception happens during that particular instrumentation the payload will
|
50
|
+
# have a key +:exception+ with an array of two elements as value: a string with
|
51
|
+
# the name of the exception class, and the exception message.
|
52
|
+
#
|
53
|
+
# As the previous example depicts, the class +ActiveSupport::Notifications::Event+
|
54
|
+
# is able to take the arguments as they come and provide an object-oriented
|
55
|
+
# interface to that data.
|
56
|
+
#
|
57
|
+
# You can also subscribe to all events whose name matches a certain regexp:
|
58
|
+
#
|
59
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
|
60
|
+
# ...
|
32
61
|
# end
|
33
62
|
#
|
63
|
+
# and even pass no argument to +subscribe+, in which case you are subscribing
|
64
|
+
# to all events.
|
65
|
+
#
|
66
|
+
# == Temporary Subscriptions
|
67
|
+
#
|
68
|
+
# Sometimes you do not want to subscribe to an event for the entire life of
|
69
|
+
# the application. There are two ways to unsubscribe.
|
70
|
+
#
|
71
|
+
# === Subscribe While a Block Runs
|
72
|
+
#
|
73
|
+
# You can subscribe to some event temporarily while some block runs. For
|
74
|
+
# example, in
|
75
|
+
#
|
76
|
+
# callback = lambda {|*args| ... }
|
77
|
+
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
|
78
|
+
# ...
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# the callback will be called for all "sql.active_record" events instrumented
|
82
|
+
# during the execution of the block. The callback is unsubscribed automatically
|
83
|
+
# after that.
|
84
|
+
#
|
85
|
+
# === Manual Unsubscription
|
86
|
+
#
|
87
|
+
# The +subscribe+ method returns a subscriber object:
|
88
|
+
#
|
89
|
+
# subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
|
90
|
+
# ...
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# To prevent that block from being called anymore, just unsubscribe passing
|
94
|
+
# that reference:
|
95
|
+
#
|
96
|
+
# ActiveSupport::Notifications.unsubscribe(subscriber)
|
97
|
+
#
|
98
|
+
# == Default Queue
|
99
|
+
#
|
34
100
|
# Notifications ships with a queue implementation that consumes and publish events
|
35
101
|
# to log subscribers in a thread. You can use any queue implementation you want.
|
36
102
|
#
|
@@ -62,6 +128,13 @@ module ActiveSupport
|
|
62
128
|
end
|
63
129
|
end
|
64
130
|
|
131
|
+
def subscribed(callback, *args, &block)
|
132
|
+
subscriber = subscribe(*args, &callback)
|
133
|
+
yield
|
134
|
+
ensure
|
135
|
+
unsubscribe(subscriber)
|
136
|
+
end
|
137
|
+
|
65
138
|
def unsubscribe(args)
|
66
139
|
notifier.unsubscribe(args)
|
67
140
|
@instrumenters.clear
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'active_support/secure_random'
|
2
1
|
require 'active_support/core_ext/module/delegation'
|
3
2
|
|
4
3
|
module ActiveSupport
|
@@ -29,7 +28,7 @@ module ActiveSupport
|
|
29
28
|
|
30
29
|
private
|
31
30
|
def unique_id
|
32
|
-
|
31
|
+
SecureRandom.hex(10)
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|