@@ -63,58 +63,78 @@ module Dependencies #:nodoc:
63
63
mattr_accessor :log_activity
64
64
self . log_activity = false
65
65
66
- class WatchStack < Array
66
+ # The WatchStack keeps a stack of the modules being watched as files are loaded.
67
+ # If a file in the process of being loaded (parent.rb) triggers the load of
68
+ # another file (child.rb) the stack will ensure that child.rb handles the new
69
+ # constants.
70
+ #
71
+ # If child.rb is being autoloaded, its constants will be added to
72
+ # autoloaded_constants. If it was being `require`d, they will be discarded.
73
+ #
74
+ # This is handled by walking back up the watch stack and adding the constants
75
+ # found by child.rb to the list of original constants in parent.rb
76
+ class WatchStack < Hash
77
+ # @watching is a stack of lists of constants being watched. For instance,
78
+ # if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb
79
+ # then requires namespace/child.rb, the stack will look like [[Object], [Namespace]].
80
+
67
81
def initialize
68
- @mutex = Mutex . new
82
+ @watching = [ ]
83
+ super { |h , k | h [ k ] = [ ] }
69
84
end
70
85
71
- def self . locked ( * methods )
72
- methods . each { | m | class_eval " def #{ m } (*) lock { super } end" , __FILE__ , __LINE__ }
73
- end
86
+ # return a list of new constants found since the last call to watch_modules
87
+ def new_constants
88
+ constants = [ ]
74
89
75
- locked :concat , :each , :delete_if , :<<
90
+ # Grab the list of namespaces that we're looking for new constants under
91
+ @watching . last . each do |namespace |
92
+ # Retrieve the constants that were present under the namespace when watch_modules
93
+ # was originally called
94
+ original_constants = self [ namespace ] . last
76
95
77
- def new_constants_for ( frames )
78
- constants = [ ]
79
- frames . each do |mod_name , prior_constants |
80
- mod = Inflector . constantize ( mod_name ) if Dependencies . qualified_const_defined? ( mod_name )
96
+ mod = Inflector . constantize ( namespace ) if Dependencies . qualified_const_defined? ( namespace )
81
97
next unless mod . is_a? ( Module )
82
98
83
- new_constants = mod . local_constant_names - prior_constants
84
-
85
- # If we are checking for constants under, say, :Object, nested under something
86
- # else that is checking for constants also under :Object, make sure the
87
- # parent knows that we have found, and taken care of, the constant.
88
- #
89
- # In particular, this means that since Kernel.require discards the constants
90
- # it finds, parents will be notified that about those constants, and not
91
- # consider them "new". As a result, they will not be added to the
92
- # autoloaded_constants list.
93
- each do |key , value |
94
- value . concat ( new_constants ) if key == mod_name
99
+ # Get a list of the constants that were added
100
+ new_constants = mod . local_constant_names - original_constants
101
+
102
+ # self[namespace] returns an Array of the constants that are being evaluated
103
+ # for that namespace. For instance, if parent.rb requires child.rb, the first
104
+ # element of self[Object] will be an Array of the constants that were present
105
+ # before parent.rb was required. The second element will be an Array of the
106
+ # constants that were present before child.rb was required.
107
+ self [ namespace ] . each do |constants |
108
+ constants . concat ( new_constants )
95
109
end
96
110
111
+ # Normalize the list of new constants, and add them to the list we will return
97
112
new_constants . each do |suffix |
98
- constants << ( [ mod_name , suffix ] - [ "Object" ] ) . join ( "::" )
113
+ constants << ( [ namespace , suffix ] - [ "Object" ] ) . join ( "::" )
99
114
end
100
115
end
101
116
constants
117
+ ensure
118
+ # A call to new_constants is always called after a call to watch_modules
119
+ pop_modules ( @watching . pop )
102
120
end
103
121
104
122
# Add a set of modules to the watch stack, remembering the initial constants
105
- def add_modules ( modules )
106
- list = modules . map do |desc |
107
- name = Dependencies . to_constant_name ( desc )
108
- consts = Dependencies . qualified_const_defined? ( name ) ?
109
- Inflector . constantize ( name ) . local_constant_names : [ ]
110
- [ name , consts ]
123
+ def watch_namespaces ( namespaces )
124
+ watching = [ ]
125
+ namespaces . map do |namespace |
126
+ module_name = Dependencies . to_constant_name ( namespace )
127
+ original_constants = Dependencies . qualified_const_defined? ( module_name ) ?
128
+ Inflector . constantize ( module_name ) . local_constant_names : [ ]
129
+
130
+ watching << module_name
131
+ self [ module_name ] << original_constants
111
132
end
112
- concat ( list )
113
- list
133
+ @watching << watching
114
134
end
115
135
116
- def lock
117
- @mutex . synchronize { yield self }
136
+ def pop_modules ( modules )
137
+ modules . each { | mod | self [ mod ] . pop }
118
138
end
119
139
end
120
140
@@ -563,14 +583,15 @@ def mark_for_unload(const_desc)
563
583
# and will be removed immediately.
564
584
def new_constants_in ( *descs )
565
585
log_call ( *descs )
566
- watch_frames = constant_watch_stack . add_modules ( descs )
567
586
587
+ constant_watch_stack . watch_namespaces ( descs )
568
588
aborting = true
589
+
569
590
begin
570
591
yield # Now yield to the code that is to define new constants.
571
592
aborting = false
572
593
ensure
573
- new_constants = constant_watch_stack . new_constants_for ( watch_frames )
594
+ new_constants = constant_watch_stack . new_constants
574
595
575
596
log "New constants: #{ new_constants * ', ' } "
576
597
return new_constants unless aborting
@@ -580,9 +601,6 @@ def new_constants_in(*descs)
580
601
end
581
602
582
603
return [ ]
583
- ensure
584
- # Remove the stack frames that we added.
585
- watch_frames . each { |f | constant_watch_stack . delete ( f ) } if watch_frames . present?
586
604
end
587
605
588
606
class LoadingModule #:nodoc:
0 commit comments