activesupport 4.1.16 → 4.2.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.

Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +140 -714
  3. data/README.rdoc +7 -2
  4. data/lib/active_support/backtrace_cleaner.rb +4 -4
  5. data/lib/active_support/cache.rb +18 -20
  6. data/lib/active_support/cache/file_store.rb +5 -0
  7. data/lib/active_support/cache/strategy/local_cache.rb +5 -4
  8. data/lib/active_support/cache/strategy/local_cache_middleware.rb +5 -0
  9. data/lib/active_support/callbacks.rb +92 -140
  10. data/lib/active_support/concern.rb +9 -1
  11. data/lib/active_support/core_ext/array/access.rb +5 -1
  12. data/lib/active_support/core_ext/array/grouping.rb +5 -0
  13. data/lib/active_support/core_ext/class/delegating_attributes.rb +4 -0
  14. data/lib/active_support/core_ext/date_time/calculations.rb +11 -1
  15. data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
  16. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  17. data/lib/active_support/core_ext/hash.rb +1 -0
  18. data/lib/active_support/core_ext/hash/conversions.rb +2 -3
  19. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  20. data/lib/active_support/core_ext/hash/keys.rb +10 -6
  21. data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
  22. data/lib/active_support/core_ext/kernel.rb +3 -2
  23. data/lib/active_support/core_ext/kernel/concern.rb +10 -0
  24. data/lib/active_support/core_ext/kernel/reporting.rb +14 -0
  25. data/lib/active_support/core_ext/load_error.rb +4 -1
  26. data/lib/active_support/core_ext/module/delegation.rb +13 -25
  27. data/lib/active_support/core_ext/numeric/time.rb +1 -19
  28. data/lib/active_support/core_ext/object.rb +1 -0
  29. data/lib/active_support/core_ext/object/duplicable.rb +4 -11
  30. data/lib/active_support/core_ext/object/itself.rb +12 -0
  31. data/lib/active_support/core_ext/object/json.rb +1 -1
  32. data/lib/active_support/core_ext/object/to_query.rb +2 -1
  33. data/lib/active_support/core_ext/object/with_options.rb +15 -2
  34. data/lib/active_support/core_ext/string/access.rb +4 -4
  35. data/lib/active_support/core_ext/string/filters.rb +25 -1
  36. data/lib/active_support/core_ext/string/inflections.rb +3 -1
  37. data/lib/active_support/core_ext/string/output_safety.rb +29 -19
  38. data/lib/active_support/core_ext/thread.rb +7 -0
  39. data/lib/active_support/core_ext/time/conversions.rb +1 -1
  40. data/lib/active_support/core_ext/time/zones.rb +0 -1
  41. data/lib/active_support/dependencies.rb +5 -4
  42. data/lib/active_support/duration.rb +2 -3
  43. data/lib/active_support/gem_version.rb +3 -3
  44. data/lib/active_support/hash_with_indifferent_access.rb +13 -5
  45. data/lib/active_support/i18n_railtie.rb +1 -7
  46. data/lib/active_support/inflector/inflections.rb +1 -1
  47. data/lib/active_support/inflector/methods.rb +39 -15
  48. data/lib/active_support/json/encoding.rb +0 -4
  49. data/lib/active_support/logger.rb +0 -14
  50. data/lib/active_support/logger_silence.rb +3 -24
  51. data/lib/active_support/message_encryptor.rb +2 -1
  52. data/lib/active_support/multibyte/unicode.rb +5 -3
  53. data/lib/active_support/notifications.rb +7 -2
  54. data/lib/active_support/notifications/fanout.rb +11 -6
  55. data/lib/active_support/number_helper.rb +7 -8
  56. data/lib/active_support/number_helper/number_to_rounded_converter.rb +2 -2
  57. data/lib/active_support/test_case.rb +3 -13
  58. data/lib/active_support/testing/assertions.rb +1 -1
  59. data/lib/active_support/testing/declarative.rb +1 -25
  60. data/lib/active_support/testing/isolation.rb +16 -6
  61. data/lib/active_support/testing/tagged_logging.rb +1 -1
  62. data/lib/active_support/testing/time_helpers.rb +5 -1
  63. data/lib/active_support/time.rb +0 -2
  64. data/lib/active_support/time_with_zone.rb +14 -3
  65. data/lib/active_support/values/time_zone.rb +76 -75
  66. data/lib/active_support/xml_mini.rb +0 -3
  67. data/lib/active_support/xml_mini/jdom.rb +5 -6
  68. data/lib/active_support/xml_mini/rexml.rb +5 -6
  69. metadata +17 -16
  70. data/lib/active_support/core_ext/object/to_json.rb +0 -5
  71. data/lib/active_support/file_watcher.rb +0 -36
  72. data/lib/active_support/security_utils.rb +0 -27
