activesupport 4.1.0.beta2 → 4.1.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +166 -8
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support.rb +1 -1
  5. data/lib/active_support/cache.rb +2 -2
  6. data/lib/active_support/cache/mem_cache_store.rb +6 -8
  7. data/lib/active_support/cache/strategy/local_cache.rb +8 -2
  8. data/lib/active_support/callbacks.rb +1 -1
  9. data/lib/active_support/configurable.rb +1 -1
  10. data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
  11. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +14 -0
  12. data/lib/active_support/core_ext/class/delegating_attributes.rb +9 -8
  13. data/lib/active_support/core_ext/date/conversions.rb +5 -2
  14. data/lib/active_support/core_ext/date_time/calculations.rb +5 -1
  15. data/lib/active_support/core_ext/enumerable.rb +1 -1
  16. data/lib/active_support/core_ext/hash.rb +1 -0
  17. data/lib/active_support/core_ext/hash/compact.rb +20 -0
  18. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  19. data/lib/active_support/core_ext/hash/except.rb +1 -1
  20. data/lib/active_support/core_ext/hash/keys.rb +6 -6
  21. data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
  22. data/lib/active_support/core_ext/module/concerning.rb +1 -1
  23. data/lib/active_support/core_ext/module/delegation.rb +22 -20
  24. data/lib/active_support/core_ext/object/blank.rb +43 -17
  25. data/lib/active_support/core_ext/object/inclusion.rb +12 -0
  26. data/lib/active_support/core_ext/object/json.rb +4 -4
  27. data/lib/active_support/core_ext/object/to_param.rb +7 -3
  28. data/lib/active_support/core_ext/object/to_query.rb +6 -1
  29. data/lib/active_support/core_ext/range/each.rb +1 -2
  30. data/lib/active_support/core_ext/string/access.rb +1 -1
  31. data/lib/active_support/core_ext/string/output_safety.rb +12 -12
  32. data/lib/active_support/core_ext/thread.rb +1 -1
  33. data/lib/active_support/core_ext/time/calculations.rb +6 -4
  34. data/lib/active_support/dependencies.rb +10 -1
  35. data/lib/active_support/hash_with_indifferent_access.rb +7 -3
  36. data/lib/active_support/i18n_railtie.rb +1 -1
  37. data/lib/active_support/inflections.rb +6 -0
  38. data/lib/active_support/inflector/methods.rb +1 -1
  39. data/lib/active_support/json/encoding.rb +7 -1
  40. data/lib/active_support/key_generator.rb +7 -9
  41. data/lib/active_support/message_encryptor.rb +1 -1
  42. data/lib/active_support/multibyte/unicode.rb +36 -36
  43. data/lib/active_support/number_helper/number_to_rounded_converter.rb +37 -7
  44. data/lib/active_support/ordered_hash.rb +4 -0
  45. data/lib/active_support/testing/isolation.rb +2 -0
  46. data/lib/active_support/testing/time_helpers.rb +84 -12
  47. data/lib/active_support/time_with_zone.rb +8 -7
  48. data/lib/active_support/values/time_zone.rb +9 -21
  49. data/lib/active_support/values/unicode_tables.dat +0 -0
  50. data/lib/active_support/version.rb +1 -1
  51. data/lib/active_support/xml_mini.rb +4 -2
  52. metadata +3 -1
@@ -35,7 +35,7 @@ module Enumerable
35
35
  if block_given?
36
36
  Hash[map { |elem| [yield(elem), elem] }]
37
37
  else
38
- to_enum :index_by
38
+ to_enum(:index_by) { size if respond_to?(:size) }
39
39
  end
40
40
  end
41
41
 
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/hash/compact'
1
2
  require 'active_support/core_ext/hash/conversions'
2
3
  require 'active_support/core_ext/hash/deep_merge'
3
4
  require 'active_support/core_ext/hash/except'
