activesupport 3.0.0.beta → 3.0.0.beta2

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 (73) hide show
  1. data/CHANGELOG +15 -1
  2. data/lib/active_support.rb +3 -0
  3. data/lib/active_support/all.rb +0 -1
  4. data/lib/active_support/cache/mem_cache_store.rb +1 -1
  5. data/lib/active_support/callbacks.rb +2 -2
  6. data/lib/active_support/core_ext/array/conversions.rb +0 -1
  7. data/lib/active_support/core_ext/array/extract_options.rb +16 -1
  8. data/lib/active_support/core_ext/class.rb +1 -0
  9. data/lib/active_support/core_ext/class/attribute.rb +30 -5
  10. data/lib/active_support/core_ext/class/attribute_accessors.rb +33 -27
  11. data/lib/active_support/core_ext/class/delegating_attributes.rb +10 -7
  12. data/lib/active_support/core_ext/class/subclasses.rb +55 -0
  13. data/lib/active_support/core_ext/date_time/conversions.rb +1 -0
  14. data/lib/active_support/core_ext/file/atomic.rb +3 -2
  15. data/lib/active_support/core_ext/file/path.rb +5 -0
  16. data/lib/active_support/core_ext/hash/conversions.rb +9 -0
  17. data/lib/active_support/core_ext/kernel.rb +0 -1
  18. data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
  19. data/lib/active_support/core_ext/kernel/reporting.rb +1 -1
  20. data/lib/active_support/core_ext/module.rb +5 -3
  21. data/lib/active_support/core_ext/module/anonymous.rb +24 -0
  22. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -21
  23. data/lib/active_support/core_ext/module/delegation.rb +20 -9
  24. data/lib/active_support/core_ext/module/introspection.rb +8 -8
  25. data/lib/active_support/core_ext/module/method_names.rb +14 -0
  26. data/lib/active_support/core_ext/module/reachable.rb +10 -0
  27. data/lib/active_support/core_ext/module/remove_method.rb +6 -0
  28. data/lib/active_support/core_ext/object.rb +7 -1
  29. data/lib/active_support/core_ext/object/extending.rb +11 -0
  30. data/lib/active_support/core_ext/object/singleton_class.rb +13 -0
  31. data/lib/active_support/core_ext/proc.rb +3 -3
  32. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  33. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  34. data/lib/active_support/core_ext/string/interpolation.rb +1 -91
  35. data/lib/active_support/core_ext/string/output_safety.rb +12 -8
  36. data/lib/active_support/core_ext/string/xchar.rb +1 -1
  37. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  38. data/lib/active_support/core_ext/time/marshal.rb +56 -0
  39. data/lib/active_support/dependencies.rb +146 -191
  40. data/lib/active_support/dependencies/autoload.rb +1 -0
  41. data/lib/active_support/deprecation/method_wrappers.rb +9 -9
  42. data/lib/active_support/deprecation/reporting.rb +2 -1
  43. data/lib/active_support/hash_with_indifferent_access.rb +4 -0
  44. data/lib/active_support/i18n.rb +2 -1
  45. data/lib/active_support/inflector/methods.rb +1 -1
  46. data/lib/active_support/inflector/transliterate.rb +3 -3
  47. data/lib/active_support/json/backends/yajl.rb +40 -0
  48. data/lib/active_support/json/decoding.rb +16 -1
  49. data/lib/active_support/lazy_load_hooks.rb +17 -0
  50. data/lib/active_support/memoizable.rb +1 -1
  51. data/lib/active_support/multibyte.rb +2 -2
  52. data/lib/active_support/multibyte/utils.rb +1 -1
  53. data/lib/active_support/notifications.rb +7 -3
  54. data/lib/active_support/notifications/fanout.rb +19 -5
  55. data/lib/active_support/ordered_options.rb +6 -0
  56. data/lib/active_support/railtie.rb +9 -9
  57. data/lib/active_support/ruby/shim.rb +2 -0
  58. data/lib/active_support/test_case.rb +2 -7
  59. data/lib/active_support/testing/assertions.rb +15 -0
  60. data/lib/active_support/testing/isolation.rb +2 -2
  61. data/lib/active_support/time.rb +1 -1
  62. data/lib/active_support/values/time_zone.rb +7 -4
  63. data/lib/active_support/version.rb +3 -2
  64. data/lib/active_support/whiny_nil.rb +4 -5
  65. data/lib/active_support/xml_mini/libxmlsax.rb +1 -0
  66. data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
  67. data/lib/active_support/xml_mini/rexml.rb +7 -1
  68. metadata +65 -31
  69. data/lib/active_support/core_ext/kernel/daemonizing.rb +0 -7
  70. data/lib/active_support/core_ext/module/inclusion.rb +0 -30
  71. data/lib/active_support/core_ext/module/loading.rb +0 -25
  72. data/lib/active_support/core_ext/object/metaclass.rb +0 -13
  73. data/lib/active_support/core_ext/time/marshal_with_utc_flag.rb +0 -22
