rails-dev-boost 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3c6be77bb649766e7ae7bfe30a7d45f848c62ec3
4
+ data.tar.gz: 380d7b77ab210ea426ee7a4369e46102e317bb5b
5
+ SHA512:
6
+ metadata.gz: e69c5b78346a397e7bc99069e323db29ea92dd3ced9cd2da58d3083e7fb625be6075c3482e85bccece82390bf681cf24e3615dee5b0c2b211a8cfcf86ec650e7
7
+ data.tar.gz: e4d005306b5d46d9f668cc241d8a9dfc735a51447da2f13cf6c6aaad97ab1f19580b997b0db78fe6308bc64af60f8ea80a002305153198d7c5562ae29fc37435
@@ -8,7 +8,7 @@ Alternative to Robert Pankowecki's [`active_reload`](https://github.com/paneq/ac
8
8
 
9
9
  ## Branches
10
10
 
11
- If you are using **Rails 3**: [`rails-dev-boost/master`](http://github.com/thedarkone/rails-dev-boost/tree/master) branch.
11
+ If you are using **Rails 3 and newer**: [`rails-dev-boost/master`](http://github.com/thedarkone/rails-dev-boost/tree/master) branch.
12
12
 
13
13
  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.
14
14
 
@@ -29,7 +29,7 @@ I'm very interested in making the plugin as robust as possible and will work wit
29
29
 
30
30
  There is built-in debug mode in `rails-dev-boost` that can be enabled by putting this line a Rails initializer file:
31
31
 
32
- RailsDevelopmentBoost.debug!
32
+ RailsDevelopmentBoost.debug! if defined?(RailsDevelopmentBoost)
33
33
 
34
34
  After restarting your server `rails-dev-boost` will start to spewing detailed tracing information about its actions into your `development.log` file.
35
35
 
@@ -47,7 +47,7 @@ Usage through `Gemfile`:
47
47
 
48
48
  ```ruby
49
49
  group :development do
50
- gem 'rails-dev-boost', :git => 'git://github.com/thedarkone/rails-dev-boost.git', :require => 'rails_development_boost'
50
+ gem 'rails-dev-boost', :git => 'git://github.com/thedarkone/rails-dev-boost.git'
51
51
  end
52
52
  ```
53
53
 
@@ -279,6 +279,24 @@ class Blog < ActiveRecord::Base
279
279
  end
280
280
  ```
281
281
 
282
+ ## Asynchronous mode
283
+
284
+ By default `rails-dev-boost` now runs in an "async" mode, watching and unloading modified files in a separate thread. This allows for an even faster development mode because there is no longer a need to do a `File.mtime` check of all the `.rb` files at the beginning of the request.
285
+
286
+ To disable the async mode put the following code in a Rails initializer file (these are found in `config/initializers` directory):
287
+ ```ruby
288
+ RailsDevelopmentBoost.async = false
289
+ ```
290
+
291
+ ## `routes.rb` potentially not reloading
292
+
293
+ Since Rails 4.0 `ActiveSupport` now by default reloads `routes.rb` file **if any other auto-loaded `.rb` has changed**. This behavior is different from all previous Rails versions, where `routes.rb` had been reloaded **only if the `routes.rb` file itself had been changed**. This now results in `routes.rb` being reloading on all requests in which any other unrelated `.rb` has been changed, it is in my opinion an unnecessary slowdown, thus `rails-dev-boost` by default reverts Rails to the pre Rails 4.0 behavior.
294
+
295
+ To disable this patch and revert to the default Rails 4.0 behavior - put the following code in a Rails initializer file (these are found in `config/initializers` directory):
296
+ ```ruby
297
+ RailsDevelopmentBoost.reload_routes_on_any_change = true
298
+ ```
299
+
282
300
  ## FAQ
283
301
 
284
302
  ### Q: Since the plugin uses its special "unloading mechanism" won't everything break down?
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -13,6 +13,8 @@ module RailsDevelopmentBoost
13
13
  else
14
14
  ActionDispatch::Callbacks.before(:prepend => true) { ActiveSupport::Dependencies.unload_modified_files! }
15
15
  end
16
+
17
+ DependenciesPatch.enable_async_mode_by_default!
16
18
  end
17
19
  end
18
20
 
@@ -35,6 +37,8 @@ module RailsDevelopmentBoost
35
37
  else
36
38
  ActiveSupport.on_load(:action_controller) { ViewHelpersPatch.apply! }
37
39
  end
40
+
41
+ app.config.middleware.use 'RailsDevelopmentBoost::Async::Middleware'
38
42
  end
39
43
  end
40
44
  end
@@ -55,7 +59,12 @@ module RailsDevelopmentBoost
55
59
  DependenciesPatch.debug!
56
60
  end
57
61
 
58
- def self.async!
59
- DependenciesPatch.async!
62
+ def self.async=(new_value)
63
+ DependenciesPatch.async = new_value
64
+ end
65
+
66
+ class << self
67
+ attr_accessor :reload_routes_on_any_change
60
68
  end
69
+ self.reload_routes_on_any_change = false
61
70
  end
@@ -1,15 +1,29 @@
1
- require 'rb-fsevent'
2
1
  require 'monitor'
3
2
 
4
3
  module RailsDevelopmentBoost
5
4
  module Async
5
+ class Middleware
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ if DependenciesPatch.applied? && DependenciesPatch.async?
12
+ Async.synchronize { @app.call(env) }
13
+ else
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+
6
19
  extend self
20
+ autoload :Reactor, 'rails_development_boost/async/reactor'
7
21
 
8
22
  MONITOR = Monitor.new
9
23
 
10
24
  def heartbeat_check!
11
25
  if @reactor
12
- unless @reactor.alive?
26
+ unless @reactor.alive_and_watching?(ActiveSupport::Dependencies.autoload_paths)
13
27
  @reactor.stop
14
28
  @reactor = nil
15
29
  start!
@@ -25,13 +39,56 @@ module RailsDevelopmentBoost
25
39
  MONITOR.synchronize { yield }
26
40
  end
27
41
 
42
+ def usable?
43
+ Reactor.implementation
44
+ end
45
+
46
+ def process_new_async_value(new_value)
47
+ if new_value
48
+ if !Async.usable?
49
+ msg = 'Unable to start rails-dev-boost in an asynchronous mode. '
50
+ if gem_error = Reactor.gem_load_error
51
+ msg << "Please install the missing gem for even faster rails-dev-boost experience:\n#{gem_error}\n" <<
52
+ "If you can't use the suggest gem version please let me know at https://github.com/thedarkone/rails-dev-boost/issues !\n"
53
+ else
54
+ msg << "Are you running on a OS that is 'async-unsupported' by rails-dev-boost? Please open an issue on github: https://github.com/thedarkone/rails-dev-boost/issues !"
55
+ end
56
+ msg << "\nTo get rid of this message disable the rails-dev-boost's async mode by putting the following code " +
57
+ "in a Rails initializer file (these are found in config/initializers directory):\n" +
58
+ "\n\tRailsDevelopmentBoost.async = false if defined?(RailsDevelopmentBoost)\n\n"
59
+ async_warning(msg)
60
+ new_value = false
61
+ elsif in_console?
62
+ async_warning('Warning: using asynchronous mode in Rails console mode might result in surprising behaviour and is not recommended.')
63
+ end
64
+ end
65
+ new_value
66
+ end
67
+
68
+ def enable_by_default!(user_provided_value = false)
69
+ unless user_provided_value
70
+ DependenciesPatch.async = true unless usable? # trigger the warning message, unless there is a user supplied `async` setting
71
+ DependenciesPatch.async = !in_console?
72
+ end
73
+ end
74
+
28
75
  private
76
+ def in_console?
77
+ defined?(IRB) || defined?(Pry)
78
+ end
79
+
80
+ def async_warning(msg)
81
+ msg = msg.gsub(/^/, '[RAILS-DEV-BOOST] ')
82
+ Kernel.warn(msg)
83
+ Rails.logger.info(msg)
84
+ end
29
85
 
30
86
  def start!
31
- @reactor = Reactor.new
32
- @reactor.watch(ActiveSupport::Dependencies.autoload_paths) {|changed_dirs| unload_affected(changed_dirs)}
33
- @reactor.start!
34
- self.unloaded_something = LoadedFile.unload_modified! # don't miss-out on any of the file changes as the async thread hasn't been started as of yet
87
+ if @reactor = Reactor.get
88
+ @reactor.watch(ActiveSupport::Dependencies.autoload_paths) {|changed_dirs| unload_affected(changed_dirs)}
89
+ @reactor.start!
90
+ self.unloaded_something = LoadedFile.unload_modified! # don't miss-out on any of the file changes as the async thread hasn't been started as of yet
91
+ end
35
92
  end
36
93
 
37
94
  def re_raise_unload_error_if_any
@@ -45,9 +102,7 @@ module RailsDevelopmentBoost
45
102
  changed_dirs = changed_dirs.map {|changed_dir| File.expand_path(changed_dir).chomp(File::SEPARATOR)}
46
103
 
47
104
  synchronize do
48
- self.unloaded_something = LoadedFile::LOADED.each_file_unload_if_changed do |file|
49
- changed_dirs.any? {|changed_dir| file.path.starts_with?(changed_dir)} && file.changed?
50
- end
105
+ self.unloaded_something = LoadedFile::LOADED.unload_modified!(changed_dirs)
51
106
  end
52
107
  rescue Exception => e
53
108
  @unload_error ||= e
@@ -56,18 +111,5 @@ module RailsDevelopmentBoost
56
111
  def unloaded_something=(value)
57
112
  @unloaded_something ||= value
58
113
  end
59
-
60
- class Reactor
61
- delegate :alive?, :to => '@thread'
62
- delegate :watch, :stop, :to => '@watcher'
63
-
64
- def initialize
65
- @watcher = FSEvent.new
66
- end
67
-
68
- def start!
69
- @thread = Thread.new { @watcher.run }
70
- end
71
- end
72
114
  end
73
115
  end
@@ -0,0 +1,195 @@
1
+ require 'thread'
2
+ require 'set'
3
+
4
+ module RailsDevelopmentBoost
5
+ module Async
6
+ # Not using Listen gem directly, because I don't want to be storing/checking .rb files' SHA contents and would like to rely on mtime values exclusively.
7
+ module Reactor
8
+ class MissingNativeGem < StandardError
9
+ def initialize(gem_name, version)
10
+ gem_version_msg = indented_code("gem '#{gem_name}', '#{version}'")
11
+ super("by adding the following to your Gemfile:\n#{gem_version_msg}\n\nThis can go into the same :development group if (you are using one):\n" <<
12
+ indented_code("group :development do\n\tgem 'rails-dev-boost', :github => 'thedarkone/rails-dev-boost'#{gem_version_msg}\nend\n"))
13
+ end
14
+
15
+ private
16
+ def indented_code(msg)
17
+ "\n\t" << msg.gsub("\n", "\n\t")
18
+ end
19
+ end
20
+
21
+ extend self
22
+ attr_reader :gem_load_error
23
+
24
+ def get
25
+ if impl = implementation
26
+ impl.new
27
+ end
28
+ end
29
+
30
+ def implementation
31
+ defined?(@implementation) ? @implementation : (@implementation = find_usable_implementation)
32
+ end
33
+
34
+ def find_usable_implementation
35
+ [Darwin, Linux, Windows].find(&:usable?)
36
+ rescue MissingNativeGem => e
37
+ @gem_load_error ||= e.message
38
+ nil
39
+ end
40
+
41
+ class Base
42
+ def initialize
43
+ @watcher = create_watcher
44
+ @directories = Set.new
45
+ end
46
+
47
+ def watch(directories, &block)
48
+ @directories.merge(directories)
49
+ watch_internal(directories, &block)
50
+ end
51
+
52
+ def start!
53
+ @thread = Thread.new { start_watcher! }
54
+ end
55
+
56
+ def stop
57
+ @watcher.stop
58
+ stop_thread
59
+ end
60
+
61
+ def alive_and_watching?(directories)
62
+ @thread.alive? && directories.all? {|directory| @directories.include?(directory)}
63
+ end
64
+
65
+ private
66
+ def watch_internal(directories, &block)
67
+ @watcher.watch(directories, &block)
68
+ end
69
+
70
+ def stop_thread
71
+ @thread.join if @thread
72
+ end
73
+
74
+ def start_watcher!
75
+ @watcher.run
76
+ end
77
+
78
+ class << self
79
+ def usable?
80
+ gem_check! if platform_match?
81
+ end
82
+
83
+ private
84
+ def platform_match?
85
+ require 'rbconfig'
86
+ RbConfig::CONFIG['target_os'] =~ self::TARGET_OS_REGEX
87
+ end
88
+
89
+ def gem_check!
90
+ defined?(@gem_loaded) ? @gem_loaded : @gem_loaded = load_gem!
91
+ end
92
+
93
+ def load_gem!
94
+ gem(self::GEM_NAME, self::GEM_VERSION)
95
+ require(self::GEM_NAME)
96
+ true
97
+ rescue Gem::LoadError
98
+ raise MissingNativeGem.new(self::GEM_NAME, self::GEM_VERSION)
99
+ end
100
+ end
101
+ end
102
+
103
+ class Darwin < Base
104
+ TARGET_OS_REGEX = /darwin(1.+)?$/i
105
+ GEM_NAME = 'rb-fsevent'
106
+ GEM_VERSION = '>= 0.9.1'
107
+
108
+ private
109
+ def create_watcher
110
+ FSEvent.new
111
+ end
112
+ end
113
+
114
+ # Errors, comments and other gotchas taken from Listen gem (https://github.com/guard/listen)
115
+ class Linux < Base
116
+ TARGET_OS_REGEX = /linux/i
117
+ GEM_NAME = 'rb-inotify'
118
+ GEM_VERSION = '>= 0.8.8'
119
+
120
+ EVENTS = [:recursive, :attrib, :create, :delete, :move, :close_write]
121
+
122
+ # The message to show when the limit of inotify watchers is not enough
123
+ #
124
+ INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
125
+ Listen error: unable to monitor directories for changes.
126
+
127
+ Please head to https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers
128
+ for information on how to solve this issue.
129
+ EOS
130
+
131
+ private
132
+ def watch_internal(directories)
133
+ directories.each do |directory|
134
+ @watcher.watch(directory, *EVENTS) do |event|
135
+ unless root?(event) || file_event_on_a_dir?(event)
136
+ if File.file?(absolute_name = event.absolute_name)
137
+ yield [File.dirname(absolute_name)]
138
+ elsif File.directory?(absolute_name)
139
+ yield [absolute_name]
140
+ end
141
+ end
142
+ end
143
+ end
144
+ rescue Errno::ENOSPC
145
+ abort(INOTIFY_LIMIT_MESSAGE)
146
+ end
147
+
148
+ def root?(event) # Event on root directory
149
+ event.name.empty? # same as event.name == ""
150
+ end
151
+
152
+ # INotify reports changes to files inside directories as events
153
+ # on the directories themselves too.
154
+ #
155
+ # @see http://linux.die.net/man/7/inotify
156
+ def file_event_on_a_dir?(event)
157
+ # event.flags.include?(:isdir) and event.flags & [:close, :modify] != []
158
+ flags = event.flags
159
+ flags.include?(:isdir) && (flags.include?(:close) || flags.include?(:modify))
160
+ end
161
+
162
+ def create_watcher
163
+ INotify::Notifier.new
164
+ end
165
+
166
+ def stop_thread
167
+ Thread.kill(@thread) if @thread
168
+ end
169
+ end
170
+
171
+ class Windows < Base
172
+ TARGET_OS_REGEX = /mswin|mingw|cygwin/i
173
+ GEM_NAME = 'wdm'
174
+ GEM_VERSION = '>= 0.0.3'
175
+
176
+ private
177
+ def watch_internal(directories)
178
+ directories.each do |directory|
179
+ @watcher.watch_recursively(directory) do |change|
180
+ yield [File.dirname(change.path)]
181
+ end
182
+ end
183
+ end
184
+
185
+ def create_watcher
186
+ WDM::Monitor.new
187
+ end
188
+
189
+ def start_watcher!
190
+ @watcher.run!
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -18,13 +18,22 @@ module RailsDevelopmentBoost
18
18
 
19
19
  patch = self
20
20
  ActiveSupport::Dependencies.module_eval do
21
- alias_method :local_const_defined?, :uninherited_const_defined? unless method_defined?(:local_const_defined?) # pre 4da45060 compatibility
21
+ unless method_defined?(:local_const_defined?) # pre 4da45060 compatibility
22
+ if method_defined?(:uninherited_const_defined?)
23
+ alias_method :local_const_defined?, :uninherited_const_defined?
24
+ else # post 4.0 compat
25
+ def local_const_defined?(mod, const_name)
26
+ mod.const_defined?(const_name, false)
27
+ end
28
+ end
29
+ end
22
30
  remove_possible_method :remove_unloadable_constants!
23
31
  remove_possible_method :clear
24
32
  include patch
25
33
  alias_method_chain :load_file, 'constant_tracking'
26
34
  alias_method_chain :remove_constant, 'handling_of_connections'
27
35
  extend patch
36
+ @routes_path_loading = nil
28
37
  end
29
38
 
30
39
  ActiveSupport::Dependencies::Loadable.module_eval do
@@ -45,23 +54,24 @@ module RailsDevelopmentBoost
45
54
  end
46
55
  end
47
56
 
48
- def self.async!
49
- @async = true
57
+ def self.async=(new_value)
58
+ @async = Async.process_new_async_value(new_value)
50
59
  end
51
60
 
52
61
  def self.async?
53
62
  @async
54
63
  end
55
64
 
65
+ def self.enable_async_mode_by_default!
66
+ Async.enable_by_default!(defined?(@async))
67
+ end
68
+
56
69
  def self.applied?
57
70
  ActiveSupport::Dependencies < self
58
71
  end
59
72
 
60
73
  autoload :InstrumentationPatch, 'rails_development_boost/dependencies_patch/instrumentation_patch'
61
74
 
62
- mattr_accessor :constants_being_removed
63
- self.constants_being_removed = []
64
-
65
75
  mattr_accessor :explicit_dependencies
66
76
  self.explicit_dependencies = {}
67
77
 
@@ -91,6 +101,12 @@ module RailsDevelopmentBoost
91
101
  end while const_name.sub!(/::[^:]+\Z/, NOTHING)
92
102
  false
93
103
  end
104
+
105
+ def load_path_to_real_path(path)
106
+ expanded_path = File.expand_path(path)
107
+ expanded_path << '.rb' unless path =~ /\.r(?:b|ake)\Z/
108
+ expanded_path
109
+ end
94
110
  end
95
111
 
96
112
  class ModuleCache
@@ -154,12 +170,50 @@ module RailsDevelopmentBoost
154
170
  end
155
171
  end
156
172
 
173
+ class ConstantsHeap < Array
174
+ NAMESPACE_SEPARATOR = '::'
175
+
176
+ def initialize(*args)
177
+ super
178
+ @seen = Set.new
179
+ end
180
+
181
+ def add_const?(const_name, force_insert = false)
182
+ if @seen.add?(const_name) || force_insert
183
+ (self[const_name.count(NAMESPACE_SEPARATOR)] ||= []) << const_name
184
+ true
185
+ else
186
+ false
187
+ end
188
+ end
189
+
190
+ def pop_next_const
191
+ reverse_each do |slot|
192
+ if const_name = slot.try(:pop)
193
+ return const_name
194
+ end
195
+ end
196
+ nil
197
+ end
198
+
199
+ def seen?(const_name)
200
+ @seen.include?(const_name)
201
+ end
202
+
203
+ def clear_seen
204
+ @seen.clear
205
+ end
206
+ end
207
+
208
+ mattr_accessor :constants_to_remove
209
+ self.constants_to_remove = ConstantsHeap.new
210
+
157
211
  def unload_modified_files!
158
- unloaded_something = unload_modified_files_internal!
159
- load_failure = clear_load_failure
160
- unloaded_something || load_failure
161
- ensure
162
- async_synchronize { @module_cache = nil }
212
+ async_synchronize do
213
+ unloaded_something = unload_modified_files_internal!
214
+ load_failure = clear_load_failure
215
+ unloaded_something || load_failure
216
+ end
163
217
  end
164
218
 
165
219
  def remove_explicitely_unloadable_constants!
@@ -173,7 +227,10 @@ module RailsDevelopmentBoost
173
227
  # Augmented `load_file'.
174
228
  def load_file_with_constant_tracking(path, *args)
175
229
  async_synchronize do
176
- @module_cache = nil # nuking the module_cache helps to avoid any stale-class issues when the async mode is used in a console session
230
+ if @module_cache
231
+ @module_cache = nil
232
+ constants_to_remove.clear_seen
233
+ end
177
234
  load_file_with_constant_tracking_internal(path, args)
178
235
  end
179
236
  end
@@ -187,6 +244,14 @@ module RailsDevelopmentBoost
187
244
  currently_loading.pop
188
245
  end
189
246
 
247
+ def loading_routes_file(path)
248
+ prev_value = @routes_path_loading
249
+ @routes_path_loading = Util.load_path_to_real_path(path)
250
+ yield
251
+ ensure
252
+ @routes_path_loading = prev_value
253
+ end
254
+
190
255
  def associate_constants_to_file(constants, file_path)
191
256
  # freezing strings before using them as Hash keys is slightly more memory efficient
192
257
  constants.map!(&:freeze)
@@ -195,13 +260,32 @@ module RailsDevelopmentBoost
195
260
  LoadedFile.for(file_path).add_constants(constants)
196
261
  end
197
262
 
263
+ def schedule_const_for_unloading(const_name)
264
+ if constants_to_remove.add_const?(const_name)
265
+ if qualified_const_defined?(const_name) && object = const_name.constantize
266
+ @module_cache ||= ModuleCache.new # make sure module_cache has been created
267
+ schedule_dependent_constants_for_removal(const_name, object)
268
+ end
269
+ true
270
+ end
271
+ end
272
+
273
+ def process_consts_scheduled_for_removal
274
+ unless @now_removing_const
275
+ @now_removing_const = true
276
+ begin
277
+ process_consts_scheduled_for_removal_internal
278
+ ensure
279
+ @now_removing_const = nil
280
+ end
281
+ end
282
+ end
283
+
198
284
  # Augmented `remove_constant'.
199
285
  def remove_constant_with_handling_of_connections(const_name)
200
286
  async_synchronize do
201
- module_cache # make sure module_cache has been created
202
- prevent_further_removal_of(const_name) do
203
- unprotected_remove_constant(const_name)
204
- end
287
+ schedule_const_for_unloading(const_name)
288
+ process_consts_scheduled_for_removal
205
289
  end
206
290
  end
207
291
 
@@ -247,6 +331,20 @@ module RailsDevelopmentBoost
247
331
  end
248
332
 
249
333
  private
334
+ def process_consts_scheduled_for_removal_internal
335
+ heap = constants_to_remove
336
+ result = nil
337
+ while const_to_remove = heap.pop_next_const
338
+ begin
339
+ result = unprotected_remove_constant(const_to_remove, qualified_const_defined?(const_to_remove) && const_to_remove.constantize)
340
+ rescue Exception
341
+ heap.add_const?(const_to_remove, true)
342
+ raise
343
+ end
344
+ end
345
+ result
346
+ end
347
+
250
348
  def unload_modified_files_internal!
251
349
  log_call
252
350
  if DependenciesPatch.async?
@@ -270,6 +368,9 @@ module RailsDevelopmentBoost
270
368
 
271
369
  # Associate newly loaded constants to the file just loaded
272
370
  associate_constants_to_file(new_constants, path)
371
+ if routes_path_loading = @routes_path_loading
372
+ RoutesLoadedFile.for(routes_path_loading).associate_with(LoadedFile.for(path))
373
+ end
273
374
  end
274
375
 
275
376
  result
@@ -282,16 +383,17 @@ module RailsDevelopmentBoost
282
383
  yield
283
384
  end
284
385
  end
285
-
286
- def unprotected_remove_constant(const_name)
287
- if qualified_const_defined?(const_name) && object = const_name.constantize
288
- handle_connected_constants(object, const_name)
289
- LoadedFile.unload_files_with_const!(const_name)
290
- if object.kind_of?(Module)
291
- remove_parent_modules_if_autoloaded(object)
292
- remove_child_module_constants(object, const_name)
293
- end
386
+
387
+ def schedule_dependent_constants_for_removal(const_name, object)
388
+ handle_connected_constants(object, const_name)
389
+ LoadedFile.schedule_for_unloading_files_with_const!(const_name)
390
+ if object.kind_of?(Module)
391
+ remove_parent_modules_if_autoloaded(object)
392
+ remove_child_module_constants(object, const_name)
294
393
  end
394
+ end
395
+
396
+ def unprotected_remove_constant(const_name, object)
295
397
  result = remove_constant_without_handling_of_connections(const_name)
296
398
  clear_tracks_of_removed_const(const_name, object)
297
399
  result
@@ -299,17 +401,19 @@ module RailsDevelopmentBoost
299
401
 
300
402
  def error_loading_file(file_path, e)
301
403
  LoadedFile.for(file_path).stale! if LoadedFile.loaded?(file_path)
302
- @load_failure = true
404
+ # only the errors that blow through the full stack are load failures, this lets user code handle failed load failures by rescuing raised exceptions without triggering a full dependecies reload
405
+ @load_failure = true if currently_loading.size == 1
303
406
  raise e
304
407
  end
305
408
 
306
409
  def handle_connected_constants(object, const_name)
307
410
  return unless Module === object && qualified_const_defined?(const_name)
411
+ remove_nested_constants(const_name)
308
412
  remove_explicit_dependencies_of(const_name)
309
413
  remove_dependent_modules(object)
414
+ # TODO move these into the cleanup phase
310
415
  update_activerecord_related_references(object)
311
416
  update_mongoid_related_references(object)
312
- remove_nested_constants(const_name)
313
417
  end
314
418
 
315
419
  def remove_nested_constants(const_name)
@@ -317,7 +421,7 @@ module RailsDevelopmentBoost
317
421
  end
318
422
 
319
423
  def remove_nested_constant(parent_const, child_const)
320
- remove_constant(child_const)
424
+ schedule_const_for_unloading(child_const)
321
425
  end
322
426
 
323
427
  # AS::Dependencies doesn't track same-file nested constants, so we need to look out for them on our own.
@@ -339,7 +443,7 @@ module RailsDevelopmentBoost
339
443
  end
340
444
 
341
445
  def remove_autoloaded_parent_module(initial_object, parent_object)
342
- remove_constant(parent_object._mod_name)
446
+ schedule_const_for_unloading(parent_object._mod_name)
343
447
  end
344
448
 
345
449
  def autoloaded_object?(object) # faster than going through Dependencies.autoloaded?
@@ -373,7 +477,7 @@ module RailsDevelopmentBoost
373
477
  end
374
478
 
375
479
  def remove_child_module_constant(parent_object, full_child_const_name)
376
- remove_constant(full_child_const_name)
480
+ schedule_const_for_unloading(full_child_const_name)
377
481
  end
378
482
 
379
483
  def remove_explicit_dependencies_of(const_name)
@@ -385,21 +489,23 @@ module RailsDevelopmentBoost
385
489
  end
386
490
 
387
491
  def remove_explicit_dependency(const_name, depending_const)
388
- remove_constant(depending_const)
492
+ schedule_const_for_unloading(depending_const)
389
493
  end
390
494
 
391
495
  def clear_tracks_of_removed_const(const_name, object = nil)
392
496
  autoloaded_constants.delete(const_name)
393
- @module_cache.remove_const(const_name, object)
497
+ # @module_cache might be nil if remove_constant has been called with a non-existent constant, ie: it hasn't been checked with `qualified_const_defined?`. Because AS::Dep doesn't blow, neither
498
+ # should we.
499
+ @module_cache.remove_const(const_name, object) if @module_cache
394
500
  LoadedFile.const_unloaded(const_name)
395
501
  end
396
502
 
397
503
  def remove_dependent_modules(mod)
398
- module_cache.each_dependent_on(mod) {|other| remove_dependent_constant(mod, other)}
504
+ @module_cache.each_dependent_on(mod) {|other| remove_dependent_constant(mod, other)}
399
505
  end
400
506
 
401
507
  def remove_dependent_constant(original_module, dependent_module)
402
- remove_constant(dependent_module._mod_name)
508
+ schedule_const_for_unloading(dependent_module._mod_name)
403
509
  end
404
510
 
405
511
  AR_REFLECTION_CACHES = [:@klass]
@@ -419,10 +525,10 @@ module RailsDevelopmentBoost
419
525
  def update_mongoid_related_references(klass)
420
526
  if defined?(Mongoid::Document) && klass < Mongoid::Document
421
527
  while (superclass = Util.first_non_anonymous_superclass(superclass || klass)) != Object && superclass < Mongoid::Document
422
- remove_constant(superclass._mod_name) # this is necessary to nuke the @_types caches
528
+ schedule_const_for_unloading(superclass._mod_name) # this is necessary to nuke the @_types caches
423
529
  end
424
530
 
425
- module_cache.each_dependent_on(Mongoid::Document) do |model|
531
+ @module_cache.each_dependent_on(Mongoid::Document) do |model|
426
532
  clean_up_relation_caches(model.relations, klass, MONGOID_RELATION_CACHES)
427
533
  end
428
534
  end
@@ -435,20 +541,5 @@ module RailsDevelopmentBoost
435
541
  end
436
542
  end
437
543
  end
438
-
439
- def module_cache
440
- @module_cache ||= ModuleCache.new
441
- end
442
-
443
- def prevent_further_removal_of(const_name)
444
- return if constants_being_removed.include?(const_name)
445
-
446
- constants_being_removed << const_name
447
- begin
448
- yield
449
- ensure
450
- constants_being_removed.delete(const_name)
451
- end
452
- end
453
544
  end
454
545
  end
@@ -2,7 +2,7 @@ module RailsDevelopmentBoost
2
2
  module DependenciesPatch
3
3
  module InstrumentationPatch
4
4
  module Instrumenter
5
- delegate :boost_log, :to => 'ActiveSupport::Dependencies'
5
+ delegate :boost_log, :boost_log_nested, :boost_log_schedule_const_removal, :to => 'ActiveSupport::Dependencies'
6
6
 
7
7
  def self.included(mod)
8
8
  mod.extend(ClassMethods)
@@ -38,9 +38,14 @@ module RailsDevelopmentBoost
38
38
  add_constants_without_instrumentation(new_constants)
39
39
  end
40
40
 
41
- def unload_dependent_file_with_instrumentation(dependent_file)
42
- boost_log('UNLOAD_DEPENDENT', "#{boost_inspect}: #{dependent_file.boost_inspect}")
43
- unload_dependent_file_without_instrumentation(dependent_file)
41
+ def dependent_file_schedule_for_unloading_with_instrumentation!(dependent_file)
42
+ boost_log('SCHEDULE_DEPENDENT', "#{boost_inspect}: #{dependent_file.boost_inspect}")
43
+ dependent_file_schedule_for_unloading_without_instrumentation!(dependent_file)
44
+ end
45
+
46
+ def schedule_const_for_unloading_with_instrumentation(const_name)
47
+ boost_log_schedule_const_removal('SCHEDULE_REMOVAL', const_name, const_name)
48
+ schedule_const_for_unloading_without_instrumentation(const_name)
44
49
  end
45
50
 
46
51
  private
@@ -69,9 +74,9 @@ module RailsDevelopmentBoost
69
74
  end
70
75
  end
71
76
 
72
- def unload_containing_file_with_instrumentation(const_name, file)
73
- boost_log('UNLOAD_CONTAINING_FILE', "#{const_name} -> #{file.boost_inspect}")
74
- unload_containing_file_without_instrumentation(const_name, file)
77
+ def schedule_containing_file_with_instrumentation(const_name, file)
78
+ boost_log('SCHEDULE_CONTAINING_FILE', "#{const_name} -> #{file.boost_inspect}")
79
+ schedule_containing_file_without_instrumentation(const_name, file)
75
80
  end
76
81
  end
77
82
  end
@@ -79,14 +84,14 @@ module RailsDevelopmentBoost
79
84
  module Files
80
85
  include Instrumenter
81
86
 
82
- def unload_modified_file_with_instrumentation(file)
87
+ def schedule_modified_file_with_instrumentation(file)
83
88
  boost_log('CHANGED', "#{file.boost_inspect}")
84
- unload_modified_file_without_instrumentation(file)
89
+ boost_log_nested { schedule_modified_file_without_instrumentation(file) }
85
90
  end
86
91
 
87
- def unload_decorator_file_with_instrumentation(file)
88
- boost_log('UNLOAD_DECORATOR_FILE', "#{file.boost_inspect}")
89
- unload_decorator_file_without_instrumentation(file)
92
+ def schedule_decorator_file_with_instrumentation(file)
93
+ boost_log('SCHEDULE_DECORATOR_FILE', "#{file.boost_inspect}")
94
+ schedule_decorator_file_without_instrumentation(file)
90
95
  end
91
96
  end
92
97
 
@@ -123,15 +128,23 @@ module RailsDevelopmentBoost
123
128
  raw_boost_log("#{ "[#{action}] " if action}#{msg}")
124
129
  end
125
130
 
126
- private
127
- def unprotected_remove_constant(const_name)
128
- boost_log('REMOVING', const_name)
131
+ def boost_log_schedule_const_removal(action, msg, const_name)
132
+ action = "#{action} | #{ActiveSupport::Dependencies.constants_to_remove.seen?(const_name) ? 'SKIP' : 'SCHEDULED'}"
133
+ boost_log(action, msg)
134
+ end
135
+
136
+ def boost_log_nested
129
137
  @removal_nesting = (@removal_nesting || 0) + 1
130
- super
138
+ yield
131
139
  ensure
132
140
  @removal_nesting -= 1
133
141
  end
134
142
 
143
+ private
144
+ def schedule_dependent_constants_for_removal(const_name, object)
145
+ boost_log_nested { super }
146
+ end
147
+
135
148
  def error_loading_file(file_path, e)
136
149
  description = RailsDevelopmentBoost::LoadedFile.loaded?(file_path) ? RailsDevelopmentBoost::LoadedFile.for(file_path).boost_inspect : file_path
137
150
  boost_log('ERROR_WHILE_LOADING', "#{description}: #{e.inspect}")
@@ -139,32 +152,34 @@ module RailsDevelopmentBoost
139
152
  end
140
153
 
141
154
  def remove_explicit_dependency(const_name, depending_const)
142
- boost_log('EXPLICIT_DEPENDENCY', "#{const_name} -> #{depending_const}")
155
+ boost_log_schedule_const_removal('EXPLICIT_DEPENDENCY', "#{const_name} -> #{depending_const}", depending_const)
143
156
  super
144
157
  end
145
158
 
146
159
  def remove_dependent_constant(original_module, dependent_module)
147
- boost_log('DEPENDENT_MODULE', "#{original_module._mod_name} -> #{dependent_module._mod_name}")
160
+ const_name = dependent_module._mod_name
161
+ boost_log_schedule_const_removal('DEPENDENT_MODULE', "#{original_module._mod_name} -> #{const_name}", const_name)
148
162
  super
149
163
  end
150
164
 
151
165
  def remove_autoloaded_parent_module(initial_object, parent_object)
152
- boost_log('REMOVE_PARENT', "#{initial_object._mod_name} -> #{parent_object._mod_name}")
166
+ const_name = parent_object._mod_name
167
+ boost_log_schedule_const_removal('REMOVE_PARENT', "#{initial_object._mod_name} -> #{const_name}", const_name)
153
168
  super
154
169
  end
155
170
 
156
171
  def remove_child_module_constant(parent_object, full_child_const_name)
157
- boost_log('REMOVE_CHILD', "#{parent_object._mod_name} -> #{full_child_const_name}")
172
+ boost_log_schedule_const_removal('REMOVE_CHILD', "#{parent_object._mod_name} -> #{full_child_const_name}", full_child_const_name)
158
173
  super
159
174
  end
160
175
 
161
176
  def remove_nested_constant(parent_const, child_const)
162
- boost_log('REMOVE_NESTED', "#{parent_const} :: #{child_const.sub(/\A#{parent_const}::/, '')}")
177
+ boost_log_schedule_const_removal('REMOVE_NESTED', "#{parent_const} :: #{child_const.sub(/\A#{parent_const}::/, '')}", child_const)
163
178
  super
164
179
  end
165
180
 
166
181
  def raw_boost_log(msg)
167
- Rails.logger.info("[DEV-BOOST] #{"\t" * (@removal_nesting || 0)}#{msg}")
182
+ Rails.logger.info("[DEV-BOOST] #{" " * (@removal_nesting || 0)}#{msg}")
168
183
  end
169
184
  end
170
185
  end
@@ -3,8 +3,16 @@ require 'active_support/descendants_tracker'
3
3
  module RailsDevelopmentBoost
4
4
  module DescendantsTrackerPatch
5
5
  def self.apply!
6
+ # removing the .clear method across all Rails/Ruby versions
7
+ begin
8
+ ActiveSupport::DescendantsTracker.send(:remove_method, :clear)
9
+ rescue NameError
10
+ end
11
+ begin
12
+ ActiveSupport::DescendantsTracker.singleton_class.send(:remove_method, :clear)
13
+ rescue NameError
14
+ end
6
15
  ActiveSupport::DescendantsTracker.extend self
7
- ActiveSupport::DescendantsTracker.singleton_class.remove_possible_method :clear
8
16
  end
9
17
 
10
18
  def delete(klass)
@@ -5,11 +5,10 @@ module RailsDevelopmentBoost
5
5
  end
6
6
 
7
7
  def load(file, wrap = false)
8
- expanded_path = File.expand_path(file)
8
+ real_path = DependenciesPatch::Util.load_path_to_real_path(file)
9
9
  # force the manual #load calls for autoloadable files to go through the AS::Dep stack
10
- if ActiveSupport::Dependencies.in_autoload_path?(expanded_path)
11
- expanded_path << '.rb' unless expanded_path =~ /\.(rb|rake)\Z/
12
- ActiveSupport::Dependencies.load_file_from_explicit_load(expanded_path)
10
+ if ActiveSupport::Dependencies.in_autoload_path?(real_path)
11
+ ActiveSupport::Dependencies.load_file_from_explicit_load(real_path)
13
12
  else
14
13
  super
15
14
  end
@@ -1,40 +1,69 @@
1
+ require 'set'
2
+
1
3
  module RailsDevelopmentBoost
2
4
  class LoadedFile
3
5
  class Files < Hash
4
6
  def initialize(*args)
7
+ @directories = {}
5
8
  super {|hash, file_path| hash[file_path] = LoadedFile.new(file_path)}
6
9
  end
7
10
 
8
- def unload_modified!
9
- each_file_unload_if_changed {|file| file.changed?}
11
+ def []=(file_path, loaded_file)
12
+ (@directories[loaded_file.dirname] ||= Set.new) << loaded_file
13
+ super
14
+ end
15
+
16
+ def delete(file_path)
17
+ if loaded_file = super
18
+ dirname = loaded_file.dirname
19
+ if @directories[dirname].delete(loaded_file).empty?
20
+ @directories.delete(dirname)
21
+ end
22
+ end
23
+ loaded_file
10
24
  end
11
25
 
12
- def each_file_unload_if_changed
26
+ def unload_modified!(filter_directories = nil)
13
27
  unloaded_something = false
14
- values.each do |file|
15
- if yield(file)
16
- unload_modified_file(file)
28
+ find_files_in(filter_directories).each do |file|
29
+ if file.changed?
30
+ schedule_modified_file(file)
17
31
  unloaded_something = true
18
32
  end
19
33
  end
20
34
  if unloaded_something
21
35
  values.each do |file|
22
- unload_decorator_file(file) if file.decorator_like?
36
+ schedule_decorator_file(file) if file.decorator_like?
23
37
  end
24
38
  end
39
+ ActiveSupport::Dependencies.process_consts_scheduled_for_removal
25
40
  unloaded_something
26
41
  end
27
42
 
28
- def unload_modified_file(file)
29
- file.unload!
43
+ def find_files_in(filter_directories = nil)
44
+ if filter_directories
45
+ arr = []
46
+ @directories.each_pair do |dirname, files|
47
+ arr.concat(files.to_a) if filter_directories.any? {|filter_directory| dirname.starts_with?(filter_directory)}
48
+ end
49
+ arr
50
+ else
51
+ values
52
+ end
30
53
  end
31
54
 
32
- def unload_decorator_file(file)
33
- file.unload!
55
+ def schedule_modified_file(file)
56
+ file.schedule_consts_for_unloading!
57
+ end
58
+
59
+ def schedule_decorator_file(file)
60
+ file.schedule_consts_for_unloading!
34
61
  end
35
62
 
36
63
  def constants
37
- values.map(&:constants).flatten
64
+ arr = []
65
+ each_value {|file| arr.concat(file.constants)}
66
+ arr
38
67
  end
39
68
 
40
69
  def stored?(file)
@@ -110,16 +139,20 @@ module RailsDevelopmentBoost
110
139
  @constants |= new_constants
111
140
  end
112
141
 
142
+ def dirname
143
+ File.dirname(@path)
144
+ end
145
+
113
146
  # "decorator" files are popular with certain Rails frameworks (spree/refinerycms etc.) they don't define their own constants, instead
114
147
  # they are usually used for adding methods to other classes via Model.class_eval { def meth; end }
115
148
  def decorator_like?
116
149
  @constants.empty? && !INTERDEPENDENCIES[self]
117
150
  end
118
151
 
119
- def unload!
152
+ def schedule_consts_for_unloading!
120
153
  guard_double_unloading do
121
- INTERDEPENDENCIES.each_dependent_on(self) {|dependent_file| unload_dependent_file(dependent_file)}
122
- @constants.dup.each {|const| ActiveSupport::Dependencies.remove_constant(const)}
154
+ INTERDEPENDENCIES.each_dependent_on(self) {|dependent_file| dependent_file_schedule_for_unloading!(dependent_file)}
155
+ @constants.dup.each {|const| schedule_const_for_unloading(const)}
123
156
  clean_up_if_necessary
124
157
  end
125
158
  end
@@ -166,8 +199,8 @@ module RailsDevelopmentBoost
166
199
  @path.eql?(other)
167
200
  end
168
201
 
169
- def unload_dependent_file(dependent_file)
170
- dependent_file.unload!
202
+ def dependent_file_schedule_for_unloading!(dependent_file)
203
+ dependent_file.schedule_consts_for_unloading!
171
204
  end
172
205
 
173
206
  def guard_double_unloading
@@ -206,6 +239,10 @@ module RailsDevelopmentBoost
206
239
  INTERDEPENDENCIES.each_dependent_on(self, &:stale!)
207
240
  end
208
241
 
242
+ def schedule_const_for_unloading(const_name)
243
+ ActiveSupport::Dependencies.schedule_const_for_unloading(const_name)
244
+ end
245
+
209
246
  class << self
210
247
  def unload_modified!
211
248
  LOADED.unload_modified!
@@ -227,12 +264,12 @@ module RailsDevelopmentBoost
227
264
  CONSTANTS_TO_FILES[const_name]
228
265
  end
229
266
 
230
- def unload_files_with_const!(const_name)
231
- CONSTANTS_TO_FILES.each_file_with_const(const_name) {|file| unload_containing_file(const_name, file)}
267
+ def schedule_for_unloading_files_with_const!(const_name)
268
+ CONSTANTS_TO_FILES.each_file_with_const(const_name) {|file| schedule_containing_file(const_name, file)}
232
269
  end
233
270
 
234
- def unload_containing_file(const_name, file)
235
- file.unload!
271
+ def schedule_containing_file(const_name, file)
272
+ file.schedule_consts_for_unloading!
236
273
  end
237
274
 
238
275
  def const_unloaded(const_name)
@@ -250,4 +287,28 @@ module RailsDevelopmentBoost
250
287
  File.mtime(@path).to_i rescue nil
251
288
  end
252
289
  end
290
+
291
+ class RoutesLoadedFile < LoadedFile
292
+ def decorator_like?
293
+ false
294
+ end
295
+
296
+ def changed?
297
+ false
298
+ end
299
+
300
+ def schedule_consts_for_unloading!
301
+ Reloader.routes_reloader.force_execute!
302
+ end
303
+
304
+ def add_constants(new_constants)
305
+ end
306
+
307
+ def clean_up_if_necessary
308
+ end
309
+
310
+ def self.for(file_path)
311
+ LOADED.loaded?(file_path) ? LOADED[file_path] : LOADED[file_path] = new(file_path)
312
+ end
313
+ end
253
314
  end
@@ -1,10 +1,41 @@
1
1
  module RailsDevelopmentBoost
2
2
  module Reloader # replacement for the Rails' post fa1d9a file_update_checker
3
+ module RoutesReloaderPatch
4
+ def force_execute!
5
+ @force_execute = true
6
+ end
7
+
8
+ def updated?
9
+ @force_execute = true if result = super
10
+ result
11
+ end
12
+
13
+ def execute
14
+ if RailsDevelopmentBoost.reload_routes_on_any_change || @in_execute_if_updated || @force_execute || updated?
15
+ @force_execute = false
16
+ super
17
+ end
18
+ end
19
+
20
+ def load(file, wrap = false)
21
+ ActiveSupport::Dependencies.loading_routes_file(file) { super }
22
+ end
23
+
24
+ def execute_if_updated
25
+ old_in_execute_if_updated = @in_execute_if_updated
26
+ @in_execute_if_updated = true
27
+ super
28
+ ensure
29
+ @in_execute_if_updated = old_in_execute_if_updated
30
+ end
31
+ end
32
+
3
33
  extend self
4
34
 
5
35
  def hook_in!
6
36
  Rails.application.reloaders.unshift(self)
7
37
  ActionDispatch::Reloader.to_prepare(:prepend => true) { RailsDevelopmentBoost::Reloader.execute_if_updated }
38
+ patch_routes_reloader! if Rails::VERSION::MAJOR >= 4
8
39
  end
9
40
 
10
41
  def execute
@@ -20,7 +51,19 @@ module RailsDevelopmentBoost
20
51
 
21
52
  alias_method :updated?, :execute
22
53
 
54
+ def routes_reloader
55
+ Rails.application.respond_to?(:routes_reloader) && Rails.application.routes_reloader
56
+ end
57
+
23
58
  private
59
+ # Rails 4.0+ calls routes_reloader.execute instead of routes_reloader.execute_if_updated because an autoloaded Rails::Engine might be mounted via routes.rb, therefore if any constants
60
+ # are unloaded this triggers the reloading of routes.rb, we would like to avoid that.
61
+ def patch_routes_reloader!
62
+ if reloader = routes_reloader
63
+ reloader.extend(RoutesReloaderPatch)
64
+ end
65
+ end
66
+
24
67
  def init
25
68
  Rails.application.reloaders.delete_if do |reloader|
26
69
  if rails_file_checker?(reloader)
@@ -43,7 +86,7 @@ module RailsDevelopmentBoost
43
86
  if (dir_glob = reloader.instance_variable_get(:@glob)).kind_of?(String)
44
87
  autoload_paths = ActiveSupport::Dependencies.autoload_paths
45
88
  dir_glob.sub(/\A\{/, '').sub(/\}\Z/, '').split(',').all? do |glob_path|
46
- autoload_paths.any? {|autoload_path| glob_path.starts_with?(autoload_path)}
89
+ autoload_paths.any? {|autoload_path| glob_path.starts_with?(autoload_path.to_s)}
47
90
  end
48
91
  end
49
92
  end
metadata CHANGED
@@ -1,50 +1,40 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rails-dev-boost
3
- version: !ruby/object:Gem::Version
4
- hash: 21
5
- prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
11
5
  platform: ruby
12
- authors:
6
+ authors:
13
7
  - Roman Le Negrate
14
8
  - thedarkone
15
9
  autorequire:
16
10
  bindir: bin
17
11
  cert_chain: []
18
-
19
- date: 2012-10-02 00:00:00 +02:00
20
- default_executable:
21
- dependencies:
22
- - !ruby/object:Gem::Dependency
23
- name: rails
24
- prerelease: false
25
- requirement: &id001 !ruby/object:Gem::Requirement
26
- none: false
27
- requirements:
28
- - - ">="
29
- - !ruby/object:Gem::Version
30
- hash: 7
31
- segments:
32
- - 3
33
- - 0
34
- version: "3.0"
12
+ date: 2014-04-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: railties
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '3.0'
35
21
  type: :runtime
36
- version_requirements: *id001
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '3.0'
37
28
  description: Make your Rails app 10 times faster in development mode
38
29
  email: roman.lenegrate@gmail.com
39
30
  executables: []
40
-
41
31
  extensions: []
42
-
43
- extra_rdoc_files:
32
+ extra_rdoc_files:
44
33
  - LICENSE
45
34
  - README.markdown
46
- files:
35
+ files:
47
36
  - lib/rails-dev-boost.rb
37
+ - lib/rails_development_boost/async/reactor.rb
48
38
  - lib/rails_development_boost/async.rb
49
39
  - lib/rails_development_boost/dependencies_patch/instrumentation_patch.rb
50
40
  - lib/rails_development_boost/dependencies_patch.rb
@@ -92,41 +82,31 @@ files:
92
82
  - LICENSE
93
83
  - README.markdown
94
84
  - VERSION
95
- has_rdoc: true
96
85
  homepage: http://github.com/thedarkone/rails-dev-boost
97
86
  licenses: []
98
-
87
+ metadata: {}
99
88
  post_install_message:
100
- rdoc_options:
89
+ rdoc_options:
101
90
  - --charset=UTF-8
102
- require_paths:
91
+ require_paths:
103
92
  - lib
104
- required_ruby_version: !ruby/object:Gem::Requirement
105
- none: false
106
- requirements:
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- hash: 3
110
- segments:
111
- - 0
112
- version: "0"
113
- required_rubygems_version: !ruby/object:Gem::Requirement
114
- none: false
115
- requirements:
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- hash: 3
119
- segments:
120
- - 0
121
- version: "0"
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
122
103
  requirements: []
123
-
124
104
  rubyforge_project:
125
- rubygems_version: 1.5.2
105
+ rubygems_version: 2.0.14
126
106
  signing_key:
127
107
  specification_version: 3
128
108
  summary: Speeds up Rails development mode
129
- test_files:
109
+ test_files:
130
110
  - test/constants/active_record/comment.rb
131
111
  - test/constants/active_record/message.rb
132
112
  - test/constants/active_record/other.rb