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
@@ -89,6 +89,7 @@ module ActiveSupport
89
89
  #
90
90
  # 'SSLError'.underscore.camelize # => "SslError"
91
91
  def underscore(camel_cased_word)
92
+ return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
92
93
  word = camel_cased_word.to_s.gsub('::', '/')
93
94
  word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
94
95
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
@@ -98,26 +99,46 @@ module ActiveSupport
98
99
  word
99
100
  end
100
101
 
101
- # Capitalizes the first word, turns underscores into spaces, and strips a
102
- # trailing '_id' if present.
103
- # Like +titleize+, this is meant for creating pretty output.
102
+ # Tweaks an attribute name for display to end users.
103
+ #
104
+ # Specifically, +humanize+ performs these transformations:
105
+ #
106
+ # * Applies human inflection rules to the argument.
107
+ # * Deletes leading underscores, if any.
108
+ # * Removes a "_id" suffix if present.
109
+ # * Replaces underscores with spaces, if any.
110
+ # * Downcases all words except acronyms.
111
+ # * Capitalizes the first word.
104
112
  #
105
113
  # The capitalization of the first word can be turned off by setting the
106
- # optional parameter +capitalize+ to false.
107
- # By default, this parameter is true.
114
+ # +:capitalize+ option to false (default is true).
108
115
  #
109
116
  # humanize('employee_salary') # => "Employee salary"
110
117
  # humanize('author_id') # => "Author"
111
118
  # humanize('author_id', capitalize: false) # => "author"
119
+ # humanize('_id') # => "Id"
120
+ #
121
+ # If "SSL" was defined to be an acronym:
122
+ #
123
+ # humanize('ssl_error') # => "SSL error"
124
+ #
112
125
  def humanize(lower_case_and_underscored_word, options = {})
113
126
  result = lower_case_and_underscored_word.to_s.dup
127
+
114
128
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
115
- result.gsub!(/_id$/, "")
129
+
130
+ result.sub!(/\A_+/, '')
131
+ result.sub!(/_id\z/, '')
116
132
  result.tr!('_', ' ')
117
- result.gsub!(/([a-z\d]*)/i) { |match|
133
+
134
+ result.gsub!(/([a-z\d]*)/i) do |match|
118
135
  "#{inflections.acronyms[match] || match.downcase}"
119
- }
120
- result.gsub!(/^\w/) { |match| match.upcase } if options.fetch(:capitalize, true)
136
+ end
137
+
138
+ if options.fetch(:capitalize, true)
139
+ result.sub!(/\A\w/) { |match| match.upcase }
140
+ end
141
+
121
142
  result
122
143
  end
123
144
 
@@ -171,6 +192,8 @@ module ActiveSupport
171
192
  #
172
193
  # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
173
194
  # 'Inflections'.demodulize # => "Inflections"
195
+ # '::Inflections'.demodulize # => "Inflections"
196
+ # ''.demodulize # => ""
174
197
  #
175
198
  # See also +deconstantize+.
176
199
  def demodulize(path)
@@ -227,7 +250,7 @@ module ActiveSupport
227
250
  def constantize(camel_cased_word)
228
251
  names = camel_cased_word.split('::')
229
252
 
230
- # Trigger a builtin NameError exception including the ill-formed constant in the message.
253
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
231
254
  Object.const_get(camel_cased_word) if names.empty?
232
255
 
233
256
  # Remove the first blank element in case of '::ClassName' notation.
@@ -241,8 +264,8 @@ module ActiveSupport
241
264
  next candidate if constant.const_defined?(name, false)
242
265
  next candidate unless Object.const_defined?(name)
243
266
 
244
- # Go down the ancestors to check it it's owned
245
- # directly before we reach Object or the end of ancestors.
267
+ # Go down the ancestors to check if it is owned directly. The check
268
+ # stops when we reach Object or the end of ancestors tree.
246
269
  constant = constant.ancestors.inject do |const, ancestor|
247
270
  break const if ancestor == Object
248
271
  break ancestor if ancestor.const_defined?(name, false)
@@ -325,10 +348,11 @@ module ActiveSupport
325
348
 
326
349
  private
327
350
 