@@ -0,0 +1,56 @@
1
+ # Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
2
+ # unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load
3
+ # methods so that utc instances can be flagged on dump, and coerced back to utc on load.
4
+ if !Marshal.load(Marshal.dump(Time.now.utc)).utc?
5
+ class Time
6
+ class << self
7
+ alias_method :_load_without_utc_flag, :_load
8
+ def _load(marshaled_time)
9
+ time = _load_without_utc_flag(marshaled_time)
10
+ time.instance_eval do
11
+ if defined?(@marshal_with_utc_coercion)
12
+ val = remove_instance_variable("@marshal_with_utc_coercion")
13
+ end
14
+ val ? utc : self
15
+ end
16
+ end
17
+ end
18
+
19
+ alias_method :_dump_without_utc_flag, :_dump
20
+ def _dump(*args)
21
+ obj = dup
22
+ obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
23
+ obj._dump_without_utc_flag(*args)
24
+ end
25
+ end
26
+ end
27
+
28
+ # Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
29
+ # preserves utc_offset. Preserve zone also, even though it may not
30
+ # work in some edge cases.
31
+ if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
32
+ class Time
33
+ class << self
34
+ alias_method :_load_without_zone, :_load
35
+ def _load(marshaled_time)
36
+ time = _load_without_zone(marshaled_time)
37
+ time.instance_eval do
38
+ if zone = defined?(@_zone) && remove_instance_variable('@_zone')
39
+ ary = to_a
40
+ ary[-1] = zone
41
+ utc? ? Time.utc(*ary) : Time.local(*ary)
42
+ else
43
+ self
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ alias_method :_dump_without_zone, :_dump
50
+ def _dump(*args)
51
+ obj = dup
52
+ obj.instance_variable_set('@_zone', zone)
53
+ obj._dump_without_zone(*args)
54
+ end
55
+ end
56
+ end
@@ -1,8 +1,10 @@
1
1
  require 'set'
2
2
  require 'thread'
3
+ require 'pathname'
3
4
  require 'active_support/core_ext/module/aliasing'
4
5
  require 'active_support/core_ext/module/attribute_accessors'
5
6
  require 'active_support/core_ext/module/introspection'
7
+ require 'active_support/core_ext/module/anonymous'
6
8
  require 'active_support/core_ext/object/blank'
7
9
  require 'active_support/core_ext/load_error'
8
10
  require 'active_support/core_ext/name_error'
@@ -58,85 +60,118 @@ module ActiveSupport #:nodoc:
58
60
  mattr_accessor :log_activity
59
61
  self.log_activity = false
60
62
 
