rails-dev-boost 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +19 -0
  3. data/README.markdown +105 -0
  4. data/Rakefile +52 -0
  5. data/TODO.txt +2 -0
  6. data/VERSION +1 -0
  7. data/init.rb +3 -0
  8. data/lib/rails_development_boost.rb +32 -0
  9. data/lib/rails_development_boost/dependencies_patch.rb +332 -0
  10. data/lib/rails_development_boost/descendants_tracker_patch.rb +18 -0
  11. data/lib/rails_development_boost/loaded_file.rb +76 -0
  12. data/lib/rails_development_boost/reference_patch.rb +24 -0
  13. data/lib/rails_development_boost/view_helpers_patch.rb +17 -0
  14. data/rails-dev-boost.gemspec +116 -0
  15. data/test/constants/active_record/comment.rb +2 -0
  16. data/test/constants/active_record/message.rb +2 -0
  17. data/test/constants/active_record/other.rb +2 -0
  18. data/test/constants/active_record/post.rb +3 -0
  19. data/test/constants/deep_nesting/a.rb +2 -0
  20. data/test/constants/deep_nesting/a/b.rb +2 -0
  21. data/test/constants/deep_nesting/a/b/c.rb +2 -0
  22. data/test/constants/deep_nesting/a/b/c/d.rb +2 -0
  23. data/test/constants/double_removal/ns.rb +3 -0
  24. data/test/constants/double_removal/ns/c.rb +3 -0
  25. data/test/constants/double_removal/ns/m.rb +2 -0
  26. data/test/constants/mixins/client.rb +3 -0
  27. data/test/constants/mixins/mixin.rb +4 -0
  28. data/test/constants/mixins/update/mixin.rb +4 -0
  29. data/test/constants/nested_mixins/b.rb +2 -0
  30. data/test/constants/nested_mixins/b/c.rb +2 -0
  31. data/test/constants/nested_mixins/ma.rb +3 -0
  32. data/test/constants/nested_mixins/ma/mb.rb +3 -0
  33. data/test/constants/nested_mixins/ma/mb/mc.rb +2 -0
  34. data/test/constants/nested_mixins/oa.rb +4 -0
  35. data/test/constants/nested_mixins/oa/ob.rb +3 -0
  36. data/test/constants/nested_mixins/oa/ob/oc.rb +2 -0
  37. data/test/constants/single_removal/a.rb +2 -0
  38. data/test/constants/single_removal/b.rb +2 -0
  39. data/test/constants/singleton_mixins/a.rb +3 -0
  40. data/test/constants/singleton_mixins/b.rb +2 -0
  41. data/test/constants/subclass/a.rb +2 -0
  42. data/test/constants/subclass/b.rb +2 -0
  43. data/test/constants/subclass/c.rb +2 -0
  44. data/test/rails_development_boost_test.rb +222 -0
  45. data/test/stub_environment.rb +41 -0
  46. metadata +144 -0
