activesupport 3.0.20 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +24 -28
- data/README.rdoc +2 -2
- data/lib/active_support/backtrace_cleaner.rb +17 -12
- data/lib/active_support/buffered_logger.rb +7 -9
- data/lib/active_support/cache/file_store.rb +4 -3
- data/lib/active_support/cache/mem_cache_store.rb +10 -2
- data/lib/active_support/cache/strategy/local_cache.rb +28 -23
- data/lib/active_support/cache.rb +11 -14
- data/lib/active_support/callbacks.rb +195 -175
- data/lib/active_support/concern.rb +105 -35
- data/lib/active_support/configurable.rb +50 -8
- data/lib/active_support/core_ext/array/access.rb +2 -2
- data/lib/active_support/core_ext/array/conversions.rb +2 -2
- data/lib/active_support/core_ext/array/random_access.rb +10 -7
- data/lib/active_support/core_ext/array/uniq_by.rb +2 -3
- data/lib/active_support/core_ext/array/wrap.rb +1 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -2
- data/lib/active_support/core_ext/class/attribute.rb +15 -1
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +10 -86
- data/lib/active_support/core_ext/class/subclasses.rb +20 -34
- data/lib/active_support/core_ext/date/calculations.rb +17 -5
- data/lib/active_support/core_ext/date/conversions.rb +9 -2
- data/lib/active_support/core_ext/date_time/calculations.rb +26 -9
- data/lib/active_support/core_ext/date_time/conversions.rb +9 -9
- data/lib/active_support/core_ext/date_time/zones.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +3 -6
- data/lib/active_support/core_ext/float/rounding.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +22 -34
- data/lib/active_support/core_ext/hash/deep_dup.rb +11 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +12 -0
- data/lib/active_support/core_ext/hash/keys.rb +6 -4
- data/lib/active_support/core_ext/hash/reverse_merge.rb +9 -14
- data/lib/active_support/core_ext/hash/slice.rb +3 -1
- data/lib/active_support/core_ext/hash.rb +1 -0
- data/lib/active_support/core_ext/integer/inflections.rb +7 -4
- data/lib/active_support/core_ext/kernel/reporting.rb +31 -0
- data/lib/active_support/core_ext/kernel/requires.rb +2 -0
- data/lib/active_support/core_ext/logger.rb +11 -38
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +12 -12
- data/lib/active_support/core_ext/module/attr_internal.rb +13 -6
- data/lib/active_support/core_ext/module/delegation.rb +1 -5
- data/lib/active_support/core_ext/module/remove_method.rb +7 -2
- data/lib/active_support/core_ext/numeric/time.rb +2 -0
- data/lib/active_support/core_ext/object/blank.rb +45 -12
- data/lib/active_support/core_ext/object/duplicable.rb +50 -9
- data/lib/active_support/core_ext/object/inclusion.rb +15 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -35
- data/lib/active_support/core_ext/object/to_param.rb +13 -8
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +29 -10
- data/lib/active_support/core_ext/object/with_options.rb +25 -8
- data/lib/active_support/core_ext/object.rb +1 -1
- data/lib/active_support/core_ext/range/cover.rb +3 -0
- data/lib/active_support/core_ext/range.rb +1 -0
- data/lib/active_support/core_ext/string/behavior.rb +1 -2
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/exclude.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/inquiry.rb +13 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +45 -62
- data/lib/active_support/core_ext/string.rb +1 -0
- data/lib/active_support/core_ext/time/calculations.rb +19 -16
- data/lib/active_support/core_ext/time/conversions.rb +2 -24
- data/lib/active_support/core_ext/time/zones.rb +32 -21
- data/lib/active_support/core_ext/uri.rb +8 -0
- data/lib/active_support/dependencies.rb +74 -28
- data/lib/active_support/deprecation/behaviors.rb +7 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
- data/lib/active_support/deprecation/reporting.rb +4 -0
- data/lib/active_support/deprecation.rb +2 -2
- data/lib/active_support/descendants_tracker.rb +9 -7
- data/lib/active_support/duration.rb +5 -0
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/file_watcher.rb +36 -0
- data/lib/active_support/hash_with_indifferent_access.rb +20 -8
- data/lib/active_support/i18n.rb +0 -1
- data/lib/active_support/i18n_railtie.rb +2 -2
- data/lib/active_support/inflections.rb +1 -0
- data/lib/active_support/inflector/inflections.rb +1 -1
- data/lib/active_support/inflector/methods.rb +5 -3
- data/lib/active_support/json/decoding.rb +37 -23
- data/lib/active_support/json/encoding.rb +12 -4
- data/lib/active_support/lazy_load_hooks.rb +7 -7
- data/lib/active_support/log_subscriber/test_helper.rb +5 -3
- data/lib/active_support/log_subscriber.rb +6 -6
- data/lib/active_support/message_encryptor.rb +1 -1
- data/lib/active_support/message_verifier.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +5 -5
- data/lib/active_support/multibyte/unicode.rb +2 -1
- data/lib/active_support/notifications/fanout.rb +3 -3
- data/lib/active_support/notifications/instrumenter.rb +1 -1
- data/lib/active_support/notifications.rb +9 -10
- data/lib/active_support/ordered_hash.rb +23 -2
- data/lib/active_support/ordered_options.rb +37 -3
- data/lib/active_support/railtie.rb +1 -1
- data/lib/active_support/rescuable.rb +1 -0
- data/lib/active_support/secure_random.rb +3 -196
- data/lib/active_support/test_case.rb +2 -10
- data/lib/active_support/testing/assertions.rb +20 -7
- data/lib/active_support/testing/mochaing.rb +7 -0
- data/lib/active_support/testing/pending.rb +27 -23
- data/lib/active_support/testing/performance/jruby.rb +115 -0
- data/lib/active_support/testing/performance/rubinius.rb +113 -0
- data/lib/active_support/testing/performance/ruby/mri.rb +57 -0
- data/lib/active_support/testing/performance/ruby/yarv.rb +57 -0
- data/lib/active_support/testing/performance/ruby.rb +152 -0
- data/lib/active_support/testing/performance.rb +214 -322
- data/lib/active_support/testing/setup_and_teardown.rb +8 -11
- data/lib/active_support/time_with_zone.rb +6 -3
- data/lib/active_support/values/time_zone.rb +26 -8
- data/lib/active_support/version.rb +2 -2
- data/lib/active_support/whiny_nil.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +1 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +2 -1
- data/lib/active_support/xml_mini/nokogiri.rb +1 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
- data/lib/active_support/xml_mini/rexml.rb +1 -0
- data/lib/active_support/xml_mini.rb +8 -3
- data/lib/active_support.rb +4 -2
- metadata +70 -32
- checksums.yaml +0 -7
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -19
- data/lib/active_support/core_ext/cgi.rb +0 -1
- data/lib/active_support/core_ext/object/returning.rb +0 -43
- data/lib/active_support/json/backends/jsongem.rb +0 -47
- data/lib/active_support/json/backends/okjson.rb +0 -644
- data/lib/active_support/json/backends/yajl.rb +0 -44
- data/lib/active_support/json/backends/yaml.rb +0 -19
- data/lib/active_support/testing/default.rb +0 -9
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
require 'active_support/ordered_hash'
|
|
2
2
|
|
|
3
|
+
# Usually key value pairs are handled something like this:
|
|
4
|
+
#
|
|
5
|
+
# h = {}
|
|
6
|
+
# h[:boy] = 'John'
|
|
7
|
+
# h[:girl] = 'Mary'
|
|
8
|
+
# h[:boy] # => 'John'
|
|
9
|
+
# h[:girl] # => 'Mary'
|
|
10
|
+
#
|
|
11
|
+
# Using <tt>OrderedOptions</tt>, the above code could be reduced to:
|
|
12
|
+
#
|
|
13
|
+
# h = ActiveSupport::OrderedOptions.new
|
|
14
|
+
# h.boy = 'John'
|
|
15
|
+
# h.girl = 'Mary'
|
|
16
|
+
# h.boy # => 'John'
|
|
17
|
+
# h.girl # => 'Mary'
|
|
18
|
+
#
|
|
3
19
|
module ActiveSupport #:nodoc:
|
|
4
20
|
class OrderedOptions < OrderedHash
|
|
21
|
+
alias_method :_get, :[] # preserve the original #[] method
|
|
22
|
+
protected :_get # make it protected
|
|
23
|
+
|
|
5
24
|
def []=(key, value)
|
|
6
25
|
super(key.to_sym, value)
|
|
7
26
|
end
|
|
@@ -12,16 +31,31 @@ module ActiveSupport #:nodoc:
|
|
|
12
31
|
|
|
13
32
|
def method_missing(name, *args)
|
|
14
33
|
if name.to_s =~ /(.*)=$/
|
|
15
|
-
self[$1
|
|
34
|
+
self[$1] = args.first
|
|
16
35
|
else
|
|
17
36
|
self[name]
|
|
18
37
|
end
|
|
19
38
|
end
|
|
39
|
+
|
|
40
|
+
def respond_to?(name)
|
|
41
|
+
true
|
|
42
|
+
end
|
|
20
43
|
end
|
|
21
44
|
|
|
22
45
|
class InheritableOptions < OrderedOptions
|
|
23
|
-
def initialize(parent)
|
|
24
|
-
|
|
46
|
+
def initialize(parent = nil)
|
|
47
|
+
if parent.kind_of?(OrderedOptions)
|
|
48
|
+
# use the faster _get when dealing with OrderedOptions
|
|
49
|
+
super() { |h,k| parent._get(k) }
|
|
50
|
+
elsif parent
|
|
51
|
+
super() { |h,k| parent[k] }
|
|
52
|
+
else
|
|
53
|
+
super()
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inheritable_copy
|
|
58
|
+
self.class.new(self)
|
|
25
59
|
end
|
|
26
60
|
end
|
|
27
61
|
end
|
|
@@ -46,7 +46,7 @@ module ActiveSupport
|
|
|
46
46
|
# If assigned value cannot be matched to a TimeZone, an exception will be raised.
|
|
47
47
|
initializer "active_support.initialize_time_zone" do |app|
|
|
48
48
|
require 'active_support/core_ext/time/zones'
|
|
49
|
-
zone_default = Time.
|
|
49
|
+
zone_default = Time.find_zone!(app.config.time_zone)
|
|
50
50
|
|
|
51
51
|
unless zone_default
|
|
52
52
|
raise \
|
|
@@ -1,199 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require 'securerandom'
|
|
3
|
-
rescue LoadError
|
|
4
|
-
end
|
|
1
|
+
require 'active_support/deprecation'
|
|
5
2
|
|
|
6
3
|
module ActiveSupport
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
SecureRandom = ::SecureRandom # :nodoc:
|
|
10
|
-
else
|
|
11
|
-
# = Secure random number generator interface.
|
|
12
|
-
#
|
|
13
|
-
# This library is an interface for secure random number generator which is
|
|
14
|
-
# suitable for generating session key in HTTP cookies, etc.
|
|
15
|
-
#
|
|
16
|
-
# It supports following secure random number generators.
|
|
17
|
-
#
|
|
18
|
-
# * openssl
|
|
19
|
-
# * /dev/urandom
|
|
20
|
-
# * Win32
|
|
21
|
-
#
|
|
22
|
-
# *Note*: This module is based on the SecureRandom library from Ruby 1.9,
|
|
23
|
-
# revision 18786, August 23 2008. It's 100% interface-compatible with Ruby 1.9's
|
|
24
|
-
# SecureRandom library.
|
|
25
|
-
#
|
|
26
|
-
# == Example
|
|
27
|
-
#
|
|
28
|
-
# # random hexadecimal string.
|
|
29
|
-
# p SecureRandom.hex(10) # => "52750b30ffbc7de3b362"
|
|
30
|
-
# p SecureRandom.hex(10) # => "92b15d6c8dc4beb5f559"
|
|
31
|
-
# p SecureRandom.hex(11) # => "6aca1b5c58e4863e6b81b8"
|
|
32
|
-
# p SecureRandom.hex(12) # => "94b2fff3e7fd9b9c391a2306"
|
|
33
|
-
# p SecureRandom.hex(13) # => "39b290146bea6ce975c37cfc23"
|
|
34
|
-
# ...
|
|
35
|
-
#
|
|
36
|
-
# # random base64 string.
|
|
37
|
-
# p SecureRandom.base64(10) # => "EcmTPZwWRAozdA=="
|
|
38
|
-
# p SecureRandom.base64(10) # => "9b0nsevdwNuM/w=="
|
|
39
|
-
# p SecureRandom.base64(10) # => "KO1nIU+p9DKxGg=="
|
|
40
|
-
# p SecureRandom.base64(11) # => "l7XEiFja+8EKEtY="
|
|
41
|
-
# p SecureRandom.base64(12) # => "7kJSM/MzBJI+75j8"
|
|
42
|
-
# p SecureRandom.base64(13) # => "vKLJ0tXBHqQOuIcSIg=="
|
|
43
|
-
# ...
|
|
44
|
-
#
|
|
45
|
-
# # random binary string.
|
|
46
|
-
# p SecureRandom.random_bytes(10) # => "\016\t{\370g\310pbr\301"
|
|
47
|
-
# p SecureRandom.random_bytes(10) # => "\323U\030TO\234\357\020\a\337"
|
|
48
|
-
# ...
|
|
49
|
-
module SecureRandom
|
|
50
|
-
# SecureRandom.random_bytes generates a random binary string.
|
|
51
|
-
#
|
|
52
|
-
# The argument n specifies the length of the result string.
|
|
53
|
-
#
|
|
54
|
-
# If n is not specified, 16 is assumed.
|
|
55
|
-
# It may be larger in future.
|
|
56
|
-
#
|
|
57
|
-
# If secure random number generator is not available,
|
|
58
|
-
# NotImplementedError is raised.
|
|
59
|
-
def self.random_bytes(n=nil)
|
|
60
|
-
n ||= 16
|
|
61
|
-
|
|
62
|
-
unless defined? OpenSSL
|
|
63
|
-
begin
|
|
64
|
-
require 'openssl'
|
|
65
|
-
rescue LoadError
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
if defined? OpenSSL::Random
|
|
70
|
-
return OpenSSL::Random.random_bytes(n)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
if !defined?(@has_urandom) || @has_urandom
|
|
74
|
-
flags = File::RDONLY
|
|
75
|
-
flags |= File::NONBLOCK if defined? File::NONBLOCK
|
|
76
|
-
flags |= File::NOCTTY if defined? File::NOCTTY
|
|
77
|
-
flags |= File::NOFOLLOW if defined? File::NOFOLLOW
|
|
78
|
-
begin
|
|
79
|
-
File.open("/dev/urandom", flags) {|f|
|
|
80
|
-
unless f.stat.chardev?
|
|
81
|
-
raise Errno::ENOENT
|
|
82
|
-
end
|
|
83
|
-
@has_urandom = true
|
|
84
|
-
ret = f.readpartial(n)
|
|
85
|
-
if ret.length != n
|
|
86
|
-
raise NotImplementedError, "Unexpected partial read from random device"
|
|
87
|
-
end
|
|
88
|
-
return ret
|
|
89
|
-
}
|
|
90
|
-
rescue Errno::ENOENT
|
|
91
|
-
@has_urandom = false
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
if !defined?(@has_win32)
|
|
96
|
-
begin
|
|
97
|
-
require 'Win32API'
|
|
98
|
-
|
|
99
|
-
crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
|
|
100
|
-
@crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')
|
|
101
|
-
|
|
102
|
-
hProvStr = " " * 4
|
|
103
|
-
prov_rsa_full = 1
|
|
104
|
-
crypt_verifycontext = 0xF0000000
|
|
105
|
-
|
|
106
|
-
if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
|
|
107
|
-
raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
|
|
108
|
-
end
|
|
109
|
-
@hProv, = hProvStr.unpack('L')
|
|
110
|
-
|
|
111
|
-
@has_win32 = true
|
|
112
|
-
rescue LoadError
|
|
113
|
-
@has_win32 = false
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
if @has_win32
|
|
117
|
-
bytes = " " * n
|
|
118
|
-
if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
|
|
119
|
-
raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
|
|
120
|
-
end
|
|
121
|
-
return bytes
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
raise NotImplementedError, "No random device"
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# SecureRandom.hex generates a random hex string.
|
|
128
|
-
#
|
|
129
|
-
# The argument n specifies the length of the random length.
|
|
130
|
-
# The length of the result string is twice of n.
|
|
131
|
-
#
|
|
132
|
-
# If n is not specified, 16 is assumed.
|
|
133
|
-
# It may be larger in future.
|
|
134
|
-
#
|
|
135
|
-
# If secure random number generator is not available,
|
|
136
|
-
# NotImplementedError is raised.
|
|
137
|
-
def self.hex(n=nil)
|
|
138
|
-
random_bytes(n).unpack("H*")[0]
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# SecureRandom.base64 generates a random base64 string.
|
|
142
|
-
#
|
|
143
|
-
# The argument n specifies the length of the random length.
|
|
144
|
-
# The length of the result string is about 4/3 of n.
|
|
145
|
-
#
|
|
146
|
-
# If n is not specified, 16 is assumed.
|
|
147
|
-
# It may be larger in future.
|
|
148
|
-
#
|
|
149
|
-
# If secure random number generator is not available,
|
|
150
|
-
# NotImplementedError is raised.
|
|
151
|
-
def self.base64(n=nil)
|
|
152
|
-
[random_bytes(n)].pack("m*").delete("\n")
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# SecureRandom.random_number generates a random number.
|
|
156
|
-
#
|
|
157
|
-
# If an positive integer is given as n,
|
|
158
|
-
# SecureRandom.random_number returns an integer:
|
|
159
|
-
# 0 <= SecureRandom.random_number(n) < n.
|
|
160
|
-
#
|
|
161
|
-
# If 0 is given or an argument is not given,
|
|
162
|
-
# SecureRandom.random_number returns an float:
|
|
163
|
-
# 0.0 <= SecureRandom.random_number() < 1.0.
|
|
164
|
-
def self.random_number(n=0)
|
|
165
|
-
if 0 < n
|
|
166
|
-
hex = n.to_s(16)
|
|
167
|
-
hex = '0' + hex if (hex.length & 1) == 1
|
|
168
|
-
bin = [hex].pack("H*")
|
|
169
|
-
mask = bin[0]
|
|
170
|
-
mask |= mask >> 1
|
|
171
|
-
mask |= mask >> 2
|
|
172
|
-
mask |= mask >> 4
|
|
173
|
-
begin
|
|
174
|
-
rnd = SecureRandom.random_bytes(bin.length)
|
|
175
|
-
rnd[0] = rnd[0] & mask
|
|
176
|
-
end until rnd < bin
|
|
177
|
-
rnd.unpack("H*")[0].hex
|
|
178
|
-
else
|
|
179
|
-
# assumption: Float::MANT_DIG <= 64
|
|
180
|
-
i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
|
|
181
|
-
Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Following code is based on David Garamond's GUID library for Ruby.
|
|
186
|
-
def self.lastWin32ErrorMessage # :nodoc:
|
|
187
|
-
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
|
|
188
|
-
format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
|
|
189
|
-
format_message_ignore_inserts = 0x00000200
|
|
190
|
-
format_message_from_system = 0x00001000
|
|
191
|
-
|
|
192
|
-
code = get_last_error.call
|
|
193
|
-
msg = "\0" * 1024
|
|
194
|
-
len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
195
|
-
msg[0, len].tr("\r", '').chomp
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
end
|
|
4
|
+
# Use Ruby's SecureRandom library.
|
|
5
|
+
SecureRandom = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveSupport::SecureRandom', ::SecureRandom) # :nodoc:
|
|
199
6
|
end
|
|
@@ -5,16 +5,9 @@ require 'active_support/testing/deprecation'
|
|
|
5
5
|
require 'active_support/testing/declarative'
|
|
6
6
|
require 'active_support/testing/pending'
|
|
7
7
|
require 'active_support/testing/isolation'
|
|
8
|
+
require 'active_support/testing/mochaing'
|
|
8
9
|
require 'active_support/core_ext/kernel/reporting'
|
|
9
10
|
|
|
10
|
-
begin
|
|
11
|
-
silence_warnings { require 'mocha/setup' }
|
|
12
|
-
rescue LoadError
|
|
13
|
-
# Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
|
|
14
|
-
Object.const_set :Mocha, Module.new
|
|
15
|
-
Mocha.const_set :ExpectationError, Class.new(StandardError)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
11
|
module ActiveSupport
|
|
19
12
|
class TestCase < ::Test::Unit::TestCase
|
|
20
13
|
if defined? MiniTest
|
|
@@ -24,8 +17,7 @@ module ActiveSupport
|
|
|
24
17
|
else
|
|
25
18
|
Assertion = Test::Unit::AssertionFailedError
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
include ActiveSupport::Testing::Default
|
|
20
|
+
undef :default_test
|
|
29
21
|
end
|
|
30
22
|
|
|
31
23
|
$tags = {}
|
|
@@ -29,22 +29,35 @@ module ActiveSupport
|
|
|
29
29
|
# post :create, :article => {...}
|
|
30
30
|
# end
|
|
31
31
|
#
|
|
32
|
+
# A lambda or a list of lambdas can be passed in and evaluated:
|
|
33
|
+
#
|
|
34
|
+
# assert_difference lambda { Article.count }, 2 do
|
|
35
|
+
# post :create, :article => {...}
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
|
|
39
|
+
# post :create, :article => {...}
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
32
42
|
# A error message can be specified.
|
|
33
43
|
#
|
|
34
44
|
# assert_difference 'Article.count', -1, "An Article should be destroyed" do
|
|
35
45
|
# post :delete, :id => ...
|
|
36
46
|
# end
|
|
37
47
|
def assert_difference(expression, difference = 1, message = nil, &block)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
expressions = Array.wrap expression
|
|
49
|
+
|
|
50
|
+
exps = expressions.map { |e|
|
|
51
|
+
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
|
|
52
|
+
}
|
|
53
|
+
before = exps.map { |e| e.call }
|
|
41
54
|
|
|
42
55
|
yield
|
|
43
56
|
|
|
44
|
-
exps.each_with_index do |e, i|
|
|
45
|
-
error
|
|
46
|
-
error
|
|
47
|
-
assert_equal(before[i] + difference,
|
|
57
|
+
expressions.zip(exps).each_with_index do |(code, e), i|
|
|
58
|
+
error = "#{code.inspect} didn't change by #{difference}"
|
|
59
|
+
error = "#{message}.\n#{error}" if message
|
|
60
|
+
assert_equal(before[i] + difference, e.call, error)
|
|
48
61
|
end
|
|
49
62
|
end
|
|
50
63
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Some code from jeremymcanally's "pending"
|
|
2
|
-
#
|
|
2
|
+
# https://github.com/jeremymcanally/pending/tree/master
|
|
3
3
|
|
|
4
4
|
module ActiveSupport
|
|
5
5
|
module Testing
|
|
@@ -11,32 +11,36 @@ module ActiveSupport
|
|
|
11
11
|
@@at_exit = false
|
|
12
12
|
|
|
13
13
|
def pending(description = "", &block)
|
|
14
|
-
if
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
if defined?(::MiniTest)
|
|
15
|
+
skip(description.blank? ? nil : description)
|
|
16
|
+
else
|
|
17
|
+
if description.is_a?(Symbol)
|
|
18
|
+
is_pending = $tags[description]
|
|
19
|
+
return block.call unless is_pending
|
|
20
|
+
end
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
if block_given?
|
|
23
|
+
failed = false
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
begin
|
|
26
|
+
block.call
|
|
27
|
+
rescue Exception
|
|
28
|
+
failed = true
|
|
29
|
+
end
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
flunk("<#{description}> did not fail.") unless failed
|
|
32
|
+
end
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
caller[0] =~ (/(.*):(.*):in `(.*)'/)
|
|
35
|
+
@@pending_cases << "#{$3} at #{$1}, line #{$2}"
|
|
36
|
+
print "P"
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
@@at_exit ||= begin
|
|
39
|
+
at_exit do
|
|
40
|
+
puts "\nPending Cases:"
|
|
41
|
+
@@pending_cases.each do |test_case|
|
|
42
|
+
puts test_case
|
|
43
|
+
end
|
|
40
44
|
end
|
|
41
45
|
end
|
|
42
46
|
end
|
|
@@ -45,4 +49,4 @@ module ActiveSupport
|
|
|
45
49
|
|
|
46
50
|
end
|
|
47
51
|
end
|
|
48
|
-
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'jruby/profiler'
|
|
2
|
+
require 'java'
|
|
3
|
+
java_import java.lang.management.ManagementFactory
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
module Testing
|
|
7
|
+
module Performance
|
|
8
|
+
DEFAULTS.merge!(
|
|
9
|
+
if ARGV.include?('--benchmark')
|
|
10
|
+
{:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]}
|
|
11
|
+
else
|
|
12
|
+
{ :metrics => [:wall_time],
|
|
13
|
+
:formats => [:flat, :graph] }
|
|
14
|
+
end).freeze
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
def run_gc
|
|
18
|
+
ManagementFactory.memory_mx_bean.gc
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Profiler < Performer
|
|
22
|
+
def initialize(*args)
|
|
23
|
+
super
|
|
24
|
+
@supported = @metric.is_a?(Metrics::WallTime)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run
|
|
28
|
+
return unless @supported
|
|
29
|
+
|
|
30
|
+
@total = time_with_block do
|
|
31
|
+
@data = JRuby::Profiler.profile do
|
|
32
|
+
full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def record
|
|
38
|
+
return unless @supported
|
|
39
|
+
|
|
40
|
+
klasses = full_profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact
|
|
41
|
+
|
|
42
|
+
klasses.each do |klass|
|
|
43
|
+
fname = output_filename(klass)
|
|
44
|
+
FileUtils.mkdir_p(File.dirname(fname))
|
|
45
|
+
file = File.open(fname, 'wb') do |file|
|
|
46
|
+
klass.new(@data).printProfile(file)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
def output_filename(printer_class)
|
|
53
|
+
suffix =
|
|
54
|
+
case printer_class.name.demodulize
|
|
55
|
+
when 'FlatProfilePrinter'; 'flat.txt'
|
|
56
|
+
when 'GraphProfilePrinter'; 'graph.txt'
|
|
57
|
+
else printer_class.name.sub(/ProfilePrinter$/, '').underscore
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
"#{super()}_#{suffix}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module Metrics
|
|
65
|
+
class Base
|
|
66
|
+
def profile
|
|
67
|
+
yield
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
protected
|
|
71
|
+
def with_gc_stats
|
|
72
|
+
ManagementFactory.memory_mx_bean.gc
|
|
73
|
+
yield
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class WallTime < Time
|
|
78
|
+
def measure
|
|
79
|
+
super
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class CpuTime < Time
|
|
84
|
+
def measure
|
|
85
|
+
ManagementFactory.thread_mx_bean.get_current_thread_cpu_time / 1000 / 1000 / 1000.0 # seconds
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class UserTime < Time
|
|
90
|
+
def measure
|
|
91
|
+
ManagementFactory.thread_mx_bean.get_current_thread_user_time / 1000 / 1000 / 1000.0 # seconds
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class Memory < DigitalInformationUnit
|
|
96
|
+
def measure
|
|
97
|
+
ManagementFactory.memory_mx_bean.non_heap_memory_usage.used + ManagementFactory.memory_mx_bean.heap_memory_usage.used
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class GcRuns < Amount
|
|
102
|
+
def measure
|
|
103
|
+
ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_runs, current_gc| total_runs += current_gc.collection_count }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class GcTime < Time
|
|
108
|
+
def measure
|
|
109
|
+
ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_time, current_gc| total_time += current_gc.collection_time } / 1000.0 # seconds
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require 'rubinius/agent'
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
module Performance
|
|
6
|
+
DEFAULTS.merge!(
|
|
7
|
+
if ARGV.include?('--benchmark')
|
|
8
|
+
{:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}
|
|
9
|
+
else
|
|
10
|
+
{ :metrics => [:wall_time],
|
|
11
|
+
:formats => [:flat, :graph] }
|
|
12
|
+
end).freeze
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
def run_gc
|
|
16
|
+
GC.run(true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Performer; end
|
|
20
|
+
|
|
21
|
+
class Profiler < Performer
|
|
22
|
+
def initialize(*args)
|
|
23
|
+
super
|
|
24
|
+
@supported = @metric.is_a?(Metrics::WallTime)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run
|
|
28
|
+
return unless @supported
|
|
29
|
+
|
|
30
|
+
@profiler = Rubinius::Profiler::Instrumenter.new
|
|
31
|
+
|
|
32
|
+
@total = time_with_block do
|
|
33
|
+
@profiler.profile(false) do
|
|
34
|
+
full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def record
|
|
40
|
+
return unless @supported
|
|
41
|
+
|
|
42
|
+
if(full_profile_options[:formats].include?(:flat))
|
|
43
|
+
create_path_and_open_file(:flat) do |file|
|
|
44
|
+
@profiler.show(file)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if(full_profile_options[:formats].include?(:graph))
|
|
49
|
+
create_path_and_open_file(:graph) do |file|
|
|
50
|
+
@profiler.show(file)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
protected
|
|
56
|
+
def create_path_and_open_file(printer_name)
|
|
57
|
+
fname = "#{output_filename}_#{printer_name}.txt"
|
|
58
|
+
FileUtils.mkdir_p(File.dirname(fname))
|
|
59
|
+
File.open(fname, 'wb') do |file|
|
|
60
|
+
yield(file)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
module Metrics
|
|
66
|
+
class Base
|
|
67
|
+
attr_reader :loopback
|
|
68
|
+
|
|
69
|
+
def profile
|
|
70
|
+
yield
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
protected
|
|
74
|
+
def with_gc_stats
|
|
75
|
+
@loopback = Rubinius::Agent.loopback
|
|
76
|
+
GC.run(true)
|
|
77
|
+
yield
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class WallTime < Time
|
|
82
|
+
def measure
|
|
83
|
+
super
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class Memory < DigitalInformationUnit
|
|
88
|
+
def measure
|
|
89
|
+
loopback.get("system.memory.counter.bytes").last
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class Objects < Amount
|
|
94
|
+
def measure
|
|
95
|
+
loopback.get("system.memory.counter.objects").last
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class GcRuns < Amount
|
|
100
|
+
def measure
|
|
101
|
+
loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class GcTime < Time
|
|
106
|
+
def measure
|
|
107
|
+
(loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|