63
+ class WatchStack < Array
64
+ def initialize
65
+ @mutex = Mutex.new
66
+ end
67
+
68
+ def self.locked(*methods)
69
+ methods.each { |m| class_eval "def #{m}(*) lock { super } end" }
70
+ end
71
+
72
+ def get(key)
73
+ (val = assoc(key)) ? val[1] : []
74
+ end
75
+
76
+ locked :concat, :each, :delete_if, :<<
77
+
78
+ def new_constants_for(frames)
79
+ constants = []
80
+ frames.each do |mod_name, prior_constants|
81
+ mod = Inflector.constantize(mod_name) if Dependencies.qualified_const_defined?(mod_name)
82
+ next unless mod.is_a?(Module)
83
+
84
+ new_constants = mod.local_constant_names - prior_constants
85
+ get(mod_name).concat(new_constants)
86
+
87
+ new_constants.each do |suffix|
88
+ constants << ([mod_name, suffix] - ["Object"]).join("::")
89
+ end
90
+ end
91
+ constants
92
+ end
93
+
94
+ # Add a set of modules to the watch stack, remembering the initial constants
95
+ def add_modules(modules)
96
+ list = modules.map do |desc|
97
+ name = Dependencies.to_constant_name(desc)
98
+ consts = Dependencies.qualified_const_defined?(name) ?
99
+ Inflector.constantize(name).local_constant_names : []
100
+ [name, consts]
101
+ end
102
+ concat(list)
103
+ list
104
+ end
105
+
106
+ def lock
107
+ @mutex.synchronize { yield self }
108
+ end
109
+ end
110
+
61
111
  # An internal stack used to record which constants are loaded by any block.
62
112
  mattr_accessor :constant_watch_stack
63
- self.constant_watch_stack = []
64
-
65
- mattr_accessor :constant_watch_stack_mutex
66
- self.constant_watch_stack_mutex = Mutex.new
113
+ self.constant_watch_stack = WatchStack.new
67
114
 
68
115
  # Module includes this module
69
116
  module ModuleConstMissing #:nodoc:
70
- def self.included(base) #:nodoc:
117
+ def self.append_features(base)
71
118
  base.class_eval do
72
- unless defined? const_missing_without_dependencies
73
- alias_method_chain :const_missing, :dependencies
74
- end
119
+ # Emulate #exclude via an ivar
120
+ return if defined?(@_const_missing) && @_const_missing
121
+ @_const_missing = instance_method(:const_missing)
122
+ remove_method(:const_missing)
75
123
  end
124
+ super
76
125
  end
77
126
 
78
- def self.excluded(base) #:nodoc:
127
+ def self.exclude_from(base)
79
128
  base.class_eval do
80
- if defined? const_missing_without_dependencies
81
- undef_method :const_missing
82
- alias_method :const_missing, :const_missing_without_dependencies
83
- undef_method :const_missing_without_dependencies
84
- end
129
+ define_method :const_missing, @_const_missing
130
+ @_const_missing = nil
85
131
  end
86
132
  end
87
133
 
88
134
  # Use const_missing to autoload associations so we don't have to
89
135
  # require_association when using single-table inheritance.
90
- def const_missing_with_dependencies(class_id)
91
- ActiveSupport::Dependencies.load_missing_constant self, class_id
92
- end
136
+ def const_missing(const_name, nesting = nil)
137
+ klass_name = name.presence || "Object"
138
+
139
+ if !nesting
140
+ # We'll assume that the nesting of Foo::Bar is ["Foo::Bar", "Foo"]
141
+ # even though it might not be, such as in the case of
142
+ # class Foo::Bar; Baz; end
143
+ nesting = []
144
+ klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
145
+ end
93
146
 
94
- def unloadable(const_desc = self)
95
- super(const_desc)
96
- end
97
- end
147
+ # If there are multiple levels of nesting to search under, the top
148
+ # level is the one we want to report as the lookup fail.
149
+ error = nil
98
150
 
99
- # Class includes this module
100
- module ClassConstMissing #:nodoc:
101
- def const_missing(const_name)
102
- if [Object, Kernel].include?(self) || parent == self
103
- super
104
- else
151
+ nesting.each do |namespace|
105
152
  begin