@@ -19,7 +19,7 @@
19
19
  class Object
20
20
  # Can you safely dup this object?
21
21
  #
22
- # False for +nil+, +false+, +true+, symbol, and number objects;
22
+ # False for +nil+, +false+, +true+, symbol, number and BigDecimal(in 1.9.x) objects;
23
23
  # true otherwise.
24
24
  def duplicable?
25
25
  true
@@ -78,6 +78,9 @@ end
78
78
 
79
79
  require 'bigdecimal'
80
80
  class BigDecimal
81
+ # Needed to support Ruby 1.9.x, as it doesn't allow dup on BigDecimal, instead
82
+ # raises TypeError exception. Checking here on the runtime whether BigDecimal
83
+ # will allow dup or not.
81
84
  begin
82
85
  BigDecimal.new('4.56').dup
83
86
 
@@ -88,13 +91,3 @@ class BigDecimal
88
91
  # can't dup, so use superclass implementation
89
92
  end
90
93
  end
91
-
92
- class Method
93
- # Methods are not duplicable:
94
- #
95
- # method(:puts).duplicable? # => false
96
- # method(:puts).dup # => TypeError: allocator undefined for Method
97
- def duplicable?
98
- false
99
- end
100
- end
@@ -0,0 +1,12 @@
1
+ class Object
2
+ unless respond_to?(:itself) # TODO: Remove this file when we drop support for Ruby < 2.2
3
+ # Returns the object itself. Useful when dealing with a chaining scenario, like Active Record scopes:
4
+ #
5
+ # Event.public_send(state.presence_in([ :trashed, :drafted ]) || :itself).order(:created_at)
6
+ #
7
+ # @return Object
8
+ def itself
9
+ self
10
+ end
11
+ end
12
+ end
@@ -26,7 +26,7 @@ require 'active_support/core_ext/module/aliasing'
26
26
  # bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
27
27
  # ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
28
28
  # should give exactly the same results with or without active support.
