activesupport 4.0.0.beta1 → 4.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -0
- data/README.rdoc +2 -2
- data/lib/active_support/cache.rb +62 -49
- data/lib/active_support/cache/file_store.rb +2 -2
- data/lib/active_support/cache/strategy/local_cache.rb +48 -37
- data/lib/active_support/callbacks.rb +27 -8
- data/lib/active_support/core_ext.rb +2 -2
- data/lib/active_support/core_ext/array/conversions.rb +5 -3
- data/lib/active_support/core_ext/array/uniq_by.rb +2 -2
- data/lib/active_support/core_ext/benchmark.rb +7 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -0
- data/lib/active_support/core_ext/class/attribute.rb +31 -27
- data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +50 -24
- data/lib/active_support/core_ext/module/deprecation.rb +2 -2
- data/lib/active_support/core_ext/string.rb +0 -1
- data/lib/active_support/core_ext/string/conversions.rb +6 -8
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +2 -2
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/log_subscriber.rb +9 -46
- data/lib/active_support/message_encryptor.rb +9 -9
- data/lib/active_support/message_verifier.rb +3 -3
- data/lib/active_support/notifications.rb +22 -1
- data/lib/active_support/notifications/instrumenter.rb +2 -2
- data/lib/active_support/number_helper.rb +3 -2
- data/lib/active_support/per_thread_registry.rb +52 -0
- data/lib/active_support/subscriber.rb +93 -0
- data/lib/active_support/testing/constant_lookup.rb +2 -0
- data/lib/active_support/time_with_zone.rb +2 -0
- data/lib/active_support/values/time_zone.rb +5 -3
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +6 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- metadata +14 -7
- data/lib/active_support/core_ext/string/xchar.rb +0 -18
@@ -29,7 +29,7 @@ class String
|
|
29
29
|
# "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
|
30
30
|
# "foo".indent(2, "\t") # => "\t\tfoo"
|
31
31
|
#
|
32
|
-
# While +indent_string+ is
|
32
|
+
# While +indent_string+ is typically one space or tab, it may be any string.
|
33
33
|
#
|
34
34
|
# The third argument, +indent_empty_lines+, is a flag that says whether
|
35
35
|
# empty lines should be indented. Default is false.
|
@@ -41,7 +41,7 @@ class String
|
|
41
41
|
#
|
42
42
|
# If the optional parameter +locale+ is specified,
|
43
43
|
# the word will be singularized as a word of that language.
|
44
|
-
# By default, this
|
44
|
+
# By default, this parameter is set to <tt>:en</tt>.
|
45
45
|
# You must define your own inflection rules for languages other than English.
|
46
46
|
#
|
47
47
|
# 'posts'.singularize # => "post"
|
@@ -42,7 +42,7 @@ class ERB
|
|
42
42
|
# html_escape_once('<< Accept & Checkout')
|
43
43
|
# # => "<< Accept & Checkout"
|
44
44
|
def html_escape_once(s)
|
45
|
-
result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP
|
45
|
+
result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
46
46
|
s.html_safe? ? result.html_safe : result
|
47
47
|
end
|
48
48
|
|
@@ -60,7 +60,7 @@ class ERB
|
|
60
60
|
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
|
61
61
|
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
|
62
62
|
def json_escape(s)
|
63
|
-
result = s.to_s.gsub(JSON_ESCAPE_REGEXP
|
63
|
+
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
|
64
64
|
s.html_safe? ? result.html_safe : result
|
65
65
|
end
|
66
66
|
|
@@ -78,7 +78,7 @@ module ActiveSupport
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def self.[](*args)
|
81
|
-
new.merge(Hash[*args])
|
81
|
+
new.merge!(Hash[*args])
|
82
82
|
end
|
83
83
|
|
84
84
|
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
@@ -223,7 +223,7 @@ module ActiveSupport
|
|
223
223
|
def deep_stringify_keys; dup end
|
224
224
|
undef :symbolize_keys!
|
225
225
|
undef :deep_symbolize_keys!
|
226
|
-
def symbolize_keys; to_hash.symbolize_keys end
|
226
|
+
def symbolize_keys; to_hash.symbolize_keys! end
|
227
227
|
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
|
228
228
|
def to_options!; self end
|
229
229
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
2
|
require 'active_support/core_ext/class/attribute'
|
3
|
+
require 'active_support/subscriber'
|
3
4
|
|
4
5
|
module ActiveSupport
|
5
6
|
# ActiveSupport::LogSubscriber is an object set to consume
|
@@ -33,7 +34,7 @@ module ActiveSupport
|
|
33
34
|
# Log subscriber also has some helpers to deal with logging and automatically
|
34
35
|
# flushes all logs when the request finishes (via action_dispatch.callback
|
35
36
|
# notification) in a Rails environment.
|
36
|
-
class LogSubscriber
|
37
|
+
class LogSubscriber < Subscriber
|
37
38
|
# Embed in a String to clear all previous ANSI sequences.
|
38
39
|
CLEAR = "\e[0m"
|
39
40
|
BOLD = "\e[1m"
|
@@ -53,26 +54,15 @@ module ActiveSupport
|
|
53
54
|
|
54
55
|
class << self
|
55
56
|
def logger
|
56
|
-
if defined?(Rails) && Rails.respond_to?(:logger)
|
57
|
-
|
57
|
+
@logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
|
58
|
+
Rails.logger
|
58
59
|
end
|
59
|
-
@logger
|
60
60
|
end
|
61
61
|
|
62
62
|
attr_writer :logger
|
63
63
|
|
64
|
-
def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
|
65
|
-
log_subscribers << log_subscriber
|
66
|
-
|
67
|
-
log_subscriber.public_methods(false).each do |event|
|
68
|
-
next if %w{ start finish }.include?(event.to_s)
|
69
|
-
|
70
|
-
notifier.subscribe("#{event}.#{namespace}", log_subscriber)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
64
|
def log_subscribers
|
75
|
-
|
65
|
+
subscribers
|
76
66
|
end
|
77
67
|
|
78
68
|
# Flush all log_subscribers' logger.
|
@@ -81,39 +71,18 @@ module ActiveSupport
|
|
81
71
|
end
|
82
72
|
end
|
83
73
|
|
84
|
-
def initialize
|
85
|
-
@queue_key = [self.class.name, object_id].join "-"
|
86
|
-
super
|
87
|
-
end
|
88
|
-
|
89
74
|
def logger
|
90
75
|
LogSubscriber.logger
|
91
76
|
end
|
92
77
|
|
93
78
|
def start(name, id, payload)
|
94
|
-
|
95
|
-
|
96
|
-
e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
|
97
|
-
parent = event_stack.last
|
98
|
-
parent << e if parent
|
99
|
-
|
100
|
-
event_stack.push e
|
79
|
+
super if logger
|
101
80
|
end
|
102
81
|
|
103
82
|
def finish(name, id, payload)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
event = event_stack.pop
|
108
|
-
event.end = finished
|
109
|
-
event.payload.merge!(payload)
|
110
|
-
|
111
|
-
method = name.split('.').first
|
112
|
-
begin
|
113
|
-
send(method, event)
|
114
|
-
rescue Exception => e
|
115
|
-
logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
|
116
|
-
end
|
83
|
+
super if logger
|
84
|
+
rescue Exception => e
|
85
|
+
logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
|
117
86
|
end
|
118
87
|
|
119
88
|
protected
|
@@ -136,11 +105,5 @@ module ActiveSupport
|
|
136
105
|
bold = bold ? BOLD : ""
|
137
106
|
"#{bold}#{color}#{text}#{CLEAR}"
|
138
107
|
end
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
def event_stack
|
143
|
-
Thread.current[@queue_key] ||= []
|
144
|
-
end
|
145
108
|
end
|
146
109
|
end
|
@@ -12,10 +12,11 @@ module ActiveSupport
|
|
12
12
|
# This can be used in situations similar to the <tt>MessageVerifier</tt>, but
|
13
13
|
# where you don't want users to be able to determine the value of the payload.
|
14
14
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# crypt.
|
15
|
+
# salt = SecureRandom.random_bytes(64)
|
16
|
+
# key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
|
17
|
+
# crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
|
18
|
+
# encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
|
19
|
+
# crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
|
19
20
|
class MessageEncryptor
|
20
21
|
module NullSerializer #:nodoc:
|
21
22
|
def self.load(value)
|
@@ -28,7 +29,7 @@ module ActiveSupport
|
|
28
29
|
end
|
29
30
|
|
30
31
|
class InvalidMessage < StandardError; end
|
31
|
-
OpenSSLCipherError = OpenSSL::Cipher
|
32
|
+
OpenSSLCipherError = OpenSSL::Cipher::CipherError
|
32
33
|
|
33
34
|
# Initialize a new MessageEncryptor. +secret+ must be at least as long as
|
34
35
|
# the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
|
@@ -66,12 +67,11 @@ module ActiveSupport
|
|
66
67
|
|
67
68
|
def _encrypt(value)
|
68
69
|
cipher = new_cipher
|
69
|
-
# Rely on OpenSSL for the initialization vector
|
70
|
-
iv = cipher.random_iv
|
71
|
-
|
72
70
|
cipher.encrypt
|
73
71
|
cipher.key = @secret
|
74
|
-
|
72
|
+
|
73
|
+
# Rely on OpenSSL for the initialization vector
|
74
|
+
iv = cipher.random_iv
|
75
75
|
|
76
76
|
encrypted_data = cipher.update(@serializer.dump(value))
|
77
77
|
encrypted_data << cipher.final
|
@@ -19,10 +19,10 @@ module ActiveSupport
|
|
19
19
|
# end
|
20
20
|
#
|
21
21
|
# By default it uses Marshal to serialize the message. If you want to use
|
22
|
-
# another serialization method, you can set the serializer
|
23
|
-
#
|
22
|
+
# another serialization method, you can set the serializer in the options
|
23
|
+
# hash upon initialization:
|
24
24
|
#
|
25
|
-
# @verifier.serializer
|
25
|
+
# @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
|
26
26
|
class MessageVerifier
|
27
27
|
class InvalidSignature < StandardError; end
|
28
28
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/notifications/instrumenter'
|
2
2
|
require 'active_support/notifications/fanout'
|
3
|
+
require 'active_support/per_thread_registry'
|
3
4
|
|
4
5
|
module ActiveSupport
|
5
6
|
# = Notifications
|
@@ -177,7 +178,27 @@ module ActiveSupport
|
|
177
178
|
end
|
178
179
|
|
179
180
|
def instrumenter
|
180
|
-
|
181
|
+
InstrumentationRegistry.instrumenter_for(notifier)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# This class is a registry which holds all of the +Instrumenter+ objects
|
186
|
+
# in a particular thread local. To access the +Instrumenter+ object for a
|
187
|
+
# particular +notifier+, you can call the following method:
|
188
|
+
#
|
189
|
+
# InstrumentationRegistry.instrumenter_for(notifier)
|
190
|
+
#
|
191
|
+
# The instrumenters for multiple notifiers are held in a single instance of
|
192
|
+
# this class.
|
193
|
+
class InstrumentationRegistry # :nodoc:
|
194
|
+
extend ActiveSupport::PerThreadRegistry
|
195
|
+
|
196
|
+
def initialize
|
197
|
+
@registry = {}
|
198
|
+
end
|
199
|
+
|
200
|
+
def instrumenter_for(notifier)
|
201
|
+
@registry[notifier] ||= Instrumenter.new(notifier)
|
181
202
|
end
|
182
203
|
end
|
183
204
|
|
@@ -2,7 +2,7 @@ require 'securerandom'
|
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module Notifications
|
5
|
-
#
|
5
|
+
# Instrumenters are stored in a thread local.
|
6
6
|
class Instrumenter
|
7
7
|
attr_reader :id
|
8
8
|
|
@@ -17,7 +17,7 @@ module ActiveSupport
|
|
17
17
|
def instrument(name, payload={})
|
18
18
|
start name, payload
|
19
19
|
begin
|
20
|
-
yield
|
20
|
+
yield payload
|
21
21
|
rescue Exception => e
|
22
22
|
payload[:exception] = [e.class.name, e.message]
|
23
23
|
raise e
|
@@ -295,7 +295,7 @@ module ActiveSupport
|
|
295
295
|
|
296
296
|
options = format_options(options[:locale]).merge!(options)
|
297
297
|
|
298
|
-
parts = number.to_s.
|
298
|
+
parts = number.to_s.split('.')
|
299
299
|
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
300
300
|
parts.join(options[:separator])
|
301
301
|
end
|
@@ -356,7 +356,8 @@ module ActiveSupport
|
|
356
356
|
digits, rounded_number = 1, 0
|
357
357
|
else
|
358
358
|
digits = (Math.log10(number.abs) + 1).floor
|
359
|
-
|
359
|
+
multiplier = 10 ** (digits - precision)
|
360
|
+
rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
|
360
361
|
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
|
361
362
|
end
|
362
363
|
precision -= digits
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
# This module is used to encapsulate access to thread local variables.
|
3
|
+
#
|
4
|
+
# Instead of polluting the thread locals namespace:
|
5
|
+
#
|
6
|
+
# Thread.current[:connection_handler]
|
7
|
+
#
|
8
|
+
# you define a class that extends this module:
|
9
|
+
#
|
10
|
+
# module ActiveRecord
|
11
|
+
# class RuntimeRegistry
|
12
|
+
# extend ActiveSupport::PerThreadRegistry
|
13
|
+
#
|
14
|
+
# attr_accessor :connection_handler
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# and invoke the declared instance accessors as class methods. So
|
19
|
+
#
|
20
|
+
# ActiveRecord::RuntimeRegistry.connection_handler = connection_handler
|
21
|
+
#
|
22
|
+
# sets a connection handler local to the current thread, and
|
23
|
+
#
|
24
|
+
# ActiveRecord::RuntimeRegistry.connection_handler
|
25
|
+
#
|
26
|
+
# returns a connection handler local to the current thread.
|
27
|
+
#
|
28
|
+
# This feature is accomplished by instantiating the class and storing the
|
29
|
+
# instance as a thread local keyed by the class name. In the example above
|
30
|
+
# a key "ActiveRecord::RuntimeRegistry" is stored in <tt>Thread.current</tt>.
|
31
|
+
# The class methods proxy to said thread local instance.
|
32
|
+
#
|
33
|
+
# If the class has an initializer, it must accept no arguments.
|
34
|
+
module PerThreadRegistry
|
35
|
+
protected
|
36
|
+
|
37
|
+
def method_missing(name, *args, &block) # :nodoc:
|
38
|
+
# Caches the method definition as a singleton method of the receiver.
|
39
|
+
define_singleton_method(name) do |*a, &b|
|
40
|
+
per_thread_registry_instance.public_send(name, *a, &b)
|
41
|
+
end
|
42
|
+
|
43
|
+
send(name, *args, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def per_thread_registry_instance
|
49
|
+
Thread.current[name] ||= new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'active_support/per_thread_registry'
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
# ActiveSupport::Subscriber is an object set to consume
|
5
|
+
# ActiveSupport::Notifications. The subscriber dispatches notifications to
|
6
|
+
# a registered object based on its given namespace.
|
7
|
+
#
|
8
|
+
# An example would be Active Record subscriber responsible for collecting
|
9
|
+
# statistics about queries:
|
10
|
+
#
|
11
|
+
# module ActiveRecord
|
12
|
+
# class StatsSubscriber < ActiveSupport::Subscriber
|
13
|
+
# def sql(event)
|
14
|
+
# Statsd.timing("sql.#{event.payload[:name]}", event.duration)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# And it's finally registered as:
|
20
|
+
#
|
21
|
+
# ActiveRecord::StatsSubscriber.attach_to :active_record
|
22
|
+
#
|
23
|
+
# Since we need to know all instance methods before attaching the log
|
24
|
+
# subscriber, the line above should be called after your subscriber definition.
|
25
|
+
#
|
26
|
+
# After configured, whenever a "sql.active_record" notification is published,
|
27
|
+
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
|
28
|
+
# the +sql+ method.
|
29
|
+
class Subscriber
|
30
|
+
class << self
|
31
|
+
|
32
|
+
# Attach the subscriber to a namespace.
|
33
|
+
def attach_to(namespace, subscriber=new, notifier=ActiveSupport::Notifications)
|
34
|
+
subscribers << subscriber
|
35
|
+
|
36
|
+
subscriber.public_methods(false).each do |event|
|
37
|
+
next if %w{ start finish }.include?(event.to_s)
|
38
|
+
|
39
|
+
notifier.subscribe("#{event}.#{namespace}", subscriber)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def subscribers
|
44
|
+
@@subscribers ||= []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize
|
49
|
+
@queue_key = [self.class.name, object_id].join "-"
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def start(name, id, payload)
|
54
|
+
e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
|
55
|
+
parent = event_stack.last
|
56
|
+
parent << e if parent
|
57
|
+
|
58
|
+
event_stack.push e
|
59
|
+
end
|
60
|
+
|
61
|
+
def finish(name, id, payload)
|
62
|
+
finished = Time.now
|
63
|
+
event = event_stack.pop
|
64
|
+
event.end = finished
|
65
|
+
event.payload.merge!(payload)
|
66
|
+
|
67
|
+
method = name.split('.').first
|
68
|
+
send(method, event)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def event_stack
|
74
|
+
SubscriberQueueRegistry.get_queue(@queue_key)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# This is a registry for all the event stacks kept for subscribers.
|
79
|
+
#
|
80
|
+
# See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
|
81
|
+
# for further details.
|
82
|
+
class SubscriberQueueRegistry # :nodoc:
|
83
|
+
extend PerThreadRegistry
|
84
|
+
|
85
|
+
def initialize
|
86
|
+
@registry = {}
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_queue(queue_key)
|
90
|
+
@registry[queue_key] ||= []
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|