rack-unreloader 1.4.0 → 1.8.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.
- checksums.yaml +5 -5
- data/CHANGELOG +22 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +79 -42
- data/Rakefile +1 -1
- data/lib/rack/unreloader/reloader.rb +102 -27
- data/lib/rack/unreloader.rb +31 -7
- data/spec/spec_helper.rb +87 -0
- data/spec/strip_paths_spec.rb +858 -0
- data/spec/unreloader_spec.rb +85 -79
- metadata +25 -6
data/spec/unreloader_spec.rb
CHANGED
@@ -1,76 +1,6 @@
|
|
1
|
-
require File.join(File.dirname(File.expand_path(__FILE__)), '
|
2
|
-
gem 'minitest'
|
3
|
-
require 'minitest/autorun'
|
4
|
-
require 'minitest/hooks'
|
5
|
-
|
6
|
-
module ModifiedAt
|
7
|
-
def set_modified_time(file, time)
|
8
|
-
modified_times[File.expand_path(file)] = time
|
9
|
-
end
|
10
|
-
|
11
|
-
def modified_times
|
12
|
-
@modified_times ||= {}
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def modified_at(file)
|
18
|
-
modified_times[file] || super
|
19
|
-
end
|
20
|
-
end
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
|
21
2
|
|
22
3
|
describe Rack::Unreloader do
|
23
|
-
def code(i)
|
24
|
-
"class App; def self.call(env) @a end; @a ||= []; @a << #{i}; end"
|
25
|
-
end
|
26
|
-
|
27
|
-
def update_app(code, file=@filename)
|
28
|
-
ru.reloader.set_modified_time(file, @i += 1) if ru.reloader
|
29
|
-
File.open(file, 'wb'){|f| f.write(code)}
|
30
|
-
end
|
31
|
-
|
32
|
-
def logger
|
33
|
-
return @logger if @logger
|
34
|
-
@logger = []
|
35
|
-
def @logger.method_missing(meth, log)
|
36
|
-
self << log
|
37
|
-
end
|
38
|
-
@logger
|
39
|
-
end
|
40
|
-
|
41
|
-
def base_ru(opts={})
|
42
|
-
block = opts[:block] || proc{App}
|
43
|
-
@ru = Rack::Unreloader.new({:logger=>logger, :cooldown=>0}.merge(opts), &block)
|
44
|
-
@ru.reloader.extend ModifiedAt if @ru.reloader
|
45
|
-
Object.const_set(:RU, @ru)
|
46
|
-
end
|
47
|
-
|
48
|
-
def ru(opts={})
|
49
|
-
return @ru if @ru
|
50
|
-
base_ru(opts)
|
51
|
-
update_app(opts[:code]||code(1))
|
52
|
-
@ru.require @filename
|
53
|
-
@ru
|
54
|
-
end
|
55
|
-
|
56
|
-
def log_match(*logs)
|
57
|
-
@logger.length.must_equal logs.length
|
58
|
-
logs.zip(@logger).each{|l, log| l.is_a?(String) ? log.must_equal(l) : log.must_match(l)}
|
59
|
-
end
|
60
|
-
|
61
|
-
before do
|
62
|
-
@i = 0
|
63
|
-
@filename = 'spec/app.rb'
|
64
|
-
end
|
65
|
-
|
66
|
-
after do
|
67
|
-
ru.reloader.clear! if ru.reloader
|
68
|
-
Object.send(:remove_const, :RU)
|
69
|
-
Object.send(:remove_const, :App) if defined?(::App)
|
70
|
-
Object.send(:remove_const, :App2) if defined?(::App2)
|
71
|
-
Dir['spec/app*.rb'].each{|f| File.delete(f)}
|
72
|
-
end
|
73
|
-
|
74
4
|
it "should not reload files automatically if cooldown option is nil" do
|
75
5
|
ru(:cooldown => nil).call({}).must_equal [1]
|
76
6
|
update_app(code(2))
|
@@ -100,6 +30,16 @@ describe Rack::Unreloader do
|
|
100
30
|
%r{\ANew classes in .*spec/app\.rb: App\z}
|
101
31
|
end
|
102
32
|
|
33
|
+
it "should stop monitoring file for changes if it is deleted constants contained in file and reload file if file changes" do
|
34
|
+
ru.call({}).must_equal [1]
|
35
|
+
file_delete('spec/app.rb')
|
36
|
+
proc{ru.call({})}.must_raise NameError
|
37
|
+
log_match %r{\ALoading.*spec/app\.rb\z},
|
38
|
+
%r{\ANew classes in .*spec/app\.rb: App\z},
|
39
|
+
%r{\AUnloading.*spec/app\.rb\z},
|
40
|
+
"Removed constant App"
|
41
|
+
end
|
42
|
+
|
103
43
|
it "should check constants using ObjectSpace if require proc returns :ObjectSpace" do
|
104
44
|
base_ru
|
105
45
|
update_app(code(1))
|
@@ -191,7 +131,7 @@ describe Rack::Unreloader do
|
|
191
131
|
end
|
192
132
|
|
193
133
|
it "should not unload modules by name if :subclasses option used and module not present" do
|
194
|
-
ru(:subclasses=>'Foo', :code=>"module App; def self.call(env) @a end; @a ||= []; @a << 1; end").call({}).must_equal [1]
|
134
|
+
ru(:subclasses=>'Foo', :code=>"module App; def self.call(env) @a end; class << self; alias call call; end; @a ||= []; @a << 1; end").call({}).must_equal [1]
|
195
135
|
update_app("module App; def self.call(env) @a end; @a ||= []; @a << 2; end")
|
196
136
|
ru.call({}).must_equal [1, 2]
|
197
137
|
log_match %r{\ALoading.*spec/app\.rb\z},
|
@@ -203,7 +143,29 @@ describe Rack::Unreloader do
|
|
203
143
|
ru.call({}).must_equal [1]
|
204
144
|
update_app("module App; def self.call(env) @a end; @a ||= []; raise 'foo'; end")
|
205
145
|
proc{ru.call({})}.must_raise RuntimeError
|
206
|
-
defined?(::App).
|
146
|
+
defined?(::App).must_be_nil
|
147
|
+
update_app(code(2))
|
148
|
+
ru.call({}).must_equal [2]
|
149
|
+
log_match %r{\ALoading.*spec/app\.rb\z},
|
150
|
+
%r{\ANew classes in .*spec/app\.rb: App\z},
|
151
|
+
%r{\AUnloading.*spec/app\.rb\z},
|
152
|
+
"Removed constant App",
|
153
|
+
%r{\ALoading.*spec/app\.rb\z},
|
154
|
+
%r{\AFailed to load .*spec/app\.rb; removing partially defined constants\z},
|
155
|
+
"Removed constant App",
|
156
|
+
%r{\ALoading.*spec/app\.rb\z},
|
157
|
+
%r{\ANew classes in .*spec/app\.rb: App\z}
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should support :handle_reload_errors option to return backtrace if there is an error reloading" do
|
161
|
+
ru(:handle_reload_errors=>true).call({}).must_equal [1]
|
162
|
+
update_app("module App; def self.call(env) @a end; @a ||= []; raise 'foo'; end")
|
163
|
+
rack_response = ru.call({})
|
164
|
+
rack_response[0].must_equal 500
|
165
|
+
rack_response[1]['Content-Type'].must_equal 'text/plain'
|
166
|
+
rack_response[1]['Content-Length'].must_match(rack_response[2][0].bytesize.to_s)
|
167
|
+
rack_response[2][0].must_match(/\/spec\/app\.rb:1/)
|
168
|
+
defined?(::App).must_be_nil
|
207
169
|
update_app(code(2))
|
208
170
|
ru.call({}).must_equal [2]
|
209
171
|
log_match %r{\ALoading.*spec/app\.rb\z},
|
@@ -256,7 +218,7 @@ describe Rack::Unreloader do
|
|
256
218
|
|
257
219
|
it "should allow specifying proc for which constants get removed" do
|
258
220
|
base_ru
|
259
|
-
update_app("class App; def self.call(env) [@a, App2.a] end; @a ||= []; @a << 1; end; class App2; def self.a; @a end; @a ||= []; @a << 2; end")
|
221
|
+
update_app("class App; def self.call(env) [@a, App2.a] end; class << self; alias call call; end; @a ||= []; @a << 1; end; class App2; def self.a; @a end; class << self; alias a a; end; @a ||= []; @a << 2; end")
|
260
222
|
@ru.require('spec/app.rb'){|f| File.basename(f).sub(/\.rb/, '').capitalize}
|
261
223
|
ru.call({}).must_equal [[1], [2]]
|
262
224
|
update_app("class App; def self.call(env) [@a, App2.a] end; @a ||= []; @a << 3; end; class App2; def self.a; @a end; @a ||= []; @a << 4; end")
|
@@ -326,6 +288,20 @@ describe Rack::Unreloader do
|
|
326
288
|
ru.call({}).must_equal 4
|
327
289
|
end
|
328
290
|
|
291
|
+
it "should handle modules where name raises an exception" do
|
292
|
+
m = Module.new{def self.name; raise end}
|
293
|
+
ru(:code=>"module App; def self.call(env) @a end; @a ||= []; @a << 1; end").call({}).must_equal [1]
|
294
|
+
update_app("module App; def self.call(env) @a end; @a ||= []; @a << 2; end")
|
295
|
+
ru.call({}).must_equal [2]
|
296
|
+
log_match %r{\ALoading.*spec/app\.rb\z},
|
297
|
+
%r{\ANew classes in .*spec/app\.rb: App\z},
|
298
|
+
%r{\AUnloading.*spec/app\.rb\z},
|
299
|
+
"Removed constant App",
|
300
|
+
%r{\ALoading.*spec/app\.rb\z},
|
301
|
+
%r{\ANew classes in .*spec/app\.rb: App\z}
|
302
|
+
m
|
303
|
+
end
|
304
|
+
|
329
305
|
describe "with a directory" do
|
330
306
|
include Minitest::Hooks
|
331
307
|
|
@@ -336,7 +312,7 @@ describe Rack::Unreloader do
|
|
336
312
|
end
|
337
313
|
|
338
314
|
after do
|
339
|
-
Dir['spec/dir/**/*.rb'].each{|f|
|
315
|
+
Dir['spec/dir/**/*.rb'].each{|f| file_delete(f)}
|
340
316
|
end
|
341
317
|
|
342
318
|
after(:all) do
|
@@ -391,7 +367,7 @@ describe Rack::Unreloader do
|
|
391
367
|
ru.call({}).must_equal 3
|
392
368
|
update_app("module C; B = 4; end", 'spec/dir/subdir2/app_mod2.rb')
|
393
369
|
ru.call({}).must_equal 4
|
394
|
-
|
370
|
+
file_delete 'spec/dir/subdir/app_mod.rb'
|
395
371
|
ru.call({}).must_equal 0
|
396
372
|
end
|
397
373
|
|
@@ -412,7 +388,7 @@ describe Rack::Unreloader do
|
|
412
388
|
ru.call({}).must_equal 561
|
413
389
|
update_app("class App; end", 'spec/dir/appa.rb')
|
414
390
|
ru.call({}).must_equal 61
|
415
|
-
|
391
|
+
file_delete 'spec/dir/appb.rb'
|
416
392
|
ru.call({}).must_equal 1
|
417
393
|
end
|
418
394
|
|
@@ -478,7 +454,7 @@ describe Rack::Unreloader do
|
|
478
454
|
update_app("App.call[:foo] = 1", 'spec/dir/a.rb')
|
479
455
|
@ru.require('spec/app.rb')
|
480
456
|
ru.call({}).must_equal(:foo=>1)
|
481
|
-
|
457
|
+
file_delete('spec/dir/a.rb')
|
482
458
|
update_app("App.call[:foo] = 2", 'spec/dir/b.rb')
|
483
459
|
ru.call({}).must_equal(:foo=>2)
|
484
460
|
log_match %r{\ALoading.*spec/app\.rb\z},
|
@@ -495,7 +471,7 @@ describe Rack::Unreloader do
|
|
495
471
|
update_app("App.call[:foo] = 1", 'spec/dir/subdir/a.rb')
|
496
472
|
@ru.require('spec/app.rb')
|
497
473
|
ru.call({}).must_equal(:foo=>1)
|
498
|
-
|
474
|
+
file_delete('spec/dir/subdir/a.rb')
|
499
475
|
update_app("App.call[:foo] = 2", 'spec/dir/subdir/b.rb')
|
500
476
|
ru.call({}).must_equal(:foo=>2)
|
501
477
|
log_match %r{\ALoading.*spec/app\.rb\z},
|
@@ -505,5 +481,35 @@ describe Rack::Unreloader do
|
|
505
481
|
%r{\AUnloading .*/spec/dir/subdir/a.rb\z},
|
506
482
|
%r{\ALoading.*spec/dir/subdir/b\.rb\z}
|
507
483
|
end
|
484
|
+
|
485
|
+
it "should call hook when dropping files deleted from the directory" do
|
486
|
+
base_ru
|
487
|
+
deletes = []
|
488
|
+
Object.const_set(:Deletes, deletes)
|
489
|
+
update_app("class App; @a = {}; def self.call(env=nil) @a end; end; RU.require('spec/dir', :delete_hook=>proc{|f| Deletes << f})")
|
490
|
+
update_app("App.call[:foo] = 1", 'spec/dir/a.rb')
|
491
|
+
@ru.require('spec/app.rb', :delete_hook=>proc{|f| deletes << f})
|
492
|
+
ru.call({}).must_equal(:foo=>1)
|
493
|
+
file_delete('spec/dir/a.rb')
|
494
|
+
update_app("App.call[:foo] = 2", 'spec/dir/b.rb')
|
495
|
+
ru.call({}).must_equal(:foo=>2)
|
496
|
+
deletes.must_equal [File.expand_path('spec/dir/a.rb')]
|
497
|
+
file_delete('spec/dir/b.rb')
|
498
|
+
ru.call({}).must_equal(:foo=>2)
|
499
|
+
deletes.must_equal [File.expand_path('spec/dir/a.rb'), File.expand_path('spec/dir/b.rb')]
|
500
|
+
file_delete('spec/app.rb')
|
501
|
+
proc{ru.call({})}.must_raise NameError
|
502
|
+
deletes.must_equal [File.expand_path('spec/dir/a.rb'), File.expand_path('spec/dir/b.rb'), File.expand_path('spec/app.rb')]
|
503
|
+
log_match %r{\ALoading.*spec/app\.rb\z},
|
504
|
+
%r{\ALoading.*spec/dir/a\.rb\z},
|
505
|
+
%r{\ANew classes in .*spec/app\.rb: App\z},
|
506
|
+
%r{\ANew features in .*spec/app\.rb: .*spec/dir/a\.rb\z},
|
507
|
+
%r{\AUnloading .*/spec/dir/a.rb\z},
|
508
|
+
%r{\ALoading.*spec/dir/b\.rb\z},
|
509
|
+
%r{\AUnloading .*/spec/dir/b.rb\z},
|
510
|
+
%r{\AUnloading .*/spec/app.rb\z},
|
511
|
+
%r{\ARemoved constant App\z}
|
512
|
+
Object.send(:remove_const, :Deletes)
|
513
|
+
end
|
508
514
|
end
|
509
515
|
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.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-global_expectations
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
description: |
|
42
56
|
Rack::Unreloader is a rack middleware that reloads application files when it
|
43
57
|
detects changes, unloading constants defined in those files before reloading.
|
@@ -55,11 +69,17 @@ files:
|
|
55
69
|
- Rakefile
|
56
70
|
- lib/rack/unreloader.rb
|
57
71
|
- lib/rack/unreloader/reloader.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
- spec/strip_paths_spec.rb
|
58
74
|
- spec/unreloader_spec.rb
|
59
75
|
homepage: http://github.com/jeremyevans/rack-unreloader
|
60
76
|
licenses:
|
61
77
|
- MIT
|
62
|
-
metadata:
|
78
|
+
metadata:
|
79
|
+
bug_tracker_uri: https://github.com/jeremyevans/rack-unreloader/issues
|
80
|
+
changelog_uri: https://github.com/jeremyevans/rack-unreloader/blob/master/CHANGELOG
|
81
|
+
mailing_list_uri: https://github.com/jeremyevans/rack-unreloader/discussions
|
82
|
+
source_code_uri: https://github.com/jeremyevans/rack-unreloader
|
63
83
|
post_install_message:
|
64
84
|
rdoc_options:
|
65
85
|
- "--quiet"
|
@@ -75,15 +95,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
95
|
requirements:
|
76
96
|
- - ">="
|
77
97
|
- !ruby/object:Gem::Version
|
78
|
-
version:
|
98
|
+
version: 1.8.7
|
79
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
100
|
requirements:
|
81
101
|
- - ">="
|
82
102
|
- !ruby/object:Gem::Version
|
83
103
|
version: '0'
|
84
104
|
requirements: []
|
85
|
-
|
86
|
-
rubygems_version: 2.5.1
|
105
|
+
rubygems_version: 3.2.22
|
87
106
|
signing_key:
|
88
107
|
specification_version: 4
|
89
108
|
summary: Reload application when files change, unloading constants first
|