activesupport 4.0.13 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +406 -418
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -2
- data/lib/active_support/backtrace_cleaner.rb +8 -8
- data/lib/active_support/benchmarkable.rb +0 -10
- data/lib/active_support/cache/file_store.rb +32 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -7
- data/lib/active_support/cache/memory_store.rb +1 -0
- data/lib/active_support/cache/strategy/local_cache.rb +11 -30
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
- data/lib/active_support/cache.rb +75 -41
- data/lib/active_support/callbacks.rb +482 -261
- data/lib/active_support/concern.rb +23 -7
- data/lib/active_support/configurable.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +11 -1
- data/lib/active_support/core_ext/array/conversions.rb +2 -17
- data/lib/active_support/core_ext/array/grouping.rb +29 -12
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +16 -0
- data/lib/active_support/core_ext/class/attribute.rb +1 -2
- data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -170
- data/lib/active_support/core_ext/class/delegating_attributes.rb +13 -8
- data/lib/active_support/core_ext/class/subclasses.rb +0 -2
- data/lib/active_support/core_ext/class.rb +0 -1
- data/lib/active_support/core_ext/date/calculations.rb +10 -0
- data/lib/active_support/core_ext/date/conversions.rb +9 -1
- data/lib/active_support/core_ext/date/zones.rb +2 -33
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -11
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +45 -22
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/date_time/zones.rb +3 -21
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +51 -0
- data/lib/active_support/core_ext/enumerable.rb +17 -1
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/compact.rb +24 -0
- data/lib/active_support/core_ext/hash/conversions.rb +9 -8
- data/lib/active_support/core_ext/hash/except.rb +8 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
- data/lib/active_support/core_ext/hash/keys.rb +25 -19
- data/lib/active_support/core_ext/hash/slice.rb +8 -2
- data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
- data/lib/active_support/core_ext/hash.rb +2 -1
- data/lib/active_support/core_ext/integer/time.rb +0 -15
- data/lib/active_support/core_ext/kernel/concern.rb +10 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +13 -2
- data/lib/active_support/core_ext/kernel.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +4 -1
- data/lib/active_support/core_ext/marshal.rb +8 -5
- data/lib/active_support/core_ext/module/aliasing.rb +2 -2
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +53 -25
- data/lib/active_support/core_ext/module/deprecation.rb +0 -2
- data/lib/active_support/core_ext/module/introspection.rb +0 -16
- data/lib/active_support/core_ext/module/method_transplanting.rb +13 -0
- data/lib/active_support/core_ext/module.rb +1 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
- data/lib/active_support/core_ext/numeric/time.rb +4 -29
- data/lib/active_support/core_ext/object/blank.rb +44 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
- data/lib/active_support/core_ext/object/duplicable.rb +72 -33
- data/lib/active_support/core_ext/object/inclusion.rb +16 -15
- data/lib/active_support/core_ext/object/itself.rb +15 -0
- data/lib/active_support/core_ext/object/json.rb +197 -0
- data/lib/active_support/core_ext/object/to_query.rb +14 -6
- data/lib/active_support/core_ext/object/try.rb +36 -14
- data/lib/active_support/core_ext/object/with_options.rb +30 -3
- data/lib/active_support/core_ext/object.rb +2 -1
- data/lib/active_support/core_ext/string/access.rb +35 -35
- data/lib/active_support/core_ext/string/conversions.rb +10 -9
- data/lib/active_support/core_ext/string/exclude.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +51 -3
- data/lib/active_support/core_ext/string/inflections.rb +15 -10
- data/lib/active_support/core_ext/string/output_safety.rb +97 -33
- data/lib/active_support/core_ext/string/zones.rb +1 -0
- data/lib/active_support/core_ext/thread.rb +12 -5
- data/lib/active_support/core_ext/time/calculations.rb +47 -68
- data/lib/active_support/core_ext/time/compatibility.rb +14 -0
- data/lib/active_support/core_ext/time/conversions.rb +4 -2
- data/lib/active_support/core_ext/time/zones.rb +2 -20
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/dependencies/autoload.rb +1 -1
- data/lib/active_support/dependencies.rb +64 -25
- data/lib/active_support/deprecation/behaviors.rb +4 -4
- data/lib/active_support/deprecation.rb +4 -4
- data/lib/active_support/duration.rb +55 -11
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +15 -0
- data/lib/active_support/hash_with_indifferent_access.rb +39 -11
- data/lib/active_support/i18n.rb +4 -4
- data/lib/active_support/i18n_railtie.rb +1 -7
- data/lib/active_support/inflections.rb +6 -1
- data/lib/active_support/inflector/inflections.rb +19 -19
- data/lib/active_support/inflector/methods.rb +66 -25
- data/lib/active_support/json/decoding.rb +15 -22
- data/lib/active_support/json/encoding.rb +125 -286
- data/lib/active_support/key_generator.rb +8 -10
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber/test_helper.rb +1 -1
- data/lib/active_support/logger.rb +51 -1
- data/lib/active_support/logger_silence.rb +7 -4
- data/lib/active_support/logger_thread_safe_level.rb +32 -0
- data/lib/active_support/message_encryptor.rb +14 -6
- data/lib/active_support/message_verifier.rb +16 -12
- data/lib/active_support/multibyte/chars.rb +2 -3
- data/lib/active_support/multibyte/unicode.rb +46 -58
- data/lib/active_support/notifications/fanout.rb +12 -7
- data/lib/active_support/notifications/instrumenter.rb +2 -1
- data/lib/active_support/notifications.rb +11 -6
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +23 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +87 -0
- data/lib/active_support/number_helper.rb +32 -324
- data/lib/active_support/ordered_options.rb +8 -0
- data/lib/active_support/per_thread_registry.rb +13 -10
- data/lib/active_support/security_utils.rb +27 -0
- data/lib/active_support/subscriber.rb +35 -3
- data/lib/active_support/test_case.rb +52 -19
- data/lib/active_support/testing/assertions.rb +1 -31
- data/lib/active_support/testing/autorun.rb +2 -2
- data/lib/active_support/testing/constant_lookup.rb +1 -5
- data/lib/active_support/testing/declarative.rb +7 -21
- data/lib/active_support/testing/isolation.rb +29 -69
- data/lib/active_support/testing/setup_and_teardown.rb +17 -2
- data/lib/active_support/testing/tagged_logging.rb +2 -2
- data/lib/active_support/testing/time_helpers.rb +134 -0
- data/lib/active_support/time.rb +0 -2
- data/lib/active_support/time_with_zone.rb +60 -40
- data/lib/active_support/values/time_zone.rb +101 -101
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +4 -7
- data/lib/active_support/xml_mini/jdom.rb +6 -5
- data/lib/active_support/xml_mini/libxml.rb +1 -3
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
- data/lib/active_support/xml_mini/nokogiri.rb +1 -3
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
- data/lib/active_support/xml_mini/rexml.rb +7 -8
- data/lib/active_support/xml_mini.rb +33 -15
- data/lib/active_support.rb +27 -2
- metadata +43 -43
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/object/to_json.rb +0 -27
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
@@ -12,12 +12,14 @@ 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
|
-
# salt = SecureRandom.random_bytes(64)
|
15
|
+
# salt = SecureRandom.random_bytes(64)
|
16
16
|
# key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
|
17
17
|
# crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
|
18
18
|
# encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
|
19
19
|
# crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
|
20
20
|
class MessageEncryptor
|
21
|
+
DEFAULT_CIPHER = "aes-256-cbc"
|
22
|
+
|
21
23
|
module NullSerializer #:nodoc:
|
22
24
|
def self.load(value)
|
23
25
|
value
|
@@ -40,6 +42,7 @@ module ActiveSupport
|
|
40
42
|
# Options:
|
41
43
|
# * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
|
42
44
|
# <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
|
45
|
+
# * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
|
43
46
|
# * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
|
44
47
|
def initialize(secret, *signature_key_or_options)
|
45
48
|
options = signature_key_or_options.extract_options!
|
@@ -47,7 +50,7 @@ module ActiveSupport
|
|
47
50
|
@secret = secret
|
48
51
|
@sign_secret = sign_secret
|
49
52
|
@cipher = options[:cipher] || 'aes-256-cbc'
|
50
|
-
@verifier = MessageVerifier.new(@sign_secret || @secret, :serializer
|
53
|
+
@verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
|
51
54
|
@serializer = options[:serializer] || Marshal
|
52
55
|
end
|
53
56
|
|
@@ -63,6 +66,11 @@ module ActiveSupport
|
|
63
66
|
_decrypt(verifier.verify(value))
|
64
67
|
end
|
65
68
|
|
69
|
+
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
|
70
|
+
def self.key_len(cipher = DEFAULT_CIPHER)
|
71
|
+
OpenSSL::Cipher.new(cipher).key_len
|
72
|
+
end
|
73
|
+
|
66
74
|
private
|
67
75
|
|
68
76
|
def _encrypt(value)
|
@@ -76,12 +84,12 @@ module ActiveSupport
|
|
76
84
|
encrypted_data = cipher.update(@serializer.dump(value))
|
77
85
|
encrypted_data << cipher.final
|
78
86
|
|
79
|
-
|
87
|
+
"#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
|
80
88
|
end
|
81
89
|
|
82
90
|
def _decrypt(encrypted_message)
|
83
91
|
cipher = new_cipher
|
84
|
-
encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.
|
92
|
+
encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
|
85
93
|
|
86
94
|
cipher.decrypt
|
87
95
|
cipher.key = @secret
|
@@ -91,12 +99,12 @@ module ActiveSupport
|
|
91
99
|
decrypted_data << cipher.final
|
92
100
|
|
93
101
|
@serializer.load(decrypted_data)
|
94
|
-
rescue OpenSSLCipherError, TypeError
|
102
|
+
rescue OpenSSLCipherError, TypeError, ArgumentError
|
95
103
|
raise InvalidMessage
|
96
104
|
end
|
97
105
|
|
98
106
|
def new_cipher
|
99
|
-
OpenSSL::Cipher
|
107
|
+
OpenSSL::Cipher.new(@cipher)
|
100
108
|
end
|
101
109
|
|
102
110
|
def verifier
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'base64'
|
2
2
|
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'active_support/security_utils'
|
3
4
|
|
4
5
|
module ActiveSupport
|
5
6
|
# +MessageVerifier+ makes it easy to generate and verify messages which are
|
@@ -27,37 +28,40 @@ module ActiveSupport
|
|
27
28
|
class InvalidSignature < StandardError; end
|
28
29
|
|
29
30
|
def initialize(secret, options = {})
|
31
|
+
raise ArgumentError, 'Secret should not be nil.' unless secret
|
30
32
|
@secret = secret
|
31
33
|
@digest = options[:digest] || 'SHA1'
|
32
34
|
@serializer = options[:serializer] || Marshal
|
33
35
|
end
|
34
36
|
|
35
37
|
def verify(signed_message)
|
36
|
-
raise InvalidSignature if signed_message.blank?
|
38
|
+
raise InvalidSignature if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
|
37
39
|
|
38
40
|
data, digest = signed_message.split("--")
|
39
|
-
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
|
40
|
-
|
41
|
+
if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
|
42
|
+
begin
|
43
|
+
@serializer.load(decode(data))
|
44
|
+
rescue ArgumentError => argument_error
|
45
|
+
raise InvalidSignature if argument_error.message =~ %r{invalid base64}
|
46
|
+
raise
|
47
|
+
end
|
41
48
|
else
|
42
49
|
raise InvalidSignature
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
46
53
|
def generate(value)
|
47
|
-
data =
|
54
|
+
data = encode(@serializer.dump(value))
|
48
55
|
"#{data}--#{generate_digest(data)}"
|
49
56
|
end
|
50
57
|
|
51
58
|
private
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
l = a.unpack "C#{a.bytesize}"
|
59
|
+
def encode(data)
|
60
|
+
::Base64.strict_encode64(data)
|
61
|
+
end
|
57
62
|
|
58
|
-
|
59
|
-
|
60
|
-
res == 0
|
63
|
+
def decode(data)
|
64
|
+
::Base64.strict_decode64(data)
|
61
65
|
end
|
62
66
|
|
63
67
|
def generate_digest(data)
|
@@ -56,11 +56,10 @@ module ActiveSupport #:nodoc:
|
|
56
56
|
|
57
57
|
# Forward all undefined methods to the wrapped string.
|
58
58
|
def method_missing(method, *args, &block)
|
59
|
+
result = @wrapped_string.__send__(method, *args, &block)
|
59
60
|
if method.to_s =~ /!$/
|
60
|
-
result = @wrapped_string.__send__(method, *args, &block)
|
61
61
|
self if result
|
62
62
|
else
|
63
|
-
result = @wrapped_string.__send__(method, *args, &block)
|
64
63
|
result.kind_of?(String) ? chars(result) : result
|
65
64
|
end
|
66
65
|
end
|
@@ -87,7 +86,7 @@ module ActiveSupport #:nodoc:
|
|
87
86
|
@wrapped_string.split(*args).map { |i| self.class.new(i) }
|
88
87
|
end
|
89
88
|
|
90
|
-
# Works like
|
89
|
+
# Works like <tt>String#slice!</tt>, but returns an instance of
|
91
90
|
# Chars, or nil if the string was not modified.
|
92
91
|
def slice!(*args)
|
93
92
|
chars(@wrapped_string.slice!(*args))
|
@@ -11,7 +11,7 @@ module ActiveSupport
|
|
11
11
|
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
|
12
12
|
|
13
13
|
# The Unicode version that is supported by the implementation
|
14
|
-
UNICODE_VERSION = '
|
14
|
+
UNICODE_VERSION = '7.0.0'
|
15
15
|
|
16
16
|
# The default normalization used for operations that require
|
17
17
|
# normalization. It can be set to any of the normalizations
|
@@ -211,57 +211,44 @@ module ActiveSupport
|
|
211
211
|
codepoints
|
212
212
|
end
|
213
213
|
|
214
|
-
#
|
215
|
-
#
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
214
|
+
# Ruby >= 2.1 has String#scrub, which is faster than the workaround used for < 2.1.
|
215
|
+
# Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
|
216
|
+
if '<3'.respond_to?(:scrub) && !defined?(Rubinius)
|
217
|
+
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
|
218
|
+
# resulting in a valid UTF-8 string.
|
219
|
+
#
|
220
|
+
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
|
221
|
+
# encoding is entirely CP1252 or ISO-8859-1.
|
222
|
+
def tidy_bytes(string, force = false)
|
223
|
+
return string if string.empty?
|
224
|
+
return recode_windows1252_chars(string) if force
|
225
|
+
string.scrub { |bad| recode_windows1252_chars(bad) }
|
224
226
|
end
|
227
|
+
else
|
228
|
+
def tidy_bytes(string, force = false)
|
229
|
+
return string if string.empty?
|
230
|
+
return recode_windows1252_chars(string) if force
|
231
|
+
|
232
|
+
# We can't transcode to the same format, so we choose a nearly-identical encoding.
|
233
|
+
# We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
|
234
|
+
# CP1252 when we get errors. The final string will be 'converted' back to UTF-8
|
235
|
+
# before returning.
|
236
|
+
reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
|
237
|
+
|
238
|
+
source = string.dup
|
239
|
+
out = ''.force_encoding(Encoding::UTF_16LE)
|
240
|
+
|
241
|
+
loop do
|
242
|
+
reader.primitive_convert(source, out)
|
243
|
+
_, _, _, error_bytes, _ = reader.primitive_errinfo
|
244
|
+
break if error_bytes.nil?
|
245
|
+
out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
|
246
|
+
end
|
225
247
|
|
226
|
-
|
227
|
-
conts_expected = 0
|
228
|
-
last_lead = 0
|
229
|
-
|
230
|
-
bytes.each_index do |i|
|
231
|
-
|
232
|
-
byte = bytes[i]
|
233
|
-
is_cont = byte > 127 && byte < 192
|
234
|
-
is_lead = byte > 191 && byte < 245
|
235
|
-
is_unused = byte > 240
|
236
|
-
is_restricted = byte > 244
|
248
|
+
reader.finish
|
237
249
|
|
238
|
-
|
239
|
-
if is_unused || is_restricted
|
240
|
-
bytes[i] = tidy_byte(byte)
|
241
|
-
elsif is_cont
|
242
|
-
# Not expecting continuation byte? Clean up. Otherwise, now expect one less.
|
243
|
-
conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
|
244
|
-
else
|
245
|
-
if conts_expected > 0
|
246
|
-
# Expected continuation, but got ASCII or leading? Clean backwards up to
|
247
|
-
# the leading byte.
|
248
|
-
(1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
|
249
|
-
conts_expected = 0
|
250
|
-
end
|
251
|
-
if is_lead
|
252
|
-
# Final byte is leading? Clean it.
|
253
|
-
if i == bytes.length - 1
|
254
|
-
bytes[i] = tidy_byte(bytes.last)
|
255
|
-
else
|
256
|
-
# Valid leading byte? Expect continuations determined by position of
|
257
|
-
# first zero bit, with max of 3.
|
258
|
-
conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
|
259
|
-
last_lead = i
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
250
|
+
out.encode!(Encoding::UTF_8)
|
263
251
|
end
|
264
|
-
bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
|
265
252
|
end
|
266
253
|
|
267
254
|
# Returns the KC normalization of the string by default. NFKC is
|
@@ -306,6 +293,13 @@ module ActiveSupport
|
|
306
293
|
class Codepoint
|
307
294
|
attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
|
308
295
|
|
296
|
+
# Initializing Codepoint object with default values
|
297
|
+
def initialize
|
298
|
+
@combining_class = 0
|
299
|
+
@uppercase_mapping = 0
|
300
|
+
@lowercase_mapping = 0
|
301
|
+
end
|
302
|
+
|
309
303
|
def swapcase_mapping
|
310
304
|
uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
|
311
305
|
end
|
@@ -341,7 +335,7 @@ module ActiveSupport
|
|
341
335
|
begin
|
342
336
|
@codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
|
343
337
|
rescue => e
|
344
|
-
|
338
|
+
raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
|
345
339
|
end
|
346
340
|
|
347
341
|
# Redefine the === method so we can write shorter rules for grapheme cluster breaks
|
@@ -373,6 +367,7 @@ module ActiveSupport
|
|
373
367
|
private
|
374
368
|
|
375
369
|
def apply_mapping(string, mapping) #:nodoc:
|
370
|
+
database.codepoints
|
376
371
|
string.each_codepoint.map do |codepoint|
|
377
372
|
cp = database.codepoints[codepoint]
|
378
373
|
if cp and (ncp = cp.send(mapping)) and ncp > 0
|
@@ -383,20 +378,13 @@ module ActiveSupport
|
|
383
378
|
end.pack('U*')
|
384
379
|
end
|
385
380
|
|
386
|
-
def
|
387
|
-
|
388
|
-
[database.cp1252[byte] || byte].pack("U").unpack("C*")
|
389
|
-
elsif byte < 192
|
390
|
-
[194, byte]
|
391
|
-
else
|
392
|
-
[195, byte - 64]
|
393
|
-
end
|
381
|
+
def recode_windows1252_chars(string)
|
382
|
+
string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
|
394
383
|
end
|
395
384
|
|
396
385
|
def database
|
397
386
|
@database ||= UnicodeDatabase.new
|
398
387
|
end
|
399
|
-
|
400
388
|
end
|
401
389
|
end
|
402
390
|
end
|
@@ -25,9 +25,15 @@ module ActiveSupport
|
|
25
25
|
subscriber
|
26
26
|
end
|
27
27
|
|
28
|
-
def unsubscribe(
|
28
|
+
def unsubscribe(subscriber_or_name)
|
29
29
|
synchronize do
|
30
|
-
|
30
|
+
case subscriber_or_name
|
31
|
+
when String
|
32
|
+
@subscribers.reject! { |s| s.matches?(subscriber_or_name) }
|
33
|
+
else
|
34
|
+
@subscribers.delete(subscriber_or_name)
|
35
|
+
end
|
36
|
+
|
31
37
|
@listeners_for.clear
|
32
38
|
end
|
33
39
|
end
|
@@ -97,16 +103,15 @@ module ActiveSupport
|
|
97
103
|
end
|
98
104
|
|
99
105
|
def subscribed_to?(name)
|
100
|
-
@pattern === name
|
106
|
+
@pattern === name
|
101
107
|
end
|
102
108
|
|
103
|
-
def matches?(
|
104
|
-
|
105
|
-
@pattern && @pattern === subscriber_or_name
|
109
|
+
def matches?(name)
|
110
|
+
@pattern && @pattern === name
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
109
|
-
class Timed < Evented
|
114
|
+
class Timed < Evented # :nodoc:
|
110
115
|
def publish(name, *args)
|
111
116
|
@delegate.call name, *args
|
112
117
|
end
|
@@ -16,7 +16,7 @@ module ActiveSupport
|
|
16
16
|
# render text: 'Foo'
|
17
17
|
# end
|
18
18
|
#
|
19
|
-
# That executes the block
|
19
|
+
# That first executes the block and then notifies all subscribers once done.
|
20
20
|
#
|
21
21
|
# In the example above +render+ is the name of the event, and the rest is called
|
22
22
|
# the _payload_. The payload is a mechanism that allows instrumenters to pass
|
@@ -141,10 +141,15 @@ module ActiveSupport
|
|
141
141
|
#
|
142
142
|
# ActiveSupport::Notifications.unsubscribe(subscriber)
|
143
143
|
#
|
144
|
+
# You can also unsubscribe by passing the name of the subscriber object. Note
|
145
|
+
# that this will unsubscribe all subscriptions with the given name:
|
146
|
+
#
|
147
|
+
# ActiveSupport::Notifications.unsubscribe("render")
|
148
|
+
#
|
144
149
|
# == Default Queue
|
145
150
|
#
|
146
|
-
# Notifications ships with a queue implementation that consumes and
|
147
|
-
# to log subscribers
|
151
|
+
# Notifications ships with a queue implementation that consumes and publishes events
|
152
|
+
# to all log subscribers. You can use any queue implementation you want.
|
148
153
|
#
|
149
154
|
module Notifications
|
150
155
|
class << self
|
@@ -173,12 +178,12 @@ module ActiveSupport
|
|
173
178
|
unsubscribe(subscriber)
|
174
179
|
end
|
175
180
|
|
176
|
-
def unsubscribe(
|
177
|
-
notifier.unsubscribe(
|
181
|
+
def unsubscribe(subscriber_or_name)
|
182
|
+
notifier.unsubscribe(subscriber_or_name)
|
178
183
|
end
|
179
184
|
|
180
185
|
def instrumenter
|
181
|
-
InstrumentationRegistry.instrumenter_for(notifier)
|
186
|
+
InstrumentationRegistry.instance.instrumenter_for(notifier)
|
182
187
|
end
|
183
188
|
end
|
184
189
|
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'active_support/core_ext/big_decimal/conversions'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
4
|
+
require 'active_support/i18n'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
6
|
+
|
7
|
+
module ActiveSupport
|
8
|
+
module NumberHelper
|
9
|
+
class NumberConverter # :nodoc:
|
10
|
+
# Default and i18n option namespace per class
|
11
|
+
class_attribute :namespace
|
12
|
+
|
13
|
+
# Does the object need a number that is a valid float?
|
14
|
+
class_attribute :validate_float
|
15
|
+
|
16
|
+
attr_reader :number, :opts
|
17
|
+
|
18
|
+
DEFAULTS = {
|
19
|
+
# Used in number_to_delimited
|
20
|
+
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
|
21
|
+
format: {
|
22
|
+
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
|
23
|
+
separator: ".",
|
24
|
+
# Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
|
25
|
+
delimiter: ",",
|
26
|
+
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
|
27
|
+
precision: 3,
|
28
|
+
# If set to true, precision will mean the number of significant digits instead
|
29
|
+
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
|
30
|
+
significant: false,
|
31
|
+
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
|
32
|
+
strip_insignificant_zeros: false
|
33
|
+
},
|
34
|
+
|
35
|
+
# Used in number_to_currency
|
36
|
+
currency: {
|
37
|
+
format: {
|
38
|
+
format: "%u%n",
|
39
|
+
negative_format: "-%u%n",
|
40
|
+
unit: "$",
|
41
|
+
# These five are to override number.format and are optional
|
42
|
+
separator: ".",
|
43
|
+
delimiter: ",",
|
44
|
+
precision: 2,
|
45
|
+
significant: false,
|
46
|
+
strip_insignificant_zeros: false
|
47
|
+
}
|
48
|
+
},
|
49
|
+
|
50
|
+
# Used in number_to_percentage
|
51
|
+
percentage: {
|
52
|
+
format: {
|
53
|
+
delimiter: "",
|
54
|
+
format: "%n%"
|
55
|
+
}
|
56
|
+
},
|
57
|
+
|
58
|
+
# Used in number_to_rounded
|
59
|
+
precision: {
|
60
|
+
format: {
|
61
|
+
delimiter: ""
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
# Used in number_to_human_size and number_to_human
|
66
|
+
human: {
|
67
|
+
format: {
|
68
|
+
# These five are to override number.format and are optional
|
69
|
+
delimiter: "",
|
70
|
+
precision: 3,
|
71
|
+
significant: true,
|
72
|
+
strip_insignificant_zeros: true
|
73
|
+
},
|
74
|
+
# Used in number_to_human_size
|
75
|
+
storage_units: {
|
76
|
+
# Storage units output formatting.
|
77
|
+
# %u is the storage unit, %n is the number (default: 2 MB)
|
78
|
+
format: "%n %u",
|
79
|
+
units: {
|
80
|
+
byte: "Bytes",
|
81
|
+
kb: "KB",
|
82
|
+
mb: "MB",
|
83
|
+
gb: "GB",
|
84
|
+
tb: "TB"
|
85
|
+
}
|
86
|
+
},
|
87
|
+
# Used in number_to_human
|
88
|
+
decimal_units: {
|
89
|
+
format: "%n %u",
|
90
|
+
# Decimal units output formatting
|
91
|
+
# By default we will only quantify some of the exponents
|
92
|
+
# but the commented ones might be defined or overridden
|
93
|
+
# by the user.
|
94
|
+
units: {
|
95
|
+
# femto: Quadrillionth
|
96
|
+
# pico: Trillionth
|
97
|
+
# nano: Billionth
|
98
|
+
# micro: Millionth
|
99
|
+
# mili: Thousandth
|
100
|
+
# centi: Hundredth
|
101
|
+
# deci: Tenth
|
102
|
+
unit: "",
|
103
|
+
# ten:
|
104
|
+
# one: Ten
|
105
|
+
# other: Tens
|
106
|
+
# hundred: Hundred
|
107
|
+
thousand: "Thousand",
|
108
|
+
million: "Million",
|
109
|
+
billion: "Billion",
|
110
|
+
trillion: "Trillion",
|
111
|
+
quadrillion: "Quadrillion"
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
def self.convert(number, options)
|
118
|
+
new(number, options).execute
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(number, options)
|
122
|
+
@number = number
|
123
|
+
@opts = options.symbolize_keys
|
124
|
+
end
|
125
|
+
|
126
|
+
def execute
|
127
|
+
if !number
|
128
|
+
nil
|
129
|
+
elsif validate_float? && !valid_float?
|
130
|
+
number
|
131
|
+
else
|
132
|
+
convert
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def options
|
139
|
+
@options ||= format_options.merge(opts)
|
140
|
+
end
|
141
|
+
|
142
|
+
def format_options #:nodoc:
|
143
|
+
default_format_options.merge!(i18n_format_options)
|
144
|
+
end
|
145
|
+
|
146
|
+
def default_format_options #:nodoc:
|
147
|
+
options = DEFAULTS[:format].dup
|
148
|
+
options.merge!(DEFAULTS[namespace][:format]) if namespace
|
149
|
+
options
|
150
|
+
end
|
151
|
+
|
152
|
+
def i18n_format_options #:nodoc:
|
153
|
+
locale = opts[:locale]
|
154
|
+
options = I18n.translate(:'number.format', locale: locale, default: {}).dup
|
155
|
+
|
156
|
+
if namespace
|
157
|
+
options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
|
158
|
+
end
|
159
|
+
|
160
|
+
options
|
161
|
+
end
|
162
|
+
|
163
|
+
def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
|
164
|
+
I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
|
165
|
+
end
|
166
|
+
|
167
|
+
def translate_in_locale(key, i18n_options = {})
|
168
|
+
translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
|
169
|
+
end
|
170
|
+
|
171
|
+
def default_value(key)
|
172
|
+
key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
|
173
|
+
end
|
174
|
+
|
175
|
+
def valid_float? #:nodoc:
|
176
|
+
Float(number)
|
177
|
+
rescue ArgumentError, TypeError
|
178
|
+
false
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module NumberHelper
|
3
|
+
class NumberToCurrencyConverter < NumberConverter # :nodoc:
|
4
|
+
self.namespace = :currency
|
5
|
+
|
6
|
+
def convert
|
7
|
+
number = self.number.to_s.strip
|
8
|
+
format = options[:format]
|
9
|
+
|
10
|
+
if is_negative?(number)
|
11
|
+
format = options[:negative_format]
|
12
|
+
number = absolute_value(number)
|
13
|
+
end
|
14
|
+
|
15
|
+
rounded_number = NumberToRoundedConverter.convert(number, options)
|
16
|
+
format.gsub(/%n/, rounded_number).gsub(/%u/, options[:unit])
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def is_negative?(number)
|
22
|
+
number.to_f.phase != 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def absolute_value(number)
|
26
|
+
number.respond_to?("abs") ? number.abs : number.sub(/\A-/, '')
|
27
|
+
end
|
28
|
+
|
29
|
+
def options
|
30
|
+
@options ||= begin
|
31
|
+
defaults = default_format_options.merge(i18n_opts)
|
32
|
+
# Override negative format if format options is given
|
33
|
+
defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
|
34
|
+
defaults.merge!(opts)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def i18n_opts
|
39
|
+
# Set International negative format if not exists
|
40
|
+
i18n = i18n_format_options
|
41
|
+
i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
|
42
|
+
i18n
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module NumberHelper
|
3
|
+
class NumberToDelimitedConverter < NumberConverter #:nodoc:
|
4
|
+
self.validate_float = true
|
5
|
+
|
6
|
+
DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
|
7
|
+
|
8
|
+
def convert
|
9
|
+
parts.join(options[:separator])
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def parts
|
15
|
+
left, right = number.to_s.split('.')
|
16
|
+
left.gsub!(DELIMITED_REGEX) do |digit_to_delimit|
|
17
|
+
"#{digit_to_delimit}#{options[:delimiter]}"
|
18
|
+
end
|
19
|
+
[left, right].compact
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|