106
- begin
107
- Dependencies.load_missing_constant self, const_name
108
- rescue NameError
109
- parent.send :const_missing, const_name
110
- end
153
+ return Dependencies.load_missing_constant namespace.constantize, const_name
154
+ rescue NoMethodError then raise
111
155
  rescue NameError => e
112
- # Make sure that the name we are missing is the one that caused the error
113
- parent_qualified_name = Dependencies.qualified_name_for parent, const_name
114
- raise unless e.missing_name? parent_qualified_name
115
- qualified_name = Dependencies.qualified_name_for self, const_name
116
- raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e)
156
+ error ||= e
117
157
  end
118
158
  end
159
+
160
+ # Raise the first error for this set. If this const_missing came from an
161
+ # earlier const_missing, this will result in the real error bubbling
162
+ # all the way up
163
+ raise error
164
+ end
165
+
166
+ def unloadable(const_desc = self)
167
+ super(const_desc)
119
168
  end
120
169
  end
121
170
 
122
171
  # Object includes this module
123
172
  module Loadable #:nodoc:
124
- def self.included(base) #:nodoc:
125
- base.class_eval do
126
- unless defined? load_without_new_constant_marking
127
- alias_method_chain :load, :new_constant_marking
128
- end
129
- end
130
- end
131
-
132
- def self.excluded(base) #:nodoc:
133
- base.class_eval do
134
- if defined? load_without_new_constant_marking
135
- undef_method :load
136
- alias_method :load, :load_without_new_constant_marking
137
- undef_method :load_without_new_constant_marking
138
- end
139
- end
173
+ def self.exclude_from(base)
174
+ base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
140
175
  end
141
176
 
142
177
  def require_or_load(file_name)
@@ -144,6 +179,10 @@ module ActiveSupport #:nodoc:
144
179
  end
145
180
 
146
181
  def require_dependency(file_name, message = "No such file to load -- %s")
182
+ unless file_name.is_a?(String)
183
+ raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
184
+ end
185
+
147
186
  Dependencies.depend_on(file_name, false, message)
148
187
  end
149
188
 
@@ -151,26 +190,23 @@ module ActiveSupport #:nodoc:
151
190
  Dependencies.associate_with(file_name)
152
191
  end
153
192
 
154
- def load_with_new_constant_marking(file, *extras) #:nodoc:
193
+ def load_dependency(file)
155
194
  if Dependencies.load?
156
- Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) }
195
+ Dependencies.new_constants_in(Object) { yield }.presence
157
196
  else
158
- load_without_new_constant_marking(file, *extras)
197
+ yield
159
198
  end
160
199
  rescue Exception => exception # errors from loading file
161
200
  exception.blame_file! file
162
201
  raise
163
202
  end
164
203
 
165
- def require(file, *extras) #:nodoc:
166
- if Dependencies.load?
167
- Dependencies.new_constants_in(Object) { super }
168
- else
169
- super
170
- end
171
- rescue Exception => exception # errors from required file
172
- exception.blame_file! file
173
- raise
204
+ def load(file, *)
205
+ load_dependency(file) { super }
206
+ end
207
+
208
+ def require(file, *)
209
+ load_dependency(file) { super }
174
210
  end
175
211
 
176
212
  # Mark the given constant as unloadable. Unloadable constants are removed each
@@ -213,16 +249,15 @@ module ActiveSupport #:nodoc:
213
249
  end
214
250
 
215
251
  def hook!
216
- Object.instance_eval { include Loadable }
217
- Module.instance_eval { include ModuleConstMissing }
218
- Class.instance_eval { include ClassConstMissing }
219
- Exception.instance_eval { include Blamable }
252
+ Object.class_eval { include Loadable }
253
+ Module.class_eval { include ModuleConstMissing }
254
+ Exception.class_eval { include Blamable }
220
255
  true
221
256
  end
222
257
 
223
258
  def unhook!
