rack-unreloader 1.8.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6fefe4d57aa2ea927929699e2a6aba2e8979cea284b0784fee379404b6f1726
4
- data.tar.gz: 3aa21b88447194616e29d1ea2cf1181f102f5d58e6a29f4a11696a83d826a506
3
+ metadata.gz: 01a3ddc58ad8308cd61dd10991e7478f546bd7d1bc2fc4bb88203b4a37d7b335
4
+ data.tar.gz: ceff468f0b2755347859e3d2a408087379a056975e00c73fe35adc5854c3cbc0
5
5
  SHA512:
6
- metadata.gz: 6a5fbca1692b66ab4daf1f9b1677b15fb704d28bb972b26fdc15d78918fa37bed04657177438ff2351ba8c60a10105f7e138d869a4ab37fbd9e4143188c5685d
7
- data.tar.gz: 12d9b2d24730e1f41da6089e5995c219af4b636755738405d1404e0be3f4044c8f935bd073ba53d92cada170761f79bfea82235c5b58d9ca6bc1dac1ee1b647f
6
+ metadata.gz: 71c0dc1e1ebfcad459d8bf6d1533200ad071f857fe2b53fb7f26af1e079169eca3835b2b1eee4a39d572244bf3208bd51718120f7e89f4dcd5b646cbed6195ca
7
+ data.tar.gz: d9e9ab98a9b36a44d0d819f0142a1c9c49a21598a390da18c7cee48199cd365a0acac7c4fd95955e49b3f18a7ef8e0fe75bfa138312e3b2d16f57ebc7574731f
data/CHANGELOG CHANGED
@@ -1,3 +1,17 @@
1
+ = 2.1.0 (2023-01-18)
2
+
3
+ * Add reload? and autoload? methods for determining what mode the unreloader is operating in (jeremyevans)
4
+
5
+ * Support :autoload option and autoload method for autoloading files and directories (jeremyevans)
6
+
7
+ = 2.0.0 (2022-06-23)
8
+
9
+ * Fix TypeError being raised when requiring a file results in an error (jeremyevans)
10
+
11
+ * Drop Unreloader#strip_path_prefix (jeremyevans)
12
+
13
+ * Drop Ruby 1.8 support (jeremyevans)
14
+
1
15
  = 1.8.0 (2021-10-15)
2
16
 
3
17
  * Avoid warnings in verbose warning mode on Ruby 3+ (jeremyevans)
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2021 Jeremy Evans
1
+ Copyright (c) 2014-2023 Jeremy Evans
2
2
  Copyright (c) 2011 Padrino
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining
data/README.rdoc CHANGED
@@ -195,7 +195,7 @@ class.
195
195
 
196
196
  == Requiring
197
197
 
198
- Rack::Unreloader#require is a little different than require in that it takes
198
+ Rack::Unreloader#require is a little different than Kernel#require in that it takes
199
199
  a file glob, not a normal require path. For that reason, you must specify
200
200
  the extension when requiring the file, and it will only look in the current
201
201
  directory by default:
@@ -253,17 +253,44 @@ decide that instead of specifying the constants, ObjectSpace should be used to
253
253
  automatically determine the constants loaded. You can specify this by having the
254
254
  block return the :ObjectSpace symbol.
255
255
 
256
- == chroot Support
256
+ === Autoload
257
257
 
258
- +Rack::Unreloader#strip_path_prefix+ exists for supporting reloading in
259
- chroot environments, where you chroot an application after it has been fully
260
- loaded, but still want to pick up changes to files inside the chroot. Example:
258
+ To further speed things up in development mode, or when only running a subset of
259
+ tests, it can be helpful to autoload files instead of require them, so that if
260
+ the related constants are not accessed, you don't need to pay the cost of loading
261
+ the related files. To enable autoloading, pass the +:autoload+ option when
262
+ creating the reloader:
261
263
 
262
- Unreloader.strip_path_prefix(Dir.pwd)
263
- Dir.chroot(Dir.pwd)
264
+ Unreloader = Rack::Unreloader.new(autoload: true){App}
264
265
 