29
- [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
29
+ [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].each do |klass|
30
30
  klass.class_eval do
31
31
  def to_json_with_active_support_encoder(options = nil)
32
32
  if options.is_a?(::JSON::State)
@@ -1,3 +1,5 @@
1
+ require 'cgi'
2
+
1
3
  class Object
2
4
  # Alias of <tt>to_s</tt>.
3
5
  def to_param
@@ -7,7 +9,6 @@ class Object
7
9
  # Converts an object into a string suitable for use as a URL query string,
8
10
  # using the given <tt>key</tt> as the param name.
9
11
  def to_query(key)
10
- require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
11
12
  "#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
12
13
  end
13
14
  end
@@ -34,9 +34,22 @@ class Object
34
34
  # body i18n.t :body, user_name: user.name
35
35
  # end
36
36
  #
37
+ # When you don't pass an explicit receiver, it executes the whole block
38
+ # in merging options context:
39
+ #
40
+ # class Account < ActiveRecord::Base
41
+ # with_options dependent: :destroy do
42
+ # has_many :customers
43
+ # has_many :products
44
+ # has_many :invoices
45
+ # has_many :expenses
46
+ # end
47
+ # end
48
+ #
37
49
  # <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
38
50
  # Each nesting level will merge inherited defaults in addition to their own.
39
- def with_options(options)
40
- yield ActiveSupport::OptionMerger.new(self, options)
51
+ def with_options(options, &block)
52
+ option_merger = ActiveSupport::OptionMerger.new(self, options)
53
+ block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
41
54
  end
42
55
  end
@@ -64,7 +64,7 @@ class String
64
64
 
65
65
  # Returns the first character. If a limit is supplied, returns a substring
66
66
  # from the beginning of the string until it reaches the limit value. If the
67
- # given limit is greater than or equal to the string length, returns self.
67
+ # given limit is greater than or equal to the string length, returns a copy of self.
68
68
  #
69
69
  # str = "hello"
70
70
  # str.first # => "h"
@@ -76,7 +76,7 @@ class String
76
76
  if limit == 0
77
77
  ''
78
78
  elsif limit >= size
79
- self
79
+ self.dup
80
80
  else
81
81
  to(limit - 1)
82
82
  end
@@ -84,7 +84,7 @@ class String
84
84
 
85
85
  # Returns the last character of the string. If a limit is supplied, returns a substring
86
86
  # from the end of the string until it reaches the limit value (counting backwards). If
87
- # the given limit is greater than or equal to the string length, returns self.
87
+ # the given limit is greater than or equal to the string length, returns a copy of self.
88
88
  #
89
89
  # str = "hello"
90
90
  # str.last # => "o"
@@ -96,7 +96,7 @@ class String
96
96
  if limit == 0
97
97
  ''
98
98
  elsif limit >= size
99
- self
99
+ self.dup
100
100
  else
101
101
  from(-limit)
102
102
  end
@@ -3,7 +3,7 @@ class String
3
3
  # the string, and then changing remaining consecutive whitespace
4
4
  # groups into one space each.
5
5
  #
6
- # Note that it handles both ASCII and Unicode whitespace.
6
+ # Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
7
7
  #
8
8
  # %{ Multi-line
9
9
  # string }.squish # => "Multi-line string"
@@ -62,4 +62,28 @@ class String
62
62
 
63
63
  "#{self[0, stop]}#{omission}"
64
64
  end
65
+
66
+ # Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
67
+ #
68
+ # 'Once upon a time in a world far far away'.truncate_words(4)
69
+ # # => "Once upon a time..."
70
+ #
71
+ # Pass a string or regexp <tt>:separator</tt> to specify a different separator of words:
72
+ #
73
+ # 'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
74
+ # # => "Once<br>upon<br>a<br>time<br>in..."
75
+ #
76
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "..."):
77
+ #
78
+ # 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
79
+ # # => "And they found that many... (continued)"
80
+ def truncate_words(words_count, options = {})
81
+ sep = options[:separator] || /\s+/
82
+ sep = Regexp.escape(sep.to_s) unless Regexp === sep
83
+ if self =~ /\A((?:.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
84
+ $1 + (options[:omission] || '...')
85
+ else
86
+ dup
87
+ end
88
+ end
65
89
  end
@@ -31,7 +31,7 @@ class String
31
31
  def pluralize(count = nil, locale = :en)
32
32
  locale = count if count.is_a?(Symbol)
33
33
  if count == 1
34
- self
34
+ self.dup
35
35
  else
36
36
  ActiveSupport::Inflector.pluralize(self, locale)
37
37
  end
@@ -130,6 +130,8 @@ class String
130
130
  #
131
131
  # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
132
132
  # 'Inflections'.demodulize # => "Inflections"
133
+ # '::Inflections'.demodulize # => "Inflections"
134
+ # ''.demodulize # => ''
133
135
  #
134
136
  # See also +deconstantize+.
135
137
  def demodulize
@@ -1,5 +1,6 @@
1
1
  require 'erb'
2
2
  require 'active_support/core_ext/kernel/singleton_class'
3
+ require 'active_support/deprecation'
3
4
 
4
5
  class ERB
5
6
  module Util
@@ -18,12 +19,7 @@ class ERB
18
19
  # puts html_escape('is a > 0 & a < 10?')
19
20
  # # => is a &gt; 0 &amp; a &lt; 10?
20
21
  def html_escape(s)
21
- s = s.to_s
22
- if s.html_safe?
23
- s
24
- else
25
- s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE).html_safe
26
- end
22
+ unwrapped_html_escape(s).html_safe
27
23
  end
28
24
 
29
25
  # Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
@@ -35,6 +31,18 @@ class ERB
35
31
  singleton_class.send(:remove_method, :html_escape)
36
32
  module_function :html_escape
37
33
 
34
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
35
+ # This method is not for public consumption! Seriously!
36
+ def unwrapped_html_escape(s) # :nodoc:
37
+ s = s.to_s
38
+ if s.html_safe?
39
+ s
40
+ else
41
+ s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE)
42
+ end
43
+ end
44
+ module_function :unwrapped_html_escape
45
+
38
46
  # A utility method for escaping HTML without affecting existing escaped entities.
39
47
  #
40
48
  # html_escape_once('1 < 2 &amp; 3')
@@ -124,7 +132,7 @@ module ActiveSupport #:nodoc:
124
132
  class SafeBuffer < String
