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.

Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +47 -0
  3. data/README.rdoc +2 -2
  4. data/lib/active_support/cache.rb +62 -49
  5. data/lib/active_support/cache/file_store.rb +2 -2
  6. data/lib/active_support/cache/strategy/local_cache.rb +48 -37
  7. data/lib/active_support/callbacks.rb +27 -8
  8. data/lib/active_support/core_ext.rb +2 -2
  9. data/lib/active_support/core_ext/array/conversions.rb +5 -3
  10. data/lib/active_support/core_ext/array/uniq_by.rb +2 -2
  11. data/lib/active_support/core_ext/benchmark.rb +7 -0
  12. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -0
  13. data/lib/active_support/core_ext/class/attribute.rb +31 -27
  14. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -2
  15. data/lib/active_support/core_ext/date_and_time/calculations.rb +6 -6
  16. data/lib/active_support/core_ext/module/delegation.rb +50 -24
  17. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  18. data/lib/active_support/core_ext/string.rb +0 -1
  19. data/lib/active_support/core_ext/string/conversions.rb +6 -8
  20. data/lib/active_support/core_ext/string/filters.rb +1 -1
  21. data/lib/active_support/core_ext/string/indent.rb +1 -1
  22. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  23. data/lib/active_support/core_ext/string/output_safety.rb +2 -2
  24. data/lib/active_support/hash_with_indifferent_access.rb +2 -2
  25. data/lib/active_support/key_generator.rb +1 -1
  26. data/lib/active_support/log_subscriber.rb +9 -46
  27. data/lib/active_support/message_encryptor.rb +9 -9
  28. data/lib/active_support/message_verifier.rb +3 -3
  29. data/lib/active_support/notifications.rb +22 -1
  30. data/lib/active_support/notifications/instrumenter.rb +2 -2
  31. data/lib/active_support/number_helper.rb +3 -2
  32. data/lib/active_support/per_thread_registry.rb +52 -0
  33. data/lib/active_support/subscriber.rb +93 -0
  34. data/lib/active_support/testing/constant_lookup.rb +2 -0
  35. data/lib/active_support/time_with_zone.rb +2 -0
  36. data/lib/active_support/values/time_zone.rb +5 -3
  37. data/lib/active_support/version.rb +7 -6
  38. data/lib/active_support/xml_mini/jdom.rb +6 -0
  39. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  40. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  41. metadata +14 -7
  42. data/lib/active_support/core_ext/string/xchar.rb +0 -18
@@ -50,6 +50,6 @@ class String
50
50
  length_with_room_for_omission
51
51
  end
52
52
 
53
- self[0...stop] + options[:omission]
53
+ "#{self[0...stop]}#{options[:omission]}"
54
54
  end
55
55
  end
@@ -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 tipically one space or tab, it may be any string.
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 paramter is set to <tt>:en</tt>.
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('&lt;&lt; Accept & Checkout')
43
43
  # # => "&lt;&lt; Accept &amp; Checkout"
44
44
  def html_escape_once(s)
45
- result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] }
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) { |special| JSON_ESCAPE[special] }
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
 
@@ -39,7 +39,7 @@ module ActiveSupport
39
39
  end
40
40
  end
41
41
 
42
- class DummyKeyGenerator # :nodoc:
42
+ class LegacyKeyGenerator # :nodoc:
43
43
  SECRET_MIN_LENGTH = 30 # Characters
44
44
 
45
45
  def initialize(secret)
@@ -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
- @logger ||= Rails.logger
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
- @@log_subscribers ||= []
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
- return unless logger
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
- return unless logger
105
-
106
- finished = Time.now
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
- # key = OpenSSL::Digest::SHA256.new('password').digest # => "\x89\xE0\x156\xAC..."
16
- # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
17
- # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
18
- # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
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.const_defined?(:CipherError) ? OpenSSL::Cipher::CipherError : OpenSSL::CipherError
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
- cipher.iv = iv
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 attribute to
23
- # something that responds to dump and load, e.g.:
22
+ # another serialization method, you can set the serializer in the options
23
+ # hash upon initialization:
24
24
  #
25
- # @verifier.serializer = YAML
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
- Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
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
- # Instrumentors are stored in a thread local.
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.to_str.split('.')
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
- rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
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