328
- # Mount a regular expression that will match part by part of the constant.
351
+ # Mounts a regular expression, returned as a string to ease interpolation,
352
+ # that will match part by part the given constant.
329
353
  #
330
- # const_regexp("Foo::Bar::Baz") # => /Foo(::Bar(::Baz)?)?/
331
- # const_regexp("::") # => /::/
354
+ # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
355
+ # const_regexp("::") # => "::"
332
356
  def const_regexp(camel_cased_word) #:nodoc:
333
357
  parts = camel_cased_word.split("::")
334
358
 
@@ -58,10 +58,6 @@ module ActiveSupport
58
58
  super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
59
59
  end
60
60
  end
61
-
62
- def to_s
63
- self
64
- end
65
61
  end
66
62
 
67
63
  # Mark these as private so we don't leak encoding-specific constructs
@@ -44,20 +44,6 @@ module ActiveSupport
44
44
  def initialize(*args)
45
45
  super
46
46
  @formatter = SimpleFormatter.new
47
- after_initialize if respond_to? :after_initialize
48
- end
49
-
50
- def add(severity, message = nil, progname = nil, &block)
51
- return true if @logdev.nil? || (severity || UNKNOWN) < level
52
- super
53
- end
54
-
55
- Logger::Severity.constants.each do |severity|
56
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
57
- def #{severity.downcase}? # def debug?
58
- Logger::#{severity} >= level # DEBUG >= level
59
- end # end
60
- EOT
61
47
  end
62
48
 
63
49
  # Simple formatter which only displays the message.
@@ -1,45 +1,24 @@
1
1
  require 'active_support/concern'
2
- require 'thread_safe'
3
2
 
4
3
  module LoggerSilence
5
4
  extend ActiveSupport::Concern
6
5
 
7
6
  included do
8
7
  cattr_accessor :silencer
9
- attr_reader :local_levels
10
8
  self.silencer = true
11
9
  end
12
10
 
13
-
14
- def after_initialize
15
- @local_levels = ThreadSafe::Cache.new(:initial_capacity => 2)
16
- end
17
-
18
- def local_log_id
19
- Thread.current.__id__
20
- end
21
-
22
- def level
23
- local_levels[local_log_id] || super
24
- end
25
-
26
11
  # Silences the logger for the duration of the block.
27
12
  def silence(temporary_level = Logger::ERROR)
28
13
  if silencer
29
14
  begin
30
- old_local_level = local_levels[local_log_id]
31
- local_levels[local_log_id] = temporary_level
32
-
15
+ old_logger_level, self.level = level, temporary_level
33
16
  yield self
34
17
  ensure
35
- if old_local_level
36
- local_levels[local_log_id] = old_local_level
37
- else
38
- local_levels.delete(local_log_id)
39
- end
18
+ self.level = old_logger_level
40
19
  end
41
20
  else
42
21
  yield self
43
22
  end
44
23
  end
45
- end
24
+ end
@@ -40,6 +40,7 @@ module ActiveSupport
40
40
  # Options:
41
41
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
42
42
  # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
43
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
43
44
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
44
45
  def initialize(secret, *signature_key_or_options)
45
46
  options = signature_key_or_options.extract_options!
@@ -47,7 +48,7 @@ module ActiveSupport
47
48
  @secret = secret
48
49
  @sign_secret = sign_secret
49
50
  @cipher = options[:cipher] || 'aes-256-cbc'
50
- @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
51
+ @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
51
52
  @serializer = options[:serializer] || Marshal
52
53
  end
53
54
 
@@ -42,6 +42,7 @@ module ActiveSupport
42
42
  0x0085, # White_Space # Cc <control-0085>
43
43
  0x00A0, # White_Space # Zs NO-BREAK SPACE
44
44
  0x1680, # White_Space # Zs OGHAM SPACE MARK