@@ -0,0 +1,20 @@
1
+ class Hash
2
+ # Returns a hash with non +nil+ values.
3
+ #
4
+ # hash = { a: true, b: false, c: nil}
5
+ # hash.compact # => { a: true, b: false}
6
+ # hash # => { a: true, b: false, c: nil}
7
+ # { c: nil }.compact # => {}
8
+ def compact
9
+ self.select { |_, value| !value.nil? }
10
+ end
11
+
12
+ # Replaces current hash with non +nil+ values.
13
+ #
14
+ # hash = { a: true, b: false, c: nil}
15
+ # hash.compact! # => { a: true, b: false}
16
+ # hash # => { a: true, b: false}
17
+ def compact!
18
+ self.reject! { |_, value| value.nil? }
19
+ end
20
+ end
@@ -105,7 +105,7 @@ class Hash
105
105
  # hash = Hash.from_xml(xml)
106
106
  # # => {"hash"=>{"foo"=>1, "bar"=>2}}
107
107
  #
108
- # DisallowedType is raise if the XML contains attributes with <tt>type="yaml"</tt> or
108
+ # DisallowedType is raised if the XML contains attributes with <tt>type="yaml"</tt> or
109
109
  # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
110
110
  def from_xml(xml, disallowed_types = nil)
111
111
  ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
@@ -1,5 +1,5 @@
1
1
  class Hash
2
- # Return a hash that includes everything but the given keys. This is useful for
2
+ # Returns a hash that includes everything but the given keys. This is useful for
3
3
  # limiting a set of parameters to everything but a few known toggles:
4
4
  #
5
5
  # @person.update(params[:person].except(:admin))
@@ -1,5 +1,5 @@
1
1
  class Hash
2
- # Return a new hash with all keys converted using the block operation.
2
+ # Returns a new hash with all keys converted using the block operation.
3
3
  #
4
4
  # hash = { name: 'Rob', age: '28' }
5
5
  #
@@ -22,7 +22,7 @@ class Hash
22
22
  self
23
23
  end
24
24
 
25
- # Return a new hash with all keys converted to strings.
25
+ # Returns a new hash with all keys converted to strings.
26
26
  #
27
27
  # hash = { name: 'Rob', age: '28' }
28
28
  #
@@ -38,7 +38,7 @@ class Hash
38
38
  transform_keys!{ |key| key.to_s }
39
39
  end
40
40
 
41
- # Return a new hash with all keys converted to symbols, as long as
41
+ # Returns a new hash with all keys converted to symbols, as long as
42
42
  # they respond to +to_sym+.
43
43
  #
44
44
  # hash = { 'name' => 'Rob', 'age' => '28' }
@@ -73,7 +73,7 @@ class Hash
73
73
  end
74
74
  end
75
75
 
76
- # Return a new hash with all keys converted by the block operation.
76
+ # Returns a new hash with all keys converted by the block operation.
77
77
  # This includes the keys from the root hash and from all
78
78
  # nested hashes.
79
79
  #
@@ -100,7 +100,7 @@ class Hash
100
100
  self
101
101
  end
102
102
 
103
- # Return a new hash with all keys converted to strings.
103
+ # Returns a new hash with all keys converted to strings.
104
104
  # This includes the keys from the root hash and from all
105
105
  # nested hashes.
106
106
  #
@@ -119,7 +119,7 @@ class Hash
119
119
  deep_transform_keys!{ |key| key.to_s }
120
120
  end
121
121
 
122
- # Return a new hash with all keys converted to symbols, as long as
122
+ # Returns a new hash with all keys converted to symbols, as long as
123
123
  # they respond to +to_sym+. This includes the keys from the root hash
124
124
  # and from all nested hashes.
125
125
  #
@@ -27,7 +27,8 @@ class Module
27
27
 
28
28
  def attr_internal_define(attr_name, type)
29
29
  internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
30
- class_eval do # class_eval is necessary on 1.9 or else the methods a made private
30
+ # class_eval is necessary on 1.9 or else the methods are made private
31
+ class_eval do
31
32
  # use native attr_* methods as they are faster on some Ruby implementations
32
33
  send("attr_#{type}", internal_name)
33
34
  end
@@ -63,7 +63,7 @@ class Module
63
63
  #
64
64
  # == Mix-in noise exiled to its own file:
65
65
  #