125
133
  UNSAFE_STRING_METHODS = %w(
126
134
  capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
127
- slice squeeze strip sub succ swapcase tr tr_s upcase prepend
135
+ slice squeeze strip sub succ swapcase tr tr_s upcase
128
136
  )
129
137
 
130
138
  alias_method :original_concat, :concat
@@ -142,11 +150,7 @@ module ActiveSupport #:nodoc:
142
150
  else
143
151
  if html_safe?
144
152
  new_safe_buffer = super
145
-
146
- if new_safe_buffer
147
- new_safe_buffer.instance_eval { @html_safe = true }
148
- end
149
-
153
+ new_safe_buffer.instance_eval { @html_safe = true }
150
154
  new_safe_buffer
151
155
  else
152
156
  to_str[*args]
@@ -174,14 +178,19 @@ module ActiveSupport #:nodoc:
174
178
  end
175
179
 
176
180
  def concat(value)
177
- if !html_safe? || value.html_safe?
178
- super(value)
179
- else
180
- super(ERB::Util.h(value))
181
- end
181
+ super(html_escape_interpolated_argument(value))
182
182
  end
183
183
  alias << concat
184
184
 
185
+ def prepend(value)
186
+ super(html_escape_interpolated_argument(value))
187
+ end
188
+
189
+ def prepend!(value)
190
+ ActiveSupport::Deprecation.deprecation_warning "ActiveSupport::SafeBuffer#prepend!", :prepend
191
+ prepend value
192
+ end
193
+
185
194
  def +(other)
186
195
  dup.concat(other)
187
196
  end
@@ -210,7 +219,7 @@ module ActiveSupport #:nodoc:
210
219
  end
211
220
 
212
221
  def encode_with(coder)
213
- coder.represent_object nil, to_str
222
+ coder.represent_scalar nil, to_str
214
223
  end
215
224
 
216
225
  UNSAFE_STRING_METHODS.each do |unsafe_method|
@@ -231,7 +240,8 @@ module ActiveSupport #:nodoc:
231
240
  private
232
241
 
233
242
  def html_escape_interpolated_argument(arg)
234
- (!html_safe? || arg.html_safe?) ? arg : ERB::Util.h(arg)
243
+ (!html_safe? || arg.html_safe?) ? arg :
244
+ arg.to_s.gsub(ERB::Util::HTML_ESCAPE_REGEXP, ERB::Util::HTML_ESCAPE)
235
245
  end
236
246
  end
237
247
  end
@@ -62,6 +62,13 @@ class Thread
62
62
  _locals.has_key?(key.to_sym)
63
63
  end
64
64
 
65
+ # Freezes the thread so that thread local variables cannot be set via
66
+ # Thread#thread_variable_set, nor can fiber local variables be set.
67
+ #
68
+ # me = Thread.current
69
+ # me.freeze
70
+ # me.thread_variable_set(:oliver, "a") #=> RuntimeError: can't modify frozen thread locals
71
+ # me[:oliver] = "a" #=> RuntimeError: can't modify frozen thread locals
65
72
  def freeze
66
73
  _locals.freeze
67
74
  super
@@ -20,7 +20,7 @@ class Time
20
20
  :iso8601 => lambda { |time| time.iso8601 }
21
21
  }
22
22
 
23
- # Converts to a formatted string. See DATE_FORMATS for builtin formats.
23
+ # Converts to a formatted string. See DATE_FORMATS for built-in formats.
24
24
  #
25
25
  # This method is aliased to <tt>to_s</tt>.
26
26
  #
@@ -1,5 +1,4 @@
1
1
  require 'active_support/time_with_zone'
2
- require 'active_support/core_ext/time/acts_like'
3
2
  require 'active_support/core_ext/date_and_time/zones'
4
3
 
5
4
  class Time
@@ -180,10 +180,11 @@ module ActiveSupport #:nodoc:
180
180
  Dependencies.load_missing_constant(from_mod, const_name)
181
181
  end
182
182
 
183
- # Dependencies assumes the name of the module reflects the nesting (unless
184
- # it can be proven that is not the case), and the path to the file that
185
- # defines the constant. Anonymous modules cannot follow these conventions
186
- # and we assume therefore the user wants to refer to a top-level constant.
183
+ # We assume that the name of the module reflects the nesting
184
+ # (unless it can be proven that is not the case) and the path to the file
185
+ # that defines the constant. Anonymous modules cannot follow these
186
+ # conventions and therefore we assume that the user wants to refer to a
187
+ # top-level constant.
187
188
  def guess_for_anonymous(const_name)
