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.
- data/CHANGELOG +15 -1
- data/lib/active_support.rb +3 -0
- data/lib/active_support/all.rb +0 -1
- data/lib/active_support/cache/mem_cache_store.rb +1 -1
- data/lib/active_support/callbacks.rb +2 -2
- data/lib/active_support/core_ext/array/conversions.rb +0 -1
- data/lib/active_support/core_ext/array/extract_options.rb +16 -1
- data/lib/active_support/core_ext/class.rb +1 -0
- data/lib/active_support/core_ext/class/attribute.rb +30 -5
- data/lib/active_support/core_ext/class/attribute_accessors.rb +33 -27
- data/lib/active_support/core_ext/class/delegating_attributes.rb +10 -7
- data/lib/active_support/core_ext/class/subclasses.rb +55 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +1 -0
- data/lib/active_support/core_ext/file/atomic.rb +3 -2
- data/lib/active_support/core_ext/file/path.rb +5 -0
- data/lib/active_support/core_ext/hash/conversions.rb +9 -0
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +1 -1
- data/lib/active_support/core_ext/module.rb +5 -3
- data/lib/active_support/core_ext/module/anonymous.rb +24 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -21
- data/lib/active_support/core_ext/module/delegation.rb +20 -9
- data/lib/active_support/core_ext/module/introspection.rb +8 -8
- data/lib/active_support/core_ext/module/method_names.rb +14 -0
- data/lib/active_support/core_ext/module/reachable.rb +10 -0
- data/lib/active_support/core_ext/module/remove_method.rb +6 -0
- data/lib/active_support/core_ext/object.rb +7 -1
- data/lib/active_support/core_ext/object/extending.rb +11 -0
- data/lib/active_support/core_ext/object/singleton_class.rb +13 -0
- data/lib/active_support/core_ext/proc.rb +3 -3
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/interpolation.rb +1 -91
- data/lib/active_support/core_ext/string/output_safety.rb +12 -8
- data/lib/active_support/core_ext/string/xchar.rb +1 -1
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/time/marshal.rb +56 -0
- data/lib/active_support/dependencies.rb +146 -191
- data/lib/active_support/dependencies/autoload.rb +1 -0
- data/lib/active_support/deprecation/method_wrappers.rb +9 -9
- data/lib/active_support/deprecation/reporting.rb +2 -1
- data/lib/active_support/hash_with_indifferent_access.rb +4 -0
- data/lib/active_support/i18n.rb +2 -1
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/inflector/transliterate.rb +3 -3
- data/lib/active_support/json/backends/yajl.rb +40 -0
- data/lib/active_support/json/decoding.rb +16 -1
- data/lib/active_support/lazy_load_hooks.rb +17 -0
- data/lib/active_support/memoizable.rb +1 -1
- data/lib/active_support/multibyte.rb +2 -2
- data/lib/active_support/multibyte/utils.rb +1 -1
- data/lib/active_support/notifications.rb +7 -3
- data/lib/active_support/notifications/fanout.rb +19 -5
- data/lib/active_support/ordered_options.rb +6 -0
- data/lib/active_support/railtie.rb +9 -9
- data/lib/active_support/ruby/shim.rb +2 -0
- data/lib/active_support/test_case.rb +2 -7
- data/lib/active_support/testing/assertions.rb +15 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/time.rb +1 -1
- data/lib/active_support/values/time_zone.rb +7 -4
- data/lib/active_support/version.rb +3 -2
- data/lib/active_support/whiny_nil.rb +4 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
- data/lib/active_support/xml_mini/rexml.rb +7 -1
- metadata +65 -31
- data/lib/active_support/core_ext/kernel/daemonizing.rb +0 -7
- data/lib/active_support/core_ext/module/inclusion.rb +0 -30
- data/lib/active_support/core_ext/module/loading.rb +0 -25
- data/lib/active_support/core_ext/object/metaclass.rb +0 -13
- 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.
|
117
|
+
def self.append_features(base)
|
71
118
|
base.class_eval do
|
72
|
-
|
73
|
-
|
74
|
-
|
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.
|
127
|
+
def self.exclude_from(base)
|
79
128
|
base.class_eval do
|
80
|
-
|
81
|
-
|
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
|
91
|
-
|
92
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
-
|
107
|
-
|
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
|
-
|
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.
|
125
|
-
base.class_eval
|
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
|
193
|
+
def load_dependency(file)
|
155
194
|
if Dependencies.load?
|
156
|
-
Dependencies.new_constants_in(Object) {
|
195
|
+
Dependencies.new_constants_in(Object) { yield }.presence
|
157
196
|
else
|
158
|
-
|
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
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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.
|
217
|
-
Module.
|
218
|
-
|
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.
|
225
|
-
Loadable.
|
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
|
-
|
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
|
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
|
341
|
+
def local_const_defined?(mod, const)
|
314
342
|
mod.const_defined?(const)
|
315
343
|
end
|
316
344
|
else
|
317
|
-
def
|
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
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
next
|
331
|
-
|
332
|
-
|
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 =
|
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 =
|
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
|
-
|
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).
|
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
|
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
|
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|
|
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.
|
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
|
-
|
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
|
-
|
564
|
-
|
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
|
535
|
+
return []
|
571
536
|
ensure
|
572
537
|
# Remove the stack frames that we added.
|
573
|
-
|
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.
|
556
|
+
when String then desc.sub(/^::/, '')
|
599
557
|
when Symbol then desc.to_s
|
600
558
|
when Module
|
601
|
-
|
602
|
-
|
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
|
-
|
611
|
-
names = const.to_s.
|
612
|
-
|
613
|
-
|
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
|
574
|
+
parent.instance_eval { remove_const to_remove }
|
620
575
|
return true
|
621
576
|
end
|
622
577
|
|