66
- # Once our chunk of behavior starts pushing the scroll-to-understand it
66
+ # Once our chunk of behavior starts pushing the scroll-to-understand it's
67
67
  # boundary, we give in and move it to a separate file. At this size, the
68
68
  # overhead feels in good proportion to the size of our extraction, despite
69
69
  # diluting our at-a-glance sense of how things really work.
@@ -178,30 +178,32 @@ class Module
178
178
  # whereas conceptually, from the user point of view, the delegator should
179
179
  # be doing one call.
180
180
  if allow_nil
181
- module_eval(<<-EOS, file, line - 3)
182
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
183
- _ = #{to} # _ = client
184
- if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
185
- _.#{method}(#{definition}) # _.name(*args, &block)
186
- end # end
187
- end # end
188
- EOS
181
+ method_def = [
182
+ "def #{method_prefix}#{method}(#{definition})", # def customer_name(*args, &block)
183
+ "_ = #{to}", # _ = client
184
+ "if !_.nil? || nil.respond_to?(:#{method})", # if !_.nil? || nil.respond_to?(:name)
185
+ " _.#{method}(#{definition})", # _.name(*args, &block)
186
+ "end", # end
187
+ "end" # end
188
+ ].join ';'
189
189
  else
190
190
  exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
191
191
 
192
- module_eval(<<-EOS, file, line - 2)
193
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
194
- _ = #{to} # _ = client
195
- _.#{method}(#{definition}) # _.name(*args, &block)
196
- rescue NoMethodError => e # rescue NoMethodError => e
197
- if _.nil? && e.name == :#{method} # if _.nil? && e.name == :name
198
- #{exception} # # add helpful message to the exception
199
- else # else
200
- raise # raise
201
- end # end
202
- end # end
203
- EOS
192
+ method_def = [
193
+ "def #{method_prefix}#{method}(#{definition})", # def customer_name(*args, &block)
194
+ " _ = #{to}", # _ = client
195
+ " _.#{method}(#{definition})", # _.name(*args, &block)
196
+ "rescue NoMethodError => e", # rescue NoMethodError => e
197
+ " if _.nil? && e.name == :#{method}", # if _.nil? && e.name == :name
198
+ " #{exception}", # # add helpful message to the exception
199
+ " else", # else
200
+ " raise", # raise
201
+ " end", # end
202
+ "end" # end
203
+ ].join ';'
204
204
  end
205
+
206
+ module_eval(method_def, file, line)
205
207
  end
206
208
  end
207
209
  end
@@ -4,36 +4,42 @@ class Object
4
4
  # An object is blank if it's false, empty, or a whitespace string.
5
5
  # For example, '', ' ', +nil+, [], and {} are all blank.
6
6
  #
7
- # This simplifies:
7
+ # This simplifies
8
8
  #
9
- # if address.nil? || address.empty?
9
+ # address.nil? || address.empty?
10
10
  #
11
- # ...to:
11
+ # to
12
12
  #
13
- # if address.blank?
13
+ # address.blank?
14
+ #
15
+ # @return [true, false]
14
16
  def blank?
15
- respond_to?(:empty?) ? empty? : !self
17
+ respond_to?(:empty?) ? !!empty? : !self
16
18
  end
17
19
 
18
- # An object is present if it's not <tt>blank?</tt>.
20
+ # An object is present if it's not blank.
21
+ #
22
+ # @return [true, false]
19
23
  def present?
20
24
  !blank?
21
25
  end
22
26
 
23
- # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
24
- # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
27
+ # Returns the receiver if it's present otherwise returns +nil+.
28
+ # <tt>object.presence</tt> is equivalent to
25
29
  #
26
- # This is handy for any representation of objects where blank is the same
27
- # as not present at all. For example, this simplifies a common check for
28
- # HTTP POST/query parameters:
30
+ # object.present? ? object : nil
31
+ #
32
+ # For example, something like
29
33
  #
30
34
  # state = params[:state] if params[:state].present?
31
35
  # country = params[:country] if params[:country].present?
32
36
  # region = state || country || 'US'
33
37
  #
34
- # ...becomes:
38
+ # becomes
35
39
  #