@@ -0,0 +1 @@
1
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Roman Le Négrate
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,105 @@
1
+ # RailsDevelopmentBoost
2
+
3
+ Make your Rails app 10 times faster in development mode (see FAQ below for more details).
4
+
5
+ Alternative to Josh Goebel's [`rails_dev_mode_performance`](https://github.com/yyyc514/rails_dev_mode_performance) plugin.
6
+
7
+ ## Branches
8
+
9
+ If you are using **Rails 3**: [`rails-dev-boost/master`](http://github.com/thedarkone/rails-dev-boost/tree/master) branch.
10
+
11
+ If you are using **Rails 2.3**: [`rails-dev-boost/rails-2-3`](http://github.com/thedarkone/rails-dev-boost/tree/rails-2-3) branch.
12
+
13
+ If you are using **Rails 2.2**: [`rails-dev-boost/rails-2-2`](http://github.com/thedarkone/rails-dev-boost/tree/rails-2-2) branch.
14
+
15
+ If you are using **Rails 2.1** or **Rails 2.0** or **anything older**: you are out of luck.
16
+
17
+ ## Background
18
+
19
+ Why create a similar plugin? Because I couldn't get Josh Goebel's to work in my projects. His attempts to keep templates cached in a way that fails with recent versions of Rails. Also, removing the faulty chunk of code revealed another issue: it stats source files that may not exist, without trying to find their real path beforehand. That would be fixable is the code wasn't such a mess (no offense).
20
+
21
+ I needed better performance in development mode right away, so here is an alternative implementation.
22
+
23
+ ## Usage
24
+
25
+ ### Rails 3
26
+
27
+ Usage through `Gemfile`:
28
+
29
+ group :development do
30
+ gem 'rails-dev-boost', :git => 'git://github.com/thedarkone/rails-dev-boost.git', :require => 'rails_development_boost'
31
+ end
32
+
33
+ Installing as a plugin:
34
+
35
+ script/rails plugin install git://github.com/thedarkone/rails-dev-boost
36
+
37
+ ### Rails 2.3 and older
38
+
39
+ script/plugin install git://github.com/thedarkone/rails-dev-boost -r rails-2-3
40
+
41
+ When the server is started in *development* mode, the special unloading mechanism takes over.
42
+
43
+ It can also be used in combination with [RailsTestServing](https://github.com/Roman2K/rails-test-serving) for even faster test runs by forcefully enabling it in test mode. To do so, add the following in `config/environments/test.rb`:
44
+
45
+ def config.soft_reload() true end if RailsTestServing.active?
46
+
47
+ ## FAQ
48
+
49
+ ### Q: Since the plugin uses its special "unloading mechanism" won't everything break down?
50
+ A: Very unlikely... of course there are some edge cases where you might see some breakage (mainly if you're deviating from the Rails 1 file = 1 class conventions or doing some weird `require`s). This is a 99% solution and the seconds you're wasting waiting for the Rails to spit out a page in the dev mode do add up in the long run.
51
+
52
+ ### Q: How big of a boost is it going to give me?
53
+ A: It depends on the size of your app (the bigger it is the bigger your boost is going to be). The speed is then approximately equal to that of production env. plus the time it takes to stat all your app's `*.rb` files (which is surprisingly fast as it is cached by OS). Empty 1 controller 2 views app will become about 4x times faster more complex apps will see huge improvements.
54
+
55
+ ### Q: I'm using an older version of Rails than 2.2, will this work for me?
56
+ A: Unfortunately you are on your own right now :(.
57
+
58
+ ### Q: My `Article` model does not pick up changes from the `articles` table.
59
+ A: You need to force it to be reloaded (just hit the save button in your editor for `article.rb` file).
60
+
61
+ ### Q: I used `require 'article'` and the `Article` model is not being reloaded.
62
+ A: You really shouldn't be using `require` to load your files in the Rails app (if you want them to be automatically reloaded) and let automatic constant loading handle the require for you. You can also use `require_dependency 'article'`, as it goes through the Rails stack.
63
+
64
+ ### Q: I'm using class variables (by class variables I mean "metaclass instance variables") and they are not being reloaded.
65
+ A: Class level instance variables are not thread safe and you shouldn't be really using them :). There is generally only one case where they might pose a problem for `rails-dev-boost`:
66
+
67
+ #app/models/article.rb
68
+ class Article < ActiveRecord::Base
69
+ end
70
+
71
+ #app/models/blog.rb
72
+ class Blog < ActiveRecord::Base
73
+ def self.all_articles
74
+ @all_articles ||= Article.all
75
+ end
76
+ end
77
+
78
+ Modifying `article.rb` will not reload `@all_articles` (you would always need to re-save `blog.rb` as well).
79
+
80
+ The solution is to move class instance variable to its class like this:
81
+
82
+ #app/models/article.rb
83
+ class Article < ActiveRecord::Base
84
+ def self.all_articles
85
+ @all_articles ||= all
86
+ end
87
+ end
88
+
89
+ #app/models/blog.rb
90
+ class Blog < ActiveRecord::Base
91
+ def self.all_articles
92
+ Article.all_articles
93
+ end
94
+ end
95
+
96
+ This way saving `arcticle.rb` will trigger the reload of `@all_articles`.
97
+
98
+ ### Q: I'm using JRuby, is it going to work?
99
+ A: I haven't tested the plugin with JRuby, but the plugin does use `ObjectSpace` to do its magic. `ObjectSpace` is AFAIK disabled by default on JRuby.
100
+
101
+ FAQ added by [thedarkone](http://github.com/thedarkone).
102
+
103
+ ## Credits
104
+
105
+ Written by [Roman Le Négrate](http://roman.flucti.com) ([contact](mailto:roman.lenegrate@gmail.com)). Released under the MIT-license: see the `LICENSE` file.
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rails-dev-boost"
8
+ gem.summary = %Q{Speeds up Rails development mode}
9
+ gem.description = %Q{Make your Rails app 10 times faster in development mode}
10
+ gem.email = "roman.lenegrate@gmail.com"
11
+ gem.homepage = "http://github.com/thedarkone/rails-dev-boost"
12
+ gem.authors = ["Roman Le Négrate","thedarkone"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/test_*.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "thing #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
@@ -0,0 +1,2 @@
1
+ * Update ActiveRecord reflections for which the module passed as the :extend option has been unloaded.
2
+ - update reference: MacroReflection#@options[:extend]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ if !$rails_rake_task && (Rails.env.development? || (config.respond_to?(:soft_reload) && config.soft_reload))
2
+ require 'rails_development_boost'
3
+ end
@@ -0,0 +1,32 @@
1
+ module RailsDevelopmentBoost
2
+ ActiveSupport.on_load(:after_initialize) do
3
+ ReferencePatch.apply!
4
+ DependenciesPatch.apply!
5
+ DescendantsTrackerPatch.apply!
6
+ ObservablePatch.apply!
7
+
8
+ # this should go into ActiveSupport.on_load(:action_pack), alas Rails doesn't provide it
9
+ if defined?(ActionDispatch::Reloader) # post 0f7c970
10
+ ActionDispatch::Reloader.to_prepare { ActiveSupport::Dependencies.unload_modified_files! }
11
+ else
12
+ ActionDispatch::Callbacks.before { ActiveSupport::Dependencies.unload_modified_files! }
13
+ end
14
+ end
15
+
16
+ ActiveSupport.on_load(:action_controller) do
17
+ ActiveSupport.on_load(:after_initialize) do
18
+ ViewHelpersPatch.apply!
19
+ end
20
+ end
21
+
22
+ autoload :DependenciesPatch, 'rails_development_boost/dependencies_patch'
23
+ autoload :DescendantsTrackerPatch, 'rails_development_boost/descendants_tracker_patch'
24
+ autoload :LoadedFile, 'rails_development_boost/loaded_file'
25
+ autoload :ObservablePatch, 'rails_development_boost/observable_patch'
26
+ autoload :ReferencePatch, 'rails_development_boost/reference_patch'
27
+ autoload :ViewHelpersPatch, 'rails_development_boost/view_helpers_patch'
28
+
29
+ def self.debug!
30
+ DependenciesPatch.debug!
31
+ end
32
+ end
@@ -0,0 +1,332 @@
1
+ module RailsDevelopmentBoost
2
+ module DependenciesPatch
3
+ module LoadablePatch
4
+ def require_dependency_with_constant_tracking(*args)
5
+ ActiveSupport::Dependencies.required_dependency(args.first)
6
+ # handle plugins such as concerned_with
7
+ require_dependency_without_constant_tracking(*args)
8
+ end
9
+ end
10
+
11
+ def self.apply!
12
+ # retain the original method in case the application overwrites it on its modules/klasses
13
+ Module.send :alias_method, :_mod_name, :name
14
+
15
+ patch = self
16
+ ActiveSupport::Dependencies.module_eval do
17
+ alias_method :local_const_defined?, :uninherited_const_defined? unless method_defined?(:local_const_defined?) # pre 4da45060 compatibility
18
+ remove_possible_method :remove_unloadable_constants!
19
+ remove_possible_method :clear
20
+ include patch
21
+ alias_method_chain :load_file, 'constant_tracking'
22
+ alias_method_chain :remove_constant, 'handling_of_connections'
23
+ extend patch
24
+ end
25
+
26
+ ActiveSupport::Dependencies::Loadable.module_eval do
27
+ include LoadablePatch
28
+ alias_method_chain :require_dependency, 'constant_tracking'
29
+ end
30
+
31
+ InstrumentationPatch.apply! if @do_instrument
32
+
33
+ ActiveSupport::Dependencies.handle_already_autoloaded_constants!
34
+ end
35
+
36
+ def self.debug!
37
+ if ActiveSupport::Dependencies < DependenciesPatch
38
+ InstrumentationPatch.apply!
39
+ else
40
+ @do_instrument = true
41
+ end
42
+ end
43
+
44
+ autoload :InstrumentationPatch, 'rails_development_boost/dependencies_patch/instrumentation_patch'
45
+
46
+ mattr_accessor :module_cache
47
+ self.module_cache = []
48
+
49
+ mattr_accessor :file_map
50
+ self.file_map = {}
51
+
52
+ mattr_accessor :constants_being_removed
53
+ self.constants_being_removed = []
54
+
55
+ mattr_accessor :explicit_dependencies
56
+ self.explicit_dependencies = {}
57
+
58
+ def unload_modified_files!
59
+ log_call
60
+ file_map.values.each do |file|
61
+ unload_modified_file(file) if file.changed?
62
+ end
63
+ end
64
+
65
+ def remove_explicitely_unloadable_constants!
66
+ explicitly_unloadable_constants.each { |const| remove_constant(const) }
67
+ end
68
+
69
+ # Overridden.
70
+ def clear
71
+ end
72
+
73
+ # Augmented `load_file'.
74
+ def load_file_with_constant_tracking(path, *args, &block)
75
+ result = now_loading(path) { load_file_without_constant_tracking(path, *args, &block) }
76
+
77
+ unless load_once_path?(path)
78
+ new_constants = autoloaded_constants - file_map.values.map(&:constants).flatten
79
+
80
+ # Associate newly loaded constants to the file just loaded
81
+ associate_constants_to_file(new_constants, path)
82
+ end
83
+
84
+ result
85
+ end
86
+
87
+ def now_loading(path)
88
+ @currently_loading, old_currently_loading = path, @currently_loading
89
+ yield
90
+ rescue Exception => e
91
+ error_loading_file(@currently_loading, e)
92
+ ensure
93
+ @currently_loading = old_currently_loading
94
+ end
95
+
96
+ def associate_constants_to_file(constants, file_path)
97
+ # freezing strings before using them as Hash keys is slightly more memory efficient
98
+ constants.map!(&:freeze)
99
+ file_path.freeze
100
+
101
+ loaded_file_for(file_path).add_constants(constants)
102
+ end
103
+
104
+ def loaded_file_for(file_path)
105
+ file_map[file_path] ||= LoadedFile.new(file_path)
106
+ end
107
+
108
+ # Augmented `remove_constant'.
109
+ def remove_constant_with_handling_of_connections(const_name)
110
+ fetch_module_cache do
111
+ prevent_further_removal_of(const_name) do
112
+ unprotected_remove_constant(const_name)
113
+ end
114
+ end
115
+ end
116
+
117
+ def required_dependency(file_name)
118
+ # Rails uses require_dependency for loading helpers, we are however dealing with the helper problem elsewhere, so we can skip them
119
+ if @currently_loading && @currently_loading !~ /_controller(?:\.rb)?\Z/ && file_name !~ /_helper(?:\.rb)?\Z/
120
+ if full_path = ActiveSupport::Dependencies.search_for_file(file_name)
121
+ loaded_file_for(@currently_loading).associate_with(loaded_file_for(full_path))
122
+ end
123
+ end
124
+ end
125
+
126
+ def add_explicit_dependency(parent, child)
127
+ (explicit_dependencies[parent._mod_name] ||= []) << child._mod_name
128
+ end
129
+
130
+ def handle_already_autoloaded_constants! # we might be late to the party and other gems/plugins might have already triggered autoloading of some constants
131
+ loaded.each do |require_path|
132
+ associate_constants_to_file(autoloaded_constants, "#{require_path}.rb") # slightly heavy-handed..
133
+ end
134
+ end
135
+
136
+ private
137
+ def unprotected_remove_constant(const_name)
138
+ if qualified_const_defined?(const_name) && object = const_name.constantize
139
+ handle_connected_constants(object, const_name)
140
+ remove_same_file_constants(const_name)
141
+ if object.kind_of?(Module)
142
+ remove_parent_modules_if_autoloaded(object)
143
+ remove_child_module_constants(object)
144
+ end
145
+ end
146
+ result = remove_constant_without_handling_of_connections(const_name)
147
+ clear_tracks_of_removed_const(const_name, object)
148
+ result
149
+ end
150
+
151
+ def unload_file(file)
152
+ file.constants.dup.each {|const| remove_constant(const)}
153
+ clean_up_if_no_constants(file)
154
+ end
155
+ alias_method :unload_modified_file, :unload_file
156
+
157
+ def error_loading_file(file_path, e)
158
+ loaded_file_for(file_path).stale!
159
+ raise e
160
+ end
161
+
162
+ def handle_connected_constants(object, const_name)
163
+ return unless Module === object && qualified_const_defined?(const_name)
164
+ remove_explicit_dependencies_of(const_name)
165
+ remove_dependent_modules(object)
166
+ update_activerecord_related_references(object)
167
+ remove_nested_constants(const_name)
168
+ end
169
+
170
+ def remove_nested_constants(const_name)
171
+ autoloaded_constants.grep(/\A#{const_name}::/).each { |const| remove_nested_constant(const_name, const) }
172
+ end
173
+
174
+ def remove_nested_constant(parent_const, child_const)
175
+ remove_constant(child_const)
176
+ end
177
+
178
+ def autoloaded_namespace_object?(object) # faster than going through Dependencies.autoloaded?
179
+ LoadedFile.constants_to_files[object._mod_name]
180
+ end
181
+
182
+ # AS::Dependencies doesn't track same-file nested constants, so we need to look out for them on our own.
183
+ # For example having loaded an abc.rb that looks like this:
184
+ # class Abc; class Inner; end; end
185
+ # AS::Dependencies would only add "Abc" constant name to its autoloaded_constants list, completely ignoring Abc::Inner. This in turn
186
+ # can cause problems for classes inheriting from Abc::Inner somewhere else in the app.
187
+ def remove_parent_modules_if_autoloaded(object)
188
+ unless autoloaded_namespace_object?(object)
189
+ initial_object = object
190
+
191
+ while (object = object.parent) != Object
192
+ if autoloaded_namespace_object?(object)
193
+ remove_autoloaded_parent_module(initial_object, object)
194
+ break
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def remove_autoloaded_parent_module(initial_object, parent_object)
201
+ remove_constant(parent_object._mod_name)
202
+ end
203
+
204
+ # AS::Dependencies doesn't track same-file nested constants, so we need to look out for them on our own and remove any dependent modules/constants
205
+ def remove_child_module_constants(object)
206
+ object.constants.each do |const_name|
207
+ # we only care about "namespace" constants (classes/modules)
208
+ if local_const_defined?(object, const_name) && (child_const = object.const_get(const_name)).kind_of?(Module)
209
+ remove_child_module_constant(object, child_const)
210
+ end
211
+ end
212
+ end
213
+
214
+ def remove_child_module_constant(parent_object, child_constant)
215
+ remove_constant(child_constant._mod_name)
216
+ end
217
+
218
+ def in_autoloaded_namespace?(object)
219
+ while object != Object
220
+ return true if autoloaded_namespace_object?(object)
221
+ object = object.parent
222
+ end
223
+ false
224
+ end
225
+
226
+ def remove_same_file_constants(const_name)
227
+ LoadedFile.each_file_with_const(const_name) {|file| unload_containing_file(const_name, file)}
228
+ end
229
+
230
+ def unload_containing_file(const_name, file)
231
+ unload_file(file)
232
+ end
233
+
234
+ def remove_explicit_dependencies_of(const_name)
235
+ if dependencies = explicit_dependencies.delete(const_name)
236
+ dependencies.uniq.each {|depending_const| remove_explicit_dependency(const_name, depending_const)}
237
+ end
238
+ end
239
+
240
+ def remove_explicit_dependency(const_name, depending_const)
241
+ remove_constant(depending_const)
242
+ end
243
+
244
+ def clear_tracks_of_removed_const(const_name, object)
245
+ autoloaded_constants.delete(const_name)
246
+ module_cache.delete_if { |mod| mod._mod_name == const_name }
247
+ clean_up_references(const_name, object)
248
+
249
+ LoadedFile.each_file_with_const(const_name) do |file|
250
+ file.delete_constant(const_name)
251
+ clean_up_if_no_constants(file)
252
+ end
253
+ end
254
+
255
+ def clean_up_if_no_constants(file)
256
+ if file.constants.empty?
257
+ loaded.delete(file.require_path)
258
+ file_map.delete(file.path)
259
+ end
260
+ end
261
+
262
+ def clean_up_references(const_name, object)
263
+ ActiveSupport::Dependencies::Reference.loose!(const_name)
264
+ ActiveSupport::DescendantsTracker.delete(object)
265
+ end
266
+
267
+ def remove_dependent_modules(mod)
268
+ fetch_module_cache do |modules|
269
+ modules.dup.each do |other|
270
+ next unless other < mod || other.singleton_class.ancestors.include?(mod)
271
+ next unless first_non_anonymous_superclass(other) == mod if Class === mod
272
+ next unless qualified_const_defined?(other._mod_name) && other._mod_name.constantize == other
273
+ next unless in_autoloaded_namespace?(other)
274
+ remove_dependent_constant(mod, other)
275
+ end
276
+ end
277
+ end
278
+
279
+ def remove_dependent_constant(original_module, dependent_module)
280
+ remove_constant(dependent_module._mod_name)
281
+ end
282
+
283
+ def first_non_anonymous_superclass(klass)
284
+ while (klass = klass.superclass) && anonymous?(klass); end
285
+ klass
286
+ end
287
+
288
+ # egrep -ohR '@\w*([ck]lass|refl|target|own)\w*' activerecord | sort | uniq
289
+ def update_activerecord_related_references(klass)
290
+ return unless defined?(ActiveRecord)
291
+ return unless klass < ActiveRecord::Base
292
+
293
+ # Reset references held by macro reflections (klass is lazy loaded, so
294
+ # setting its cache to nil will force the name to be resolved again).
295
+ ActiveRecord::Base.descendants.each do |model|
296
+ model.reflections.each_value do |reflection|
297
+ reflection.instance_eval do
298
+ @klass = nil if @klass == klass
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ def anonymous?(mod)
305
+ !(name = mod._mod_name) || name.empty?
306
+ end
307
+
308
+ private
309
+
310
+ def fetch_module_cache
311
+ return(yield(module_cache)) if module_cache.any?
312
+
313
+ ObjectSpace.each_object(Module) { |mod| module_cache << mod unless anonymous?(mod) }
314
+ begin
315
+ yield module_cache
316
+ ensure
317
+ module_cache.clear
318
+ end
319
+ end
320
+
321
+ def prevent_further_removal_of(const_name)
322
+ return if constants_being_removed.include?(const_name)
323
+
324
+ constants_being_removed << const_name
325
+ begin
326
+ yield
327
+ ensure
328
+ constants_being_removed.delete(const_name)
329
+ end
330
+ end
331
+ end
332
+ end