188
189
  if Object.const_defined?(const_name)
189
190
  raise NameError.new "#{const_name} cannot be autoloaded from an anonymous class or module", const_name
@@ -78,7 +78,7 @@ module ActiveSupport
78
78
  reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
79
79
  sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
80
80
  map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
81
- to_sentence(locale: ::I18n.default_locale)
81
+ to_sentence(:locale => :en)
82
82
  end
83
83
 
84
84
  def as_json(options = nil) #:nodoc:
@@ -105,8 +105,7 @@ module ActiveSupport
105
105
 
106
106
  # We define it as a workaround to Ruby 2.0.0-p353 bug.
107
107
  # For more information, check rails/rails#13055.
108
- # It should be dropped once a new Ruby patch-level
109
- # release after 2.0.0-p353 happens.
108
+ # Remove it when we drop support for 2.0.0-p353.
110
109
  def ===(other) #:nodoc:
111
110
  value === other
112
111
  end
@@ -6,9 +6,9 @@ module ActiveSupport
6
6
 
7
7
  module VERSION
8
8
  MAJOR = 4
9
- MINOR = 1
10
- TINY = 16
11
- PRE = nil
9
+ MINOR = 2
10
+ TINY = 0
11
+ PRE = "beta1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -55,7 +55,7 @@ module ActiveSupport
55
55
  end
56
56
 
57
57
  def initialize(constructor = {})
58
- if constructor.is_a?(Hash)
58
+ if constructor.respond_to?(:to_hash)
59
59
  super()
60
60
  update(constructor)
61
61
  else
@@ -75,6 +75,7 @@ module ActiveSupport
75
75
  hash = hash.to_hash
76
76
  new(hash).tap do |new_hash|
77
77
  new_hash.default = hash.default
78
+ new_hash.default_proc = hash.default_proc if hash.default_proc
78
79
  end
79
80
  end
80
81
 
@@ -176,7 +177,14 @@ module ActiveSupport
176
177
  indices.collect { |key| self[convert_key(key)] }
177
178
  end
178
179
 
179
- # Returns an exact copy of the hash.
180
+ # Returns a shallow copy of the hash.
181
+ #
182
+ # hash = ActiveSupport::HashWithIndifferentAccess.new({ a: { b: 'b' } })
183
+ # dup = hash.dup
184
+ # dup[:a][:c] = 'c'
185
+ #
186
+ # hash[:a][:c] # => nil
187
+ # dup[:a][:c] # => "c"
180
188
  def dup
181
189
  self.class.new(self).tap do |new_hash|
182
190
  new_hash.default = default
@@ -238,11 +246,11 @@ module ActiveSupport
238
246
 
239
247
  # Convert to a regular hash with string keys.
240
248
  def to_hash
241
- _new_hash= {}
249
+ _new_hash = Hash.new(default)
242
250
  each do |key, value|
243
- _new_hash[convert_key(key)] = convert_value(value, for: :to_hash)
251
+ _new_hash[key] = convert_value(value, for: :to_hash)
244
252
  end
245
- Hash.new(default).merge!(_new_hash)
253
+ _new_hash
246
254
  end
247
255
 
248
256
  protected
@@ -8,8 +8,6 @@ module I18n
8
8
  config.i18n.railties_load_path = []
9
9
  config.i18n.load_path = []
10
10
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
11
- # Enforce I18n to check the available locales when setting a locale.
12
- config.i18n.enforce_available_locales = true
13
11
 
14
12
  # Set the i18n configuration after initialization since a lot of
15
13
  # configuration is still usually done in application initializers.
@@ -36,11 +34,7 @@ module I18n
36
34
  # Avoid issues with setting the default_locale by disabling available locales
37
35
  # check while configuring.
38
36
  enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
39
-
40
- if enforce_available_locales.nil?
41
- enforce_available_locales = I18n.enforce_available_locales
42
- end
43
-
37
+ enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
44
38
  I18n.enforce_available_locales = false
45
39
 
46
40
  app.config.i18n.each do |setting, value|
@@ -160,7 +160,7 @@ module ActiveSupport
160
160
  # uncountable 'money', 'information'
161
161
  # uncountable %w( money information rice )
162
162
  def uncountable(*words)
163
- (@uncountables << words).flatten!
163
+ @uncountables += words.flatten.map(&:downcase)
164
164
  end
165
165
 
166
166
  # Specifies a humanized form of a string by a regular expression rule or