265
- Note that Unreloader.strip_path_prefix also strips the path prefix from
266
- $LOADED_FEATURES, as that is necessary for correct operation.
266
+ Then, you can call +autoload+ instead of +require+:
267
+
268
+ Unreloader.autoload('models'){|f| File.basename(f).sub(/\.rb\z/, '').capitalize}
269
+
270
+ This will monitor the models directory for files, setting up autoloads for each
271
+ file. After the file has been loaded, normal reloading will happen for the
272
+ file. Note that for +autoload+, a block is required because the constant names
273
+ are needed before loading the file to setup the autoload.
274
+
275
+ If the <tt>reload: false</tt> option is given when creating the reloader,
276
+ autoloads will still be setup by +autoload+, but no reloading will happen. This
277
+ can be useful when testing subsets of an application. When testing subsets of
278
+ an application, you don't need reloading, but you can benefit from autoloading,
279
+ so parts of the application you are not testing are not loaded.
280
+
281
+ If you do not pass the +:autoload+ option when creating the reloader, then calls
282
+ to +autoload+ will implicitly be transformed to calls to +require+. This makes
283
+ it possible to use the same +autoload+ call in all cases, and handle four
284
+ separate scenarios:
285
+
286
+ 1. Autoload then reload: Fast development mode startup, loading the minimum
287
+ number of files, but reloading if those files are changed
288
+ 2. Autoload without reload: Useful for faster testing of a subset of an
289
+ application, so the untested subsets is not loaded.
290
+ 3. Require then reload: Slower development mode startup, but have entire
291
+ application loaded before accepting requests
292
+ 4. Require without reload: Normal production/testing mode with nothing autoloaded
293
+ or reloaded
267
294
 
268
295
  == Usage Outside Rack
269
296
 
@@ -296,11 +323,9 @@ environment anytime there are any changes) are going to be more robust than
296
323
  this approach, but probably slower. Be aware that you are trading robustness
297
324
  for speed when using this library.
298
325
 
299
- == Implementation Support
326
+ == Ruby Version Support
300
327
 
301
- Rack::Unreloader works correctly on Ruby 1.8.7+, JRuby 9.1+, and Rubinius. It
302
- also works on older versions of JRuby if you use a proc to specify the constants
303
- to unload.
328
+ Rack::Unreloader works correctly on Ruby 1.9.2+ and JRuby 9.1+.
304
329
 
305
330
  == License
306
331
 