224
- ModuleConstMissing.excluded(Module)
225
- Loadable.excluded(Object)
259
+ ModuleConstMissing.exclude_from(Module)
260
+ Loadable.exclude_from(Object)
226
261
  true
227
262
  end
228
263
 
@@ -292,29 +327,22 @@ module ActiveSupport #:nodoc:
292
327
 
293
328
  # Is the provided constant path defined?
294
329
  def qualified_const_defined?(path)
295
- raise NameError, "#{path.inspect} is not a valid constant name!" unless
296
- /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path
330
+ names = path.sub(/^::/, '').to_s.split('::')
297
331
 
298
- names = path.to_s.split('::')
299
- names.shift if names.first.empty?
300
-
301
- # We can't use defined? because it will invoke const_missing for the parent
302
- # of the name we are checking.
303
332
  names.inject(Object) do |mod, name|
304
- return false unless uninherited_const_defined?(mod, name)
333
+ return false unless local_const_defined?(mod, name)
305
334
  mod.const_get name
306
335
  end
307
- return true
308
336
  end
309
337
 
310
338
  if Module.method(:const_defined?).arity == 1
311
339
  # Does this module define this constant?
312
340
  # Wrapper to accomodate changing Module#const_defined? in Ruby 1.9
313
- def uninherited_const_defined?(mod, const)
341
+ def local_const_defined?(mod, const)
314
342
  mod.const_defined?(const)
315
343
  end
316
344
  else
317
- def uninherited_const_defined?(mod, const) #:nodoc:
345
+ def local_const_defined?(mod, const) #:nodoc:
318
346
  mod.const_defined?(const, false)
319
347
  end
320
348
  end
@@ -322,29 +350,20 @@ module ActiveSupport #:nodoc:
322
350
  # Given +path+, a filesystem path to a ruby file, return an array of constant
323
351
  # paths which would cause Dependencies to attempt to load this file.
324
352
  def loadable_constants_for_path(path, bases = load_paths)
325
- path = $1 if path =~ /\A(.*)\.rb\Z/
326
- expanded_path = File.expand_path(path)
327
-
328
- bases.collect do |root|
329
- expanded_root = File.expand_path(root)
330
- next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
331
-
332
- nesting = expanded_path[(expanded_root.size)..-1]
333
- nesting = nesting[1..-1] if nesting && nesting[0] == ?/
334
- next if nesting.blank?
335
- nesting_camel = nesting.camelize
336
- begin
337
- qualified_const_defined?(nesting_camel)
338
- rescue NameError
339
- next
340
- end
341
- [ nesting_camel ]
342
- end.compact.flatten.compact.uniq
353
+ expanded_path = Pathname.new(path[/\A(.*?)(\.rb)?\Z/, 1]).expand_path
354
+
355
+ bases.inject([]) do |paths, root|
356
+ expanded_root = Pathname.new(root).expand_path
357
+ nesting = expanded_path.relative_path_from(expanded_root).to_s
358
+ next paths if nesting =~ /\.\./
359
+ paths << nesting.camelize
360
+ end.uniq
343
361
  end
344
362
 
345
363
  # Search for a file in load_paths matching the provided suffix.
346
364
  def search_for_file(path_suffix)
347
- path_suffix = "#{path_suffix}.rb" unless path_suffix =~ /\.rb\Z/
365
+ path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
366
+
348
367
  load_paths.each do |root|
349
368
  path = File.join(root, path_suffix)
350
369
  return path if File.file? path
@@ -393,7 +412,7 @@ module ActiveSupport #:nodoc:
393
412
 
394
413
  result = nil
395
414
  newly_defined_paths = new_constants_in(*parent_paths) do
396
- result = load_without_new_constant_marking path
415
+ result = Kernel.load path
397
416
  end
398
417
 
399
418
  autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
@@ -405,7 +424,7 @@ module ActiveSupport #:nodoc:
405
424
  # Return the constant path for the provided parent and constant name.
406
425
  def qualified_name_for(mod, name)
