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.

Files changed (78) hide show
  1. data/CHANGELOG.md +1539 -30
  2. data/README.rdoc +2 -2
  3. data/lib/active_support.rb +1 -1
  4. data/lib/active_support/benchmarkable.rb +13 -18
  5. data/lib/active_support/buffered_logger.rb +43 -55
  6. data/lib/active_support/cache.rb +109 -115
  7. data/lib/active_support/cache/file_store.rb +6 -17
  8. data/lib/active_support/cache/mem_cache_store.rb +10 -2
  9. data/lib/active_support/cache/null_store.rb +44 -0
  10. data/lib/active_support/cache/strategy/local_cache.rb +2 -2
  11. data/lib/active_support/callbacks.rb +38 -35
  12. data/lib/active_support/concern.rb +5 -10
  13. data/lib/active_support/core_ext/array.rb +1 -0
  14. data/lib/active_support/core_ext/array/access.rb +1 -1
  15. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  16. data/lib/active_support/core_ext/array/wrap.rb +1 -1
  17. data/lib/active_support/core_ext/class.rb +0 -1
  18. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -2
  19. data/lib/active_support/core_ext/date/calculations.rb +37 -14
  20. data/lib/active_support/core_ext/date/freeze.rb +6 -4
  21. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  22. data/lib/active_support/core_ext/enumerable.rb +25 -8
  23. data/lib/active_support/core_ext/file/atomic.rb +1 -2
  24. data/lib/active_support/core_ext/hash/conversions.rb +7 -25
  25. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  26. data/lib/active_support/core_ext/io.rb +15 -0
  27. data/lib/active_support/core_ext/kernel.rb +0 -1
  28. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  29. data/lib/active_support/core_ext/kernel/debugger.rb +1 -7
  30. data/lib/active_support/core_ext/module.rb +2 -2
  31. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -2
  32. data/lib/active_support/core_ext/module/delegation.rb +32 -21
  33. data/lib/active_support/core_ext/module/qualified_const.rb +64 -0
  34. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  35. data/lib/active_support/core_ext/module/synchronization.rb +2 -0
  36. data/lib/active_support/core_ext/object/blank.rb +14 -2
  37. data/lib/active_support/core_ext/object/inclusion.rb +17 -7
  38. data/lib/active_support/core_ext/object/to_json.rb +2 -2
  39. data/lib/active_support/core_ext/range/conversions.rb +1 -1
  40. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  41. data/lib/active_support/core_ext/string/inflections.rb +44 -6
  42. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  43. data/lib/active_support/core_ext/string/output_safety.rb +22 -25
  44. data/lib/active_support/core_ext/time/calculations.rb +66 -12
  45. data/lib/active_support/dependencies.rb +51 -52
  46. data/lib/active_support/file_update_checker.rb +100 -15
  47. data/lib/active_support/hash_with_indifferent_access.rb +5 -1
  48. data/lib/active_support/i18n.rb +1 -1
  49. data/lib/active_support/i18n_railtie.rb +9 -4
  50. data/lib/active_support/inflections.rb +3 -3
  51. data/lib/active_support/inflector/inflections.rb +53 -92
  52. data/lib/active_support/inflector/methods.rb +173 -9
  53. data/lib/active_support/json/decoding.rb +3 -17
  54. data/lib/active_support/json/encoding.rb +11 -14
  55. data/lib/active_support/memoizable.rb +12 -1
  56. data/lib/active_support/message_encryptor.rb +52 -20
  57. data/lib/active_support/message_verifier.rb +15 -4
  58. data/lib/active_support/notifications.rb +87 -14
  59. data/lib/active_support/notifications/instrumenter.rb +1 -2
  60. data/lib/active_support/ordered_hash.rb +7 -3
  61. data/lib/active_support/tagged_logging.rb +63 -0
  62. data/lib/active_support/testing/assertions.rb +1 -1
  63. data/lib/active_support/testing/mochaing.rb +2 -2
  64. data/lib/active_support/testing/performance/ruby.rb +1 -1
  65. data/lib/active_support/testing/setup_and_teardown.rb +4 -12
  66. data/lib/active_support/time_with_zone.rb +6 -3
  67. data/lib/active_support/values/time_zone.rb +3 -7
  68. data/lib/active_support/version.rb +3 -3
  69. data/lib/active_support/xml_mini.rb +3 -3
  70. data/lib/active_support/xml_mini/jdom.rb +4 -10
  71. metadata +28 -21
  72. checksums.yaml +0 -7
  73. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  74. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  75. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  76. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  77. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  78. 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
- # Can't reliably detect whether MultiJson responds to load, since it's
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
- if MultiJson.respond_to?(:adapter)
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
- if MultiJson.respond_to?(:use)
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
- (options || {}).merge(:encoder => self)
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
- if @seen.any? { |object| object.equal?(value) }
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.shift
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
- reload = args.pop if args.last == true || args.last == :reload # reload = args.pop if args.last == true || args.last == :reload
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, cipher = 'aes-256-cbc')
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(Marshal.dump(value))
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 decrypt(encrypted_message)
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
- Marshal.load(decrypted_data)
90
+ @serializer.load(decrypted_data)
48
91
  rescue OpenSSLCipherError, TypeError
49
92
  raise InvalidMessage
50
93
  end
51
94
 
52
- def encrypt_and_sign(value)
53
- verifier.generate(encrypt(value))
95
+ def new_cipher
96
+ OpenSSL::Cipher::Cipher.new(@cipher)
54
97
  end
55
98
 
56
- def decrypt_and_verify(value)
57
- decrypt(verifier.verify(value))
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, digest = 'SHA1')
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
- Marshal.load(ActiveSupport::Base64.decode64(data))
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(Marshal.dump(value))
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 provides an instrumentation API for Ruby. To instrument an
3
- # action in Ruby you just need to do:
2
+ # = Notifications
4
3
  #
5
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
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 log subscriber. For instance, let's store all instrumented events in an array:
24
+ # a subscriber. For instance, let's store all "render" events in an array:
11
25
  #
12
- # @events = []
26
+ # events = []
13
27
  #
14
- # ActiveSupport::Notifications.subscribe do |*args|
15
- # @events << ActiveSupport::Notifications::Event.new(*args)
28
+ # ActiveSupport::Notifications.subscribe("render") do |*args|
29
+ # events << ActiveSupport::Notifications::Event.new(*args)
16
30
  # end
17
31
  #
18
- # ActiveSupport::Notifications.instrument(:render, :extra => :information) do
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 = @events.first
23
- # event.name # => :render
39
+ # event = events.first
40
+ # event.name # => "render"
24
41
  # event.duration # => 10 (in milliseconds)
25
42
  # event.payload # => { :extra => :information }
26
43
  #
27
- # When subscribing to Notifications, you can pass a pattern, to only consume
28
- # events that match the pattern:
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
- # ActiveSupport::Notifications.subscribe(/render/) do |event|
31
- # @render_events << event
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
- ::SecureRandom.hex(10)
31
+ SecureRandom.hex(10)
33
32
  end
34
33
  end
35
34