@@ -0,0 +1,89 @@
1
+ require_relative 'reloader'
2
+
3
+ module Rack
4
+ class Unreloader
5
+ class AutoloadReloader < Reloader
6
+ def initialize(opts={})
7
+ super
8
+
9
+ # Files that autoloads have been setup for, but have not yet been loaded.
10
+ # Hash with realpath keys and values that are arrays with the
11
+ # a block that will return constant name strings that will autoload the
12
+ # file, the modified time the file, and the delete hook.
13
+ @autoload_files = {}
14
+
15
+ # Directories where new files will be setup for autoloading.
16
+ # Uses same format as @monitor_dirs.
17
+ @autoload_dirs = {}
18
+ end
19
+
20
+ def autoload_dependencies(paths, opts={}, &block)
21
+ delete_hook = opts[:delete_hook]
22
+
23
+ Unreloader.expand_paths(paths).each do |file|
24
+ if File.directory?(file)
25
+ @autoload_dirs[file] = [nil, [], block, delete_hook]
26
+ check_autoload_dir(file)
27
+ else
28
+ # Comparisons against $LOADED_FEATURES need realpaths
29
+ @autoload_files[File.realpath(file)] = [block, modified_at(file), delete_hook]
30
+ Unreloader.autoload_constants(yield(file), file, @logger)
31
+ end
32
+ end
33
+ end
34
+
35
+ def remove_autoload(file, strings)
36
+ strings = Array(strings)
37
+ log("Removing autoload for #{file}: #{strings.join(" ")}") unless strings.empty?
38
+ strings.each do |s|
39
+ obj, mod = Unreloader.split_autoload(s)
40
+ # Assume that if the autoload string was valid to create the
41
+ # autoload, it is still valid when removing the autoload.
42
+ obj.send(:remove_const, mod)
43
+ end
44
+ end
45
+
46
+ def check_autoload_dir(dir)
47
+ subdir_times, files, block, delete_hook = md = @autoload_dirs[dir]
48
+ return if subdir_times && subdir_times.all?{|subdir, time| File.directory?(subdir) && modified_at(subdir) == time}
49
+ md[0] = subdir_times(dir)
50
+
51
+ cur_files = Unreloader.ruby_files(dir)
52
+ return if files == cur_files
53
+
54
+ removed_files = files - cur_files
55
+ new_files = cur_files - files
56
+
57
+ # Removed files that were never required should have the constant removed
58
+ # so that accesses to the constant do not attempt to autoload a file that
59
+ # no longer exists.
60
+ removed_files.each do |file|
61
+ remove_autoload(file, block.call(file)) unless @monitor_files[file]
62
+ end
63
+
64
+ # New files not yet loaded should have autoloads added for them.
65
+ autoload_dependencies(new_files, :delete_hook=>delete_hook, &block) unless new_files.empty?
66
+
67
+ files.replace(cur_files)
68
+ end
69
+
70
+ def reload!
71
+ (@autoload_files.keys & $LOADED_FEATURES).each do |file|
72
+ # Files setup for autoloads were autoloaded, move metadata to locations
73
+ # used for required files.
74
+ log("Autoloaded file required, setting up reloading: #{file}")
75
+ block, *metadata = @autoload_files.delete(file)
76
+ @constants_defined[file] = block
77
+ @monitor_files[file] = metadata
78
+ @files[file] = {:features=>Set.new, :constants=>Array(block.call(file))}
79
+ end
80
+
81
+ @autoload_dirs.each_key do |dir|
82
+ check_autoload_dir(dir)
83
+ end
84
+
85
+ super
86
+ end
87
+ end
88
+ end
89
+ end
@@ -5,12 +5,6 @@ module Rack
5
5
  class Reloader
6
6
  File = ::File
7
7
 
8
- # Regexp for valid constant names, to prevent code execution.
9
- VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
10
-
11
- # Options hash to force loading of files even if they haven't changed.
12
- FORCE = {:force=>true}.freeze
13
-
14
8
  # Setup the reloader. Supports :logger and :subclasses options, see
15
9
  # Rack::Unloader.new for details.
16
10
  def initialize(opts={})
@@ -55,47 +49,6 @@ module Rack
55
49
  @skip_reload = []
56
50
  end
57
51
 
58
- # Strip the given path prefix from the internal data structures.
59
- def strip_path_prefix(path_prefix)
60
- empty = ''.freeze
61
-
62
- # Strip the path prefix from $LOADED_FEATURES, otherwise the reloading won't work.
63
- # Hopefully a future version of ruby will do this automatically when chrooting.
64
- $LOADED_FEATURES.map!{|s| s.sub(path_prefix, empty)}
65
-
66
- fix_path = lambda do |s|
67
- s.sub(path_prefix, empty)
68
- end
69
-
70
- [@dependency_order, @skip_reload].each do |a|
71
- a.map!(&fix_path)
72
- end
73
-
74
- [@files, @old_entries].each do |hash|
75
- hash.each do |k,h|
76
- h[:features].map!(&fix_path)
77
- end
78
- end
79
-
80
- @monitor_dirs.each_value do |a|
81
- a[1].map!(&fix_path)
82
- end
83
-
84
- @dependencies.each_value do |a|
85
- a.map!(&fix_path)
86
- end
87
-
88
- [@files, @old_entries, @monitor_files, @monitor_dirs, @constants_defined, @dependencies].each do |hash|
89
- hash.keys.each do |k|
90
- if k.start_with?(path_prefix)
91
- hash[fix_path.call(k)] = hash.delete(k)
92
- end
93
- end
94
- end
95
-
96
- nil
97
- end
98
-
99
52
  # Unload all reloadable constants and features, and clear the list