407
426
  mod_name = to_constant_name mod
408
- (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}"
427
+ mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
409
428
  end
410
429
 
411
430
  # Load the constant named +const_name+ which is missing from +from_mod+. If
@@ -413,38 +432,30 @@ module ActiveSupport #:nodoc:
413
432
  # using const_missing.
414
433
  def load_missing_constant(from_mod, const_name)
415
434
  log_call from_mod, const_name
416
- if from_mod == Kernel
417
- if ::Object.const_defined?(const_name)
418
- log "Returning Object::#{const_name} for Kernel::#{const_name}"
419
- return ::Object.const_get(const_name)
420
- else
421
- log "Substituting Object for Kernel"
422
- from_mod = Object
423
- end
424
- end
425
-
426
- # If we have an anonymous module, all we can do is attempt to load from Object.
427
- from_mod = Object if from_mod.name.blank?
428
435
 
429
- unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).object_id == from_mod.object_id
436
+ unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
430
437
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
431
438
  end
432
439
 
433
- raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined?(from_mod, const_name)
440
+ raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if local_const_defined?(from_mod, const_name)
434
441
 
435
442
  qualified_name = qualified_name_for from_mod, const_name
436
443
  path_suffix = qualified_name.underscore
444
+
445
+ trace = caller.reject {|l| l =~ %r{#{Regexp.escape(__FILE__)}}}
437
446
  name_error = NameError.new("uninitialized constant #{qualified_name}")
447
+ name_error.set_backtrace(trace)
438
448
 
439
449
  file_path = search_for_file(path_suffix)
450
+
440
451
  if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
441
452
  require_or_load file_path
442
- raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless uninherited_const_defined?(from_mod, const_name)
453
+ raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless local_const_defined?(from_mod, const_name)
443
454
  return from_mod.const_get(const_name)
444
455
  elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
445
456
  return mod
446
457
  elsif (parent = from_mod.parent) && parent != from_mod &&
447
- ! from_mod.parents.any? { |p| uninherited_const_defined?(p, const_name) }
458
+ ! from_mod.parents.any? { |p| local_const_defined?(p, const_name) }
448
459
  # If our parents do not have a constant named +const_name+ then we are free
449
460
  # to attempt to load upwards. If they do have such a constant, then this
450
461
  # const_missing must be due to from_mod::const_name, which should not
@@ -471,7 +482,7 @@ module ActiveSupport #:nodoc:
471
482
  # Determine if the given constant has been automatically loaded.
472
483
  def autoloaded?(desc)
473
484
  # No name => anonymous module.
474
- return false if desc.is_a?(Module) && desc.name.blank?
485
+ return false if desc.is_a?(Module) && desc.anonymous?
475
486
  name = to_constant_name desc
476
487
  return false unless qualified_const_defined? name
477
488
  return autoloaded_constants.include?(name)
@@ -505,79 +516,26 @@ module ActiveSupport #:nodoc:
505
516
  # and will be removed immediately.
506
517
  def new_constants_in(*descs)
507
518
  log_call(*descs)
508
-
509
- # Build the watch frames. Each frame is a tuple of
510
- # [module_name_as_string, constants_defined_elsewhere]
511
- watch_frames = descs.collect do |desc|
512
- if desc.is_a? Module
513
- mod_name = desc.name
514
- initial_constants = desc.local_constant_names
515
- elsif desc.is_a?(String) || desc.is_a?(Symbol)
516
- mod_name = desc.to_s
517
-
518
- # Handle the case where the module has yet to be defined.
519
- initial_constants = if qualified_const_defined?(mod_name)
520
- Inflector.constantize(mod_name).local_constant_names
521
- else
522
- []
523
- end
524
- else
525
- raise Argument, "#{desc.inspect} does not describe a module!"
526
- end
527
-
528
- [mod_name, initial_constants]
529
- end
530
-
531
- constant_watch_stack_mutex.synchronize do
532
- constant_watch_stack.concat watch_frames
533
- end
519
+ watch_frames = constant_watch_stack.add_modules(descs)
534
520
 
535
521
  aborting = true
536
522
  begin
537
523
  yield # Now yield to the code that is to define new constants.
538
524
  aborting = false
539
525
  ensure
540
- # Find the new constants.
541
- new_constants = watch_frames.collect do |mod_name, prior_constants|
542
- # Module still doesn't exist? Treat it as if it has no constants.
543
- next [] unless qualified_const_defined?(mod_name)
544
-
545
- mod = Inflector.constantize(mod_name)
546
- next [] unless mod.is_a? Module
547
- new_constants = mod.local_constant_names - prior_constants
548
-
549
- # Make sure no other frames takes credit for these constants.
550
- constant_watch_stack_mutex.synchronize do
551
- constant_watch_stack.each do |frame_name, constants|
552
- constants.concat new_constants if frame_name == mod_name
553
- end
554
- end
555
-
556
- new_constants.collect do |suffix|
557
- mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}"
558
- end
559
- end.flatten
526
+ new_constants = constant_watch_stack.new_constants_for(watch_frames)
560
527
 
561
528
  log "New constants: #{new_constants * ', '}"
529
+ return new_constants unless aborting
562
530
 
563
- if aborting
564
- log "Error during loading, removing partially loaded constants "
565
- new_constants.each { |name| remove_constant name }
566
- new_constants.clear
567
- end
531
+ log "Error during loading, removing partially loaded constants "
532
+ new_constants.each {|c| remove_constant(c) }.clear
568
533
  end
569
534
 
570
- return new_constants
535
+ return []
571
536
  ensure
572
537
  # Remove the stack frames that we added.
573
- if defined?(watch_frames) && ! watch_frames.blank?
574
- frame_ids = watch_frames.collect { |frame| frame.object_id }
575
- constant_watch_stack_mutex.synchronize do
576
- constant_watch_stack.delete_if do |watch_frame|
577
- frame_ids.include? watch_frame.object_id
578
- end
579
- end
580
- end
538
+ watch_frames.each {|f| constant_watch_stack.delete(f) } if watch_frames.present?
581
539
  end
582
540
 
583
541
  class LoadingModule #:nodoc:
@@ -595,11 +553,11 @@ module ActiveSupport #:nodoc:
595
553
  # A module, class, symbol, or string may be provided.
596
554
  def to_constant_name(desc) #:nodoc:
597
555
  name = case desc
598
- when String then desc.starts_with?('::') ? desc[2..-1] : desc
556
+ when String then desc.sub(/^::/, '')
599
557
  when Symbol then desc.to_s
600
558
  when Module
601
- raise ArgumentError, "Anonymous modules have no name to be referenced by" if desc.name.blank?
602
- desc.name
559
+ desc.name.presence ||
560
+ raise(ArgumentError, "Anonymous modules have no name to be referenced by")
603
561
  else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
604
562
  end
605
563
  end
@@ -607,16 +565,13 @@ module ActiveSupport #:nodoc:
607
565
  def remove_constant(const) #:nodoc:
608
566
  return false unless qualified_const_defined? const
609
567
 
610
- const = $1 if /\A::(.*)\Z/ =~ const.to_s
611
- names = const.to_s.split('::')
612
- if names.size == 1 # It's under Object
613
- parent = Object
614
- else
615
- parent = Inflector.constantize(names[0..-2] * '::')
616
- end
568
+ # Normalize ::Foo, Foo, Object::Foo, and ::Object::Foo to Object::Foo
569
+ names = const.to_s.sub(/^::(Object)?/, 'Object::').split("::")
570
+ to_remove = names.pop
571
+ parent = Inflector.constantize(names * '::')
617
572
 
618
573
  log "removing constant #{const}"
619
- parent.instance_eval { remove_const names.last }
574
+ parent.instance_eval { remove_const to_remove }
620
575
  return true
621
576
  end
622
577