45
+ 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
45
46
  (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
46
47
  0x2028, # White_Space # Zl LINE SEPARATOR
47
48
  0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
@@ -212,7 +213,8 @@ module ActiveSupport
212
213
  end
213
214
 
214
215
  # Ruby >= 2.1 has String#scrub, which is faster than the workaround used for < 2.1.
215
- if '<3'.respond_to?(:scrub)
216
+ # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
217
+ if '<3'.respond_to?(:scrub) && !defined?(Rubinius)
216
218
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
217
219
  # resulting in a valid UTF-8 string.
218
220
  #
@@ -334,7 +336,7 @@ module ActiveSupport
334
336
  begin
335
337
  @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
336
338
  rescue => e
337
- raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
339
+ raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
338
340
  end
339
341
 
340
342
  # Redefine the === method so we can write shorter rules for grapheme cluster breaks
@@ -366,6 +368,7 @@ module ActiveSupport
366
368
  private
367
369
 
368
370
  def apply_mapping(string, mapping) #:nodoc:
371
+ database.codepoints
369
372
  string.each_codepoint.map do |codepoint|
370
373
  cp = database.codepoints[codepoint]
371
374
  if cp and (ncp = cp.send(mapping)) and ncp > 0
@@ -383,7 +386,6 @@ module ActiveSupport
383
386
  def database
384
387
  @database ||= UnicodeDatabase.new
385
388
  end
386
-
387
389
  end
388
390
  end
389
391
  end
@@ -141,6 +141,11 @@ 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
151
  # Notifications ships with a queue implementation that consumes and publishes events
@@ -173,8 +178,8 @@ module ActiveSupport
173
178
  unsubscribe(subscriber)
174
179
  end
175
180
 
176
- def unsubscribe(args)
177
- notifier.unsubscribe(args)
181
+ def unsubscribe(subscriber_or_name)
182
+ notifier.unsubscribe(subscriber_or_name)
178
183
  end
179
184
 
180
185
  def instrumenter
@@ -25,9 +25,15 @@ module ActiveSupport
25
25
  subscriber
26
26
  end
27
27
 
28
- def unsubscribe(subscriber)
28
+ def unsubscribe(subscriber_or_name)
29
29
  synchronize do
30
- @subscribers.reject! { |s| s.matches?(subscriber) }
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,12 +103,11 @@ module ActiveSupport
97
103
  end
98
104
 
99
105
  def subscribed_to?(name)
100
- @pattern === name.to_s
106
+ @pattern === name
101
107
  end
102
108
 
103
- def matches?(subscriber_or_name)
104
- self === subscriber_or_name ||
105
- @pattern && @pattern === subscriber_or_name
109
+ def matches?(name)
110
+ @pattern && @pattern === name
106
111
  end
107
112
  end
108
113
 
@@ -232,12 +232,8 @@ module ActiveSupport
232
232
  # number_to_human_size(1234567, precision: 2) # => 1.2 MB
233
233
  # number_to_human_size(483989, precision: 2) # => 470 KB
234
234
  # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
235
- #
236
- # Non-significant zeros after the fractional separator are stripped out by
237
- # default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
238
- #
239
- # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
240
- # number_to_human_size(524288000, precision: 5) # => "500 MB"
235
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
236
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
241
237
  def number_to_human_size(number, options = {})
242
238
  NumberToHumanSizeConverter.convert(number, options)
243
239
  end
@@ -305,12 +301,15 @@ module ActiveSupport
305
301
  # separator: ',',
306
302
  # significant: false) # => "1,2 Million"
307
303
  #
304
+ # number_to_human(500000000, precision: 5) # => "500 Million"
305
+ # number_to_human(12345012345, significant: false) # => "12.345 Billion"
306
+ #
308
307
  # Non-significant zeros after the decimal separator are stripped
309
308
  # out by default (set <tt>:strip_insignificant_zeros</tt> to
310
309
  # +false+ to change that):
311
310
  #
312
- # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
313
- # number_to_human(500000000, precision: 5) # => "500 Million"
311
+ # number_to_human(12.00001) # => "12"
312
+ # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
314
313
  #
315
314
  # ==== Custom Unit Quantifiers
316
315
  #
@@ -23,7 +23,7 @@ module ActiveSupport
23
23
  precision = 0 if precision < 0 # don't let it be negative
24
24
  else
25
25
  rounded_number = number.round(precision)
26
- rounded_number = rounded_number.to_i if precision == 0 && rounded_number.finite?
26
+ rounded_number = rounded_number.to_i if precision == 0
27
27
  rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
28
28
  end
29
29
 
@@ -59,7 +59,7 @@ module ActiveSupport
59
59
  end
60
60
 
61
61
  def digit_count(number)
62
- number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor
62
+ (Math.log10(absolute_number(number)) + 1).floor
63
63
  end
64
64
 
65
65
  def strip_insignificant_zeros
@@ -11,25 +11,15 @@ require 'active_support/testing/time_helpers'
11
11
  require 'active_support/core_ext/kernel/reporting'
12
12
  require 'active_support/deprecation'
13
13
 
14
- begin
15
- silence_warnings { require 'mocha/setup' }
16
- rescue LoadError
17
- end
18
-
19
14
  module ActiveSupport
20
15
  class TestCase < ::Minitest::Test
21
16
  Assertion = Minitest::Assertion
22
17
 
23
- alias_method :method_name, :name
24
-
25
- $tags = {}
26
- def self.for_tag(tag)
27
- yield if $tags[tag]
18
+ class << self
19
+ alias :my_tests_are_order_dependent! :i_suck_and_my_tests_are_order_dependent!
28
20
  end
29
21
 
30
- # FIXME: we have tests that depend on run order, we should fix that and
31
- # remove this method call.
32
- self.i_suck_and_my_tests_are_order_dependent!
22
+ alias_method :method_name, :name
33
23
 
34
24
  include ActiveSupport::Testing::TaggedLogging
35
25
  include ActiveSupport::Testing::SetupAndTeardown
@@ -9,7 +9,7 @@ module ActiveSupport
9
9
  #
10
10
  # assert_not nil # => true
11
11
  # assert_not false # => true
12
- # assert_not 'foo' # => 'foo' is not nil or false
12
+ # assert_not 'foo' # => Expected "foo" to be nil or false
13
13
  #
14
14
  # An error message can be specified.
15
15
  #
@@ -1,30 +1,6 @@
1
1
  module ActiveSupport
2
2
  module Testing
3
3
  module Declarative
4
-
5
- def self.extended(klass) #:nodoc:
6
- klass.class_eval do
7
-
8
- unless method_defined?(:describe)
9
- def self.describe(text)
10
- if block_given?
11
- super
12
- else
13
- message = "`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n"
14
- ActiveSupport::Deprecation.warn message
15
-
16
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
17
- def self.name
18
- "#{text}"
19
- end
20
- RUBY_EVAL
21
- end
22
- end
23
- end
24
-
25
- end
26
- end
27
-
28
4
  unless defined?(Spec)
29
5
  # Helper to define a test method using a String. Under the hood, it replaces
30
6
  # spaces with underscores and defines the test method.
@@ -34,7 +10,7 @@ module ActiveSupport
34
10
  # end
35
11
  def test(name, &block)
36
12
  test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
37
- defined = instance_method(test_name) rescue false
13
+ defined = method_defined? test_name
38
14
  raise "#{test_name} is already defined in #{self}" if defined
39
15
  if block_given?
40
16
  define_method(test_name, &block)
@@ -70,14 +70,24 @@ module ActiveSupport
70
70
  exit!
71
71
  else
72
72
  Tempfile.open("isolation") do |tmpfile|
73
- ENV["ISOLATION_TEST"] = self.class.name
74
- ENV["ISOLATION_OUTPUT"] = tmpfile.path
73
+ env = {
74
+ ISOLATION_TEST: self.class.name,
75
+ ISOLATION_OUTPUT: tmpfile.path
76
+ }
75
77
 
76
78
  load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
77
- `#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")}`
78
-
79
- ENV.delete("ISOLATION_TEST")
80
- ENV.delete("ISOLATION_OUTPUT")
79
+ orig_args = ORIG_ARGV.join(" ")
80
+ test_opts = "-n#{self.class.name}##{self.name}"
81
+ command = "#{Gem.ruby} #{load_paths} #{$0} #{orig_args} #{test_opts}"
82
+
83
+ # IO.popen lets us pass env in a cross-platform way
84
+ child = IO.popen([env, command])
85
+
86
+ begin
87
+ Process.wait(child.pid)
88
+ rescue Errno::ECHILD # The child process may exit before we wait
89
+ nil
90
+ end
81
91
 
82
92
  return tmpfile.read.unpack("m")[0]
83
93
  end