36
40
  # region = params[:state].presence || params[:country].presence || 'US'
41
+ #
42
+ # @return [Object]
37
43
  def presence
38
44
  self if present?
39
45
  end
@@ -43,6 +49,8 @@ class NilClass
43
49
  # +nil+ is blank:
44
50
  #
45
51
  # nil.blank? # => true
52
+ #
53
+ # @return [true]
46
54
  def blank?
47
55
  true
48
56
  end
@@ -52,6 +60,8 @@ class FalseClass
52
60
  # +false+ is blank:
53
61
  #
54
62
  # false.blank? # => true
63
+ #
64
+ # @return [true]
55
65
  def blank?
56
66
  true
57
67
  end
@@ -61,6 +71,8 @@ class TrueClass
61
71
  # +true+ is not blank:
62
72
  #
63
73
  # true.blank? # => false
74
+ #
75
+ # @return [false]
64
76
  def blank?
65
77
  false
66
78
  end
@@ -71,6 +83,8 @@ class Array
71
83
  #
72
84
  # [].blank? # => true
73
85
  # [1,2,3].blank? # => false
86
+ #
87
+ # @return [true, false]
74
88
  alias_method :blank?, :empty?
75
89
  end
76
90
 
@@ -79,18 +93,28 @@ class Hash
79
93
  #
80
94
  # {}.blank? # => true
81
95
  # { key: 'value' }.blank? # => false
96
+ #
97
+ # @return [true, false]
82
98
  alias_method :blank?, :empty?
83
99
  end
84
100
 
85
101
  class String
102
+ BLANK_RE = /\A[[:space:]]*\z/
103
+
86
104
  # A string is blank if it's empty or contains whitespaces only:
87
105
  #
88
- # ''.blank? # => true
89
- # ' '.blank? # => true
90
- # ' '.blank? # => true
91
- # ' something here '.blank? # => false
106
+ # ''.blank? # => true
107
+ # ' '.blank? # => true
108
+ # "\t\n\r".blank? # => true
109
+ # ' blah '.blank? # => false
110
+ #
111
+ # Unicode whitespace is supported:
112
+ #
113
+ # "\u00a0".blank? # => true
114
+ #
115
+ # @return [true, false]
92
116
  def blank?
93
- self =~ /\A[[:space:]]*\z/
117
+ BLANK_RE === self
94
118
  end
95
119
  end
96
120
 
@@ -99,6 +123,8 @@ class Numeric #:nodoc:
99
123
  #
100
124
  # 1.blank? # => false
101
125
  # 0.blank? # => false
126
+ #
127
+ # @return [false]
102
128
  def blank?
103
129
  false
104
130
  end
@@ -12,4 +12,16 @@ class Object
12
12
  rescue NoMethodError
13
13
  raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
14
14
  end
15
+
16
+ # Returns the receiver if it's included in the argument otherwise returns +nil+.
17
+ # Argument must be any object which responds to +#include?+. Usage:
18
+ #
19
+ # params[:bucket_type].present_in %w( project calendar )
20
+ #
21
+ # This will throw an ArgumentError if the argument doesn't respond to +#include?+.
22
+ #
23
+ # @return [Object]
24
+ def present_in(another_object)
25
+ self.in?(another_object) ? self : nil
26
+ end
15
27
  end
@@ -16,12 +16,12 @@ require 'active_support/core_ext/module/aliasing'
16
16
  # otherwise they will always use to_json gem implementation, which is backwards incompatible in
17
17
  # several cases (for instance, the JSON implementation for Hash does not work) with inheritance
18
18
  # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
19
- #
19
+ #
20
20
  # On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
21
21
  # JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
22
22
  # passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
23
23
  # calls to the original to_json method.
24
- #
24
+ #
25
25
  # It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
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}
@@ -163,7 +163,7 @@ end
163
163
  class Time
164
164
  def as_json(options = nil) #:nodoc:
165
165
  if ActiveSupport.use_standard_json_time_format
166
- xmlschema(3)
166
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
167
167
  else
168
168
  %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
169
169
  end
@@ -183,7 +183,7 @@ end
183
183
  class DateTime
184
184
  def as_json(options = nil) #:nodoc:
185
185
  if ActiveSupport.use_standard_json_time_format
186
- xmlschema(3)
186
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
187
187
  else
188
188
  strftime('%Y/%m/%d %H:%M:%S %z')
189
189
  end
@@ -51,8 +51,12 @@ class Hash
51
51
  #
52
52
  # This method is also aliased as +to_query+.
53
53
  def to_param(namespace = nil)
54
- collect do |key, value|
55
- value.to_query(namespace ? "#{namespace}[#{key}]" : key)
56
- end.sort! * '&'
54
+ if empty?
55
+ namespace ? nil.to_query(namespace) : ''
56
+ else
57
+ collect do |key, value|
58
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
59
+ end.sort! * '&'
60
+ end
57
61
  end
58
62
  end
@@ -18,7 +18,12 @@ class Array
18
18
  # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
19
19
  def to_query(key)
20
20
  prefix = "#{key}[]"
21
- collect { |value| value.to_query(prefix) }.join '&'
21
+
22
+ if empty?
23
+ nil.to_query(prefix)
24
+ else
25
+ collect { |value| value.to_query(prefix) }.join '&'
26
+ end
22
27
  end
23
28
  end
24
29
 
@@ -1,5 +1,4 @@
1
1
  require 'active_support/core_ext/module/aliasing'
2
- require 'active_support/core_ext/object/acts_like'
3
2
 
4
3
  class Range #:nodoc:
5
4
 
@@ -17,7 +16,7 @@ class Range #:nodoc:
17
16
 
18
17
  private
19
18
  def ensure_iteration_allowed
20
- if first.acts_like?(:time)
19
+ if first.is_a?(Time)
21
20
  raise TypeError, "can't iterate from #{first.class}"
22
21
  end
23
22
  end
@@ -59,7 +59,7 @@ class String
59
59
  # str.from(0).to(-1) # => "hello"
60
60
  # str.from(1).to(-2) # => "ell"
61
61
  def to(position)
62
- self[0, position + 1]
62
+ self[0..position]
63
63
  end
64
64
 
65
65
  # Returns the first character. If a limit is supplied, returns a substring
@@ -70,9 +70,20 @@ class ERB
70
70
  # them inside a script tag to avoid XSS vulnerability:
71
71
  #
72
72
  # <script>
73
- # var currentUser = <%= json_escape current_user.to_json %>;
73
+ # var currentUser = <%= raw json_escape(current_user.to_json) %>;
74
74
  # </script>
75
75
  #
76
+ # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
77
+ # don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
78
+ # automatically flag the result as HTML safe, since the raw value is unsafe to
79
+ # use inside HTML attributes.
80
+ #
81
+ # If you need to output JSON elsewhere in your HTML, you can just do something
82
+ # like this, as any unsafe characters (including quotation marks) will be
83
+ # automatically escaped for you:
84
+ #
85
+ # <div data-user-info="<%= current_user.to_json %>">...</div>
86
+ #
76
87
  # WARNING: this helper only works with valid JSON. Using this on non-JSON values
77
88
  # will open up serious XSS vulnerabilities. For example, if you replace the
78
89
  # +current_user.to_json+ in the example above with user input instead, the browser
@@ -88,17 +99,6 @@ class ERB
88
99
  # is recommended that you always apply this helper (other libraries, such as the
89
100
  # JSON gem, do not provide this kind of protection by default; also some gems
90
101
  # might override +to_json+ to bypass Active Support's encoder).
91
- #
92
- # The output of this helper method is marked as HTML safe so that you can directly
93
- # include it inside a <tt><script></tt> tag as shown above.
94
- #
95
- # However, it is NOT safe to use the output of this inside an HTML attribute,
96
- # because quotation marks are not escaped. Doing so might break your page's layout.
97
- # If you intend to use this inside an HTML attribute, you should use the
98
- # +html_escape+ helper (or its +h+ alias) instead:
99
- #
100
- # <div data-user-info="<%= h current_user.to_json %>">...</div>
101
- #
102
102
  def json_escape(s)
103
103
  result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
104
104
  s.html_safe? ? result.html_safe : result