100
53
  # of files to monitor.
101
54
  def clear!
@@ -157,11 +110,7 @@ module Rack
157
110
  return if changed_files.empty?
158
111
 
159
112
  unless @dependencies.empty?
160
- changed_files = reload_files(changed_files)
161
- changed_files.flatten!
162
- changed_files.map!{|f| File.directory?(f) ? Unreloader.ruby_files(f) : f}
163
- changed_files.flatten!
164
- changed_files.uniq!
113
+ changed_files = Unreloader.expand_directory_paths(reload_files(changed_files))
165
114
 
166
115
  order = @dependency_order
167
116
  order &= changed_files
@@ -169,14 +118,14 @@ module Rack
169
118
  end
170
119
 
171
120
  unless @skip_reload.empty?
172
- skip_reload = @skip_reload.map{|f| File.directory?(f) ? Unreloader.ruby_files(f) : f}
173
- skip_reload.flatten!
174
- skip_reload.uniq!
175
- changed_files -= skip_reload
121
+ changed_files -= Unreloader.expand_directory_paths(@skip_reload)
176
122
  end
177
123
 
124
+ changed_files.select! do |file|
125
+ @monitor_files.has_key?(file)
126
+ end
178
127
  changed_files.each do |file|
179
- safe_load(file, FORCE)
128
+ safe_load(file)
180
129
  end
181
130
  end
182
131
 
@@ -200,14 +149,14 @@ module Rack
200
149
  begin
201
150
  safe_load(file, options)
202
151
  rescue NameError, LoadError => error
203
- log "Cyclic dependency reload for #{error}"
152
+ log "Cyclic dependency reload for #{error.class}: #{error.message}"
204
153
  rescue Exception => error
154
+ log "Error: #{error.class}: #{error.message}"
205
155
  break
206
156
  end
207
157
  end
208
158
 
209
159
  if error
210
- log error
211
160
  raise error
212
161
  end
213
162
  end
@@ -301,9 +250,6 @@ module Rack
301
250
  # by the require, and rolling back the constants and features if there
302
251
  # are any errors.
303
252
  def safe_load(file, options={})
304
- return unless @monitor_files.has_key?(file)
305
- return unless options[:force] || file_changed?(file)
306
-
307
253
  prepare(file) # might call #safe_load recursively
308
254
  log "Loading #{file}"
309
255
  begin
@@ -8,27 +8,31 @@ module Rack
8
8
  # Mutex used to synchronize reloads
9
9
  MUTEX = Monitor.new
10
10
 
11
- # Reference to ::File as File would return Rack::File by default.
11
+ # Reference to ::File as File may return Rack::File by default.
12
12
  File = ::File
13
13
 
14
+ # Regexp for valid constant names, to prevent code execution.
15
+ VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
16
+
14
17
  # Given the list of paths, find all matching files, or matching ruby files
15
18
  # in subdirecories if given a directory, and return an array of expanded
16
19
  # paths.
17
20
  def self.expand_directory_paths(paths)
18
- expand_paths(paths).
19
- map{|f| File.directory?(f) ? ruby_files(f) : f}.
20
- flatten
21
+ paths = expand_paths(paths)
22
+ paths.map!{|f| File.directory?(f) ? ruby_files(f) : f}
23
+ paths.flatten!
24
+ paths
21
25
  end
22
26
 
23
27
  # Given the path glob or array of path globs, find all matching files
24
28
  # or directories, and return an array of expanded paths.
25
29
  def self.expand_paths(paths)
26
- Array(paths).
27
- flatten.
28
- map{|path| Dir.glob(path).sort_by{|filename| filename.count('/')}}.
29
- flatten.
30
- map{|path| File.expand_path(path)}.
31
- uniq
30
+ paths = Array(paths).flatten
31
+ paths.map!{|path| Dir.glob(path).sort_by!{|filename| filename.count('/')}}
32
+ paths.flatten!
33
+ paths.map!{|path| File.expand_path(path)}
34
+ paths.uniq!
35
+ paths
32
36
  end
33
37
 
34
38
  # The .rb files in the given directory or any subdirectory.
@@ -40,11 +44,51 @@ module Rack
40
44
  files.sort
41
45
  end
42
46
 
47
+ # Autoload the file for the given objects. objs should be a string, symbol,
48
+ # or array of them holding a Ruby constant name. Access to the constant will
49
+ # load the related file. A non-nil logger will have output logged to it.
50
+ def self.autoload_constants(objs, file, logger)
51
+ strings = Array(objs).map(&:to_s)
52
+ if strings.empty?
53
+ # Remove file from $LOADED_FEATURES if there are no constants to autoload.
54
+ # In general that is because the file is part of another class that will
55
+ # handle loading the file separately, and if that class is reloaded, we
56
+ # want to remove the loaded feature so the file can get loaded again.
57
+ $LOADED_FEATURES.delete(file)
58
+ else
59
+ logger.info("Setting up autoload for #{file}: #{strings.join(' ')}") if logger
60
+ strings.each do |s|
61
+ obj, mod = split_autoload(s)
62
+
63
+ if obj
64
+ obj.autoload(mod, file)
65
+ elsif logger
66
+ logger.info("Invalid constant name: #{s}")
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # Split the given string into an array. The first is a module/class to add the
73
+ # autoload to, and the second is the name of the constant to be autoloaded.
74
+ def self.split_autoload(mod_string)
75
+ if m = VALID_CONSTANT_NAME_REGEXP.match(mod_string)
76
+ ns, sep, mod = m[1].rpartition('::')
77
+ if sep.empty?
78
+ [Object, mod]
79
+ else
80
+ [Object.module_eval("::#{ns}", __FILE__, __LINE__), mod]
81
+ end
82
+ end
83
+ end
84
+
43
85
  # The Rack::Unreloader::Reloader instead related to this instance, if one.
44
86
  attr_reader :reloader
45
87
 
46
88
  # Setup the reloader. Options:
47
89
  #
90
+ # :autoload :: Whether to allow autoloading. If not set to true, calls to
91
+ # autoload will eagerly require the related files instead of autoloading.
48
92
  # :cooldown :: The number of seconds to wait between checks for changed files.
49
93
  # Defaults to 1. Set to nil/false to not check for changed files.
50
94
  # :handle_reload_errors :: Whether reload to handle reload errors by returning
@@ -58,12 +102,19 @@ module Rack
58
102
  # match exactly, since modules don't have superclasses.
59
103
  def initialize(opts={}, &block)
60
104
  @app_block = block
105
+ @autoload = opts[:autoload]
106
+ @logger = opts[:logger]
61
107
  if opts.fetch(:reload, true)
62
108
  @cooldown = opts.fetch(:cooldown, 1)
63
109
  @handle_reload_errors = opts[:handle_reload_errors]
64
110
  @last = Time.at(0)
65
- Kernel.require 'rack/unreloader/reloader'
66
- @reloader = Reloader.new(opts)
111
+ if @autoload
112
+ require_relative('unreloader/autoload_reloader')
113
+ @reloader = AutoloadReloader.new(opts)
114
+ else
115
+ require_relative('unreloader/reloader')
116
+ @reloader = Reloader.new(opts)
117
+ end
67
118
  reload!
68
119
  else
69
120
  @reloader = @cooldown = @handle_reload_errors = false
@@ -86,6 +137,18 @@ module Rack
86
137
  @app_block.call.call(env)
87
138
  end
88
139
 
140
+ # Whether the unreloader is setup for reloading. If false, no reloading
141
+ # is done after the initial require.
142
+ def reload?
143
+ !!@reloader
144
+ end
145
+
146
+ # Whether the unreloader is setup for autoloading. If false, autoloads
147
+ # are treated as requires.
148
+ def autoload?
149
+ !!@autoload
150
+ end
151
+
89
152
  # Add a file glob or array of file globs to monitor for changes.
90
153
  # Options:
91
154
  # :delete_hook :: When a file being monitored is deleted, call
@@ -98,6 +161,24 @@ module Rack
98
161
  end
99
162
  end
100
163
 
164
+ # Add a file glob or array of file global to autoload and monitor
165
+ # for changes. A block is required. It will be called with the
166
+ # path to be autoloaded, and should return the symbol for the
167
+ # constant name to autoload. Accepts the same options as #require.
168
+ def autoload(paths, opts={}, &block)
169
+ raise ArgumentError, "block required" unless block
170
+
171
+ if @autoload
172
+ if @reloader
173
+ @reloader.autoload_dependencies(paths, opts, &block)
174
+ else
175
+ Unreloader.expand_directory_paths(paths).each{|f| Unreloader.autoload_constants(yield(f), f, @logger)}
176
+ end
177
+ else
178
+ require(paths, opts, &block)
179
+ end
180
+ end
181
+
101
182
  # Records that each path in +files+ depends on +dependency+. If there
102
183
  # is a modification to +dependency+, all related files will be reloaded
103
184
  # after +dependency+ is reloaded. Both +dependency+ and each entry in +files+
@@ -128,17 +209,5 @@ module Rack
128
209
  def reload!
129
210
  @reloader.reload! if @reloader
130
211
  end
131
-
132
- # Strip the given path prefix from all absolute paths used by the
133
- # reloader. This is designed when chrooting an application.
134
- #
135
- # Options:
136
- # :strip_core :: Also strips the path prefix from $LOADED_FEATURES and
137
- # $LOAD_PATH.
138
- def strip_path_prefix(path_prefix, opts={})
139
- if @reloader
140
- @reloader.strip_path_prefix(path_prefix)
141
- end
142
- end
143
212
  end
144
213
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-unreloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-15 00:00:00.000000000 Z
11
+ date: 2023-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -66,12 +66,9 @@ files:
66
66
  - CHANGELOG
67
67
  - MIT-LICENSE
68
68
  - README.rdoc
69
- - Rakefile
70
69
  - lib/rack/unreloader.rb
70
+ - lib/rack/unreloader/autoload_reloader.rb
71
71
  - lib/rack/unreloader/reloader.rb
72
- - spec/spec_helper.rb
73
- - spec/strip_paths_spec.rb
74
- - spec/unreloader_spec.rb
75
72
  homepage: http://github.com/jeremyevans/rack-unreloader
76
73
  licenses:
77
74
  - MIT
@@ -80,7 +77,7 @@ metadata:
80
77
  changelog_uri: https://github.com/jeremyevans/rack-unreloader/blob/master/CHANGELOG
81
78
  mailing_list_uri: https://github.com/jeremyevans/rack-unreloader/discussions
82
79
  source_code_uri: https://github.com/jeremyevans/rack-unreloader
83
- post_install_message:
80
+ post_install_message:
84
81
  rdoc_options:
85
82
  - "--quiet"
86
83
  - "--line-numbers"
@@ -95,15 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
92
  requirements:
96
93
  - - ">="
97
94
  - !ruby/object:Gem::Version
98
- version: 1.8.7
95
+ version: 1.9.2
99
96
  required_rubygems_version: !ruby/object:Gem::Requirement
100
97
  requirements:
101
98
  - - ">="
102
99
  - !ruby/object:Gem::Version
103
100
  version: '0'
104
101
  requirements: []
105
- rubygems_version: 3.2.22
106
- signing_key:
102
+ rubygems_version: 3.4.1
103
+ signing_key:
107
104
  specification_version: 4
108
105
  summary: Reload application when files change, unloading constants first
109
106
  test_files: []
data/Rakefile DELETED
@@ -1,45 +0,0 @@
1
- require "rake"
2
- require "rake/clean"
3
-
4
- CLEAN.include ["rack-unreloader-*.gem", "rdoc"]
5
-
6
- desc "Build rack-unreloader gem"
7
- task :package=>[:clean] do |p|
8
- sh %{#{FileUtils::RUBY} -S gem build rack-unreloader.gemspec}
9
- end
10
-
11
- ### Specs
12
-
13
- desc "Run specs"
14
- task :spec do
15
- sh "#{FileUtils::RUBY} #{'-w ' if RUBY_VERSION >= '3'}spec/unreloader_spec.rb"
16
- end
17
-
18
- task :default => :spec
19
-
20
- ### RDoc
21
-
22
- RDOC_DEFAULT_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', 'Rack::Unreloader: Reload application when files change, unloading constants first']
23
-
24
- begin
25
- gem 'hanna-nouveau'
26
- RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
27
- rescue Gem::LoadError
28
- end
29
-
30
- rdoc_task_class = begin
31
- require "rdoc/task"
32
- RDoc::Task
33
- rescue LoadError
34
- require "rake/rdoctask"
35
- Rake::RDocTask
36
- end
37
-
38
- RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
39
-
40
- rdoc_task_class.new do |rdoc|
41
- rdoc.rdoc_dir = "rdoc"
42
- rdoc.options += RDOC_OPTS
43
- rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb"
44
- end
45
-
data/spec/spec_helper.rb DELETED
@@ -1,87 +0,0 @@
1
- require File.join(File.dirname(File.expand_path(__FILE__)), '../lib/rack/unreloader')
2
- require 'rubygems'
3
- $: << 'lib'
4
- ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
5
- gem 'minitest'
6
- require 'minitest/global_expectations/autorun'
7
- require 'minitest/hooks'
8
-
9
- module ModifiedAt
10
- def set_modified_time(file, time)
11
- time = Time.now + time if time.is_a?(Integer)
12
- modified_times[File.expand_path(file)] = time
13
- end
14
-
15
- def modified_times
16
- @modified_times ||= {}
17
- end
18
-
19
- private
20
-
21
- def modified_at(file)
22
- modified_times[file] || super
23
- end
24
- end
25
-
26
- class Minitest::Spec
27
- def code(i)
28
- "class App; class << self; def call(env) @a end; alias call call; end; @a ||= []; @a << #{i}; end"
29
- end
30
-
31
- def update_app(code, file=@filename)
32
- if ru.reloader
33
- ru.reloader.set_modified_time(File.dirname(file), @i += 1) unless File.file?(file)
34
- ru.reloader.set_modified_time(file, @i += 1)
35
- end
36
- File.open(file, 'wb'){|f| f.write(code)}
37
- end
38
-
39
- def file_delete(file)
40
- if ru.reloader
41
- ru.reloader.set_modified_time(File.dirname(file), @i += 1)
42
- end
43
- File.delete(file)
44
- end
45
-
46
- def logger
47
- return @logger if @logger
48
- @logger = []
49
- def @logger.method_missing(meth, log)
50
- self << log
51
- end
52
- @logger
53
- end
54
-
55
- def base_ru(opts={})
56
- block = opts[:block] || proc{App}
57
- @ru = Rack::Unreloader.new({:logger=>logger, :cooldown=>0}.merge(opts), &block)
58
- @ru.reloader.extend ModifiedAt if @ru.reloader
59
- Object.const_set(:RU, @ru)
60
- end
61
-
62
- def ru(opts={})
63
- return @ru if @ru
64
- base_ru(opts)
65
- update_app(opts[:code]||code(1))
66
- @ru.require @filename
67
- @ru
68
- end
69
-
70
- def log_match(*logs)
71
- @logger.length.must_equal logs.length
72
- logs.zip(@logger).each{|l, log| l.is_a?(String) ? log.must_equal(l) : log.must_match(l)}
73
- end
74
-
75
- before do
76
- @i = 0
77
- @filename = 'spec/app.rb'
78
- end
79
-
80
- after do
81
- ru.reloader.clear! if ru.reloader
82
- Object.send(:remove_const, :RU)
83
- Object.send(:remove_const, :App) if defined?(::App)
84
- Object.send(:remove_const, :App2) if defined?(::App2)
85
- Dir['spec/app*.rb'].each{|f| File.delete(f)}
86
- end
87
- end