rerun 0.9.0 → 0.13.1

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
- SHA1:
3
- metadata.gz: dfeb631293e3c084bb49fe63e960edd9ec01a7bc
4
- data.tar.gz: dfacf8cd2c638cc518b3021d3267f09b0f9df91a
2
+ SHA256:
3
+ metadata.gz: 12916c529d417f7435294064c61ab8bc9b3e4811e61ab162725b84dc42730212
4
+ data.tar.gz: 2d32573a380ae73f845564580eced58064271d3347a09da7c5e13ad72882a8c5
5
5
  SHA512:
6
- metadata.gz: 15d53a73449cc4755ca91ebf2c59c5f21b64d9ac97c43c3af3f47c9404e0fea33ba02f6e2b3ab9fe021043fbc60bb9fa0b4f81d2af1363a8bc151136462c53df
7
- data.tar.gz: 74e2275e507e39b77ca9f01358178639f2f65154b4255518e550371f5c78fb95a84d90b34e11236bc0db9ea3ba579d21fc38c68edb1368803ac44beb4c4a2574
6
+ metadata.gz: a3d9ed3d68053c23b3c9665abc33cde89d4fd5893fa1d9930fbcb93113459450810ced0d3d0cd7e83c2bc4dc9da27446228f1104dc2a46cdb01ef53dc8bc5f48
7
+ data.tar.gz: 1df642c255cd253271015b111f2b0fa4b9023c0fd697b1bd03405b2fccb9d716658f840a82884dfa7b042395c07664205f6427c08a9b432f8742ce58413792cd
data/README.md CHANGED
@@ -14,14 +14,18 @@ Rerun's advantage is its simple design. Since it uses `exec` and the standard
14
14
  Unix `SIGINT` and `SIGKILL` signals, you're sure the restarted app is really
15
15
  acting just like it was when you ran it from the command line the first time.
16
16
 
17
- By default only `*.{rb,js,css,scss,sass,erb,html,haml,ru}` files are watched.
17
+ By default it watches files ending in: `rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h`.
18
18
  Use the `--pattern` option if you want to change this.
19
19
 
20
20
  As of version 0.7.0, we use the Listen gem, which tries to use your OS's
21
21
  built-in facilities for monitoring the filesystem, so CPU use is very light.
22
22
 
23
- Rerun does not work on Windows. Sorry, but you can't do much relaunching
24
- without "fork".
23
+ **UPDATE**: Now Rerun *does* work on Windows! Caveats:
24
+ * not well-tested
25
+ * you need to press Enter after keypress input
26
+ * you may need to install the `wdm` gem manually: `gem install wdm`
27
+ * You may see this persistent `INFO` error message; to remove it, use`--no-notify`:
28
+ * `INFO: Could not find files for the given pattern(s)`
25
29
 
26
30
  # Installation:
27
31
 
@@ -40,10 +44,39 @@ they're not available. Unfortunately, Rubygems doesn't understand optional
40
44
  dependencies very well, so you may have to install extra gems (and/or put them
41
45
  in your Gemfile) to make Rerun work more smoothly on your system.
42
46
  (Learn more at <https://github.com/guard/listen#listen-adapters>.)
43
- For example, on Mac OS X, use
47
+
48
+ On Mac OS X, use
44
49
 
45
50
  gem install rb-fsevent
46
51
 
52
+ On Windows, use
53
+
54
+ gem install wdm
55
+
56
+ On *BSD, use
57
+
58
+ gem install rb-kqueue
59
+
60
+ ## Installation via Gemfile / Bundler
61
+
62
+ If you are using rerun inside an existing Ruby application (like a Rails or Sinatra app), you can add it to your Gemfile:
63
+
64
+ ``` ruby
65
+ group :development, :test do
66
+ gem "rerun"
67
+ end
68
+ ```
69
+
70
+ Using a Gemfile is also an easy way to use the pre-release branch, which may have bugfixes or features you want:
71
+
72
+ ``` ruby
73
+ group :development, :test do
74
+ gem "rerun", git: "https://github.com/alexch/rerun.git"
75
+ end
76
+ ```
77
+
78
+ When using a Gemfile, install with `bundle install` or `bundle update`, and run using `bundle exec rerun`, to guarantee you are using the rerun version specified in the Gemfile, and not a different version in a system-wide gemset.
79
+
47
80
  # Usage:
48
81
 
49
82
  rerun [options] [--] cmd
@@ -113,42 +146,95 @@ Procfile processes locally and restart them all when necessary.
113
146
 
114
147
  # Options:
115
148
 
149
+ These options can be specified on the command line and/or inside a `.rerun` config file (see below).
150
+
116
151
  `--dir` directory (or directories) to watch (default = "."). Separate multiple paths with ',' and/or use multiple `-d` options.
117
152
 
118
153
  `--pattern` glob to match inside directory. This uses the Ruby Dir glob style -- see <http://www.ruby-doc.org/core/classes/Dir.html#M002322> for details.
119
- By default it watches files ending in: `rb,js,css,scss,sass,erb,html,haml,ru`.
120
- On top of this, it also ignores dotfiles, `.tmp` files, and some other files and directories (like `.git .hg .rbx .svn bundle log tmp`) -- run `rerun --help` for the full list.
154
+ By default it watches files ending in: `rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h`.
155
+ On top of this, it also ignores dotfiles, `.tmp` files, and some other files and directories (like `.git` and `log`).
156
+ Run `rerun --help` to see the actual list.
157
+
158
+ `--ignore pattern` file glob to ignore (can be set many times). To ignore a directory, you must append `'/*'` e.g.
159
+ `--ignore 'coverage/*'`.
121
160
 
122
- `--signal` (or -s) use specified signal (instead of the default SIGTERM) to terminate the previous process.
161
+ `--[no-]ignore-dotfiles` By default, on top of --pattern and --ignore, we ignore any changes to files and dirs starting with a dot. Setting `--no-ignore-dotfiles` allows you to monitor a relevant file like .env, but you may also have to explicitly --ignore more dotfiles and dotdirs.
162
+
163
+ `--signal` (or `-s`) use specified signal(s) (instead of the default `TERM,INT,KILL`) to terminate the previous process. You can use a comma-delimited list if you want to try a signal, wait up to 5 seconds for the process to die, then try again with a different signal, and so on.
123
164
  This may be useful for forcing the respective process to terminate as quickly as possible.
124
165
  (`--signal KILL` is the equivalent of `kill -9`)
125
166
 
126
- `--clear` (or -c) clear the screen before each run
167
+ `--wait sec` (or `-w`) after asking the process to terminate, wait this long (in seconds) before either aborting, or trying the next signal in series. Default: 2 sec
168
+
169
+ `--restart` (or `-r`) expect process to restart itself, using signal HUP by default
170
+ (e.g. `-r -s INT` will send a INT and then resume watching for changes)
127
171
 
128
172
  `--exit` (or -x) expect the program to exit. With this option, rerun checks the return value; without it, rerun checks that the launched process is still running.
129
173
 
174
+ `--clear` (or -c) clear the screen before each run
175
+
130
176
  `--background` (or -b) disable on-the-fly commands, allowing the process to be backgrounded
131
177
 
132
- `--no-growl` don't use growl
178
+ `--notify NOTIFIER` use `growl` or `osx` or `notify-send` for notifications (see below)
179
+
180
+ `--no-notify` disable notifications
133
181
 
134
182
  `--name` set the app name (for display)
135
183
 
136
- Also --version and --help, naturally.
184
+ `--force-polling` use polling instead of a native filesystem scan (useful for Vagrant)
185
+
186
+ `--quiet` silences most messages
187
+
188
+ `--verbose` enables even more messages (unless you also specified `--quiet`, which overrides `--verbose`)
189
+
190
+ Also `--version` and `--help`, naturally.
191
+
192
+ ## Config file
193
+
194
+ If the current directory contains a file named `.rerun`, it will be parsed with the same rules as command-line arguments. Newlines are the same as any other whitespace, so you can stack options vertically, like this:
195
+
196
+ ```
197
+ --quiet
198
+ --pattern **/*.{rb,js,scss,sass,html,md}
199
+ ```
137
200
 
138
- # Growl Notifications
201
+ Options specified on the command line will override those in the config file. You can negate boolean options with `--no-`, so for example, with the above config file, to re-enable logging, you could say:
202
+
203
+ ```sh
204
+ rerun --no-quiet rackup
205
+ ```
206
+
207
+ If you're not sure what options are being overwritten, use `--verbose` and rerun will show you the final result of the parsing.
208
+
209
+ # Notifications
139
210
 
140
211
  If you have `growlnotify` available on the `PATH`, it sends notifications to
141
- growl in addition to the console. If you have growl but don't want rerun to use it,
142
- set the `--no-growl` option.
212
+ growl in addition to the console.
213
+
214
+ If you have `terminal-notifier`, it sends notifications to
215
+ the OS X notification center in addition to the console.
216
+
217
+ If you have `notify-send`, it sends notifications to Freedesktop-compatible
218
+ desktops in addition to the console.
219
+
220
+ If you have more than one available notification program, Rerun will pick one, or you can choose between them using `--notify growl`, `--notify osx`, `--notify notify-send`, etc.
221
+
222
+ If you have a notifier installed but don't want rerun to use it,
223
+ set the `--no-notify` option.
143
224
 
144
225
  Download [growlnotify here](http://growl.info/downloads.php#generaldownloads)
145
226
  now that Growl has moved to the App Store.
146
227
 
228
+ Install [terminal-notifier](https://github.com/julienXX/terminal-notifier) using `gem install terminal-notifier`. (You may have to put it in your system gemset and/or use `sudo` too.) Using Homebrew to install terminal-notifier is not recommended.
229
+
230
+ On Debian/Ubuntu, `notify-send` is availble in the `libnotify-bin` package. On other GNU/Linux systems, it might be in a package with a different name.
231
+
147
232
  # On-The-Fly Commands
148
233
 
149
234
  While the app is (re)running, you can make things happen by pressing keys:
150
235
 
151
236
  * **r** -- restart (as if a file had changed)
237
+ * **f** -- force restart (stop and start)
152
238
  * **c** -- clear the screen
153
239
  * **x** or **q** -- exit (just like control-C)
154
240
  * **p** -- pause/unpause filesystem watching
@@ -161,44 +247,66 @@ keys to be trapped, so use the `--background` option.
161
247
  The current algorithm for killing the process is:
162
248
 
163
249
  * send [SIGTERM](http://en.wikipedia.org/wiki/SIGTERM) (or the value of the `--signal` option)
164
- * if that doesn't work after 4 seconds, send SIGINT (aka control-C)
250
+ * if that doesn't work after 2 seconds, send SIGINT (aka control-C)
165
251
  * if that doesn't work after 2 more seconds, send SIGKILL (aka kill -9)
166
252
 
167
253
  This seems like the most gentle and unixy way of doing things, but it does
168
- mean that if your program ignores SIGTERM, it takes an extra 4 to 6 seconds to
254
+ mean that if your program ignores SIGTERM, it takes an extra 2 to 4 seconds to
169
255
  restart.
170
256
 
257
+ If you want to use your own series of signals, use the `--signal` option. If you want to change the delay before attempting the next signal, use the `--wait` option.
258
+
259
+ # Vagrant and VirtualBox
260
+
261
+ If running inside a shared directory using Vagrant and VirtualBox, you must pass the `--force-polling` option. You may also have to pass some extra `--ignore` options too; otherwise each scan can take 10 or more seconds on directories with a large number of files or subdirectories underneath it.
262
+
263
+ # Troubleshooting
264
+
265
+ ## zsh ##
266
+
267
+ If you are using `zsh` as your shell, and you are specifying your `--pattern` as `**/*.rb`, you may face this error
268
+ ```
269
+ Errno::EACCES: Permission denied - <filename>
270
+ ```
271
+ This is because `**/*.rb` gets expanded into the command by `zsh` instead of passing it through to rerun. The solution is to simply quote ('' or "") the pattern.
272
+ i.e
273
+ ```
274
+ rerun -p **/*.rb rake test
275
+ ```
276
+ becomes
277
+ ```
278
+ rerun -p "**/*.rb" rake test
279
+ ```
280
+
171
281
  # To Do:
172
282
 
173
- * Cooldown (so if a dozen files appear in a burst, say from 'git pull', it only restarts once)
283
+ ## Must have for v1.0
284
+ * Make sure to pass through quoted options correctly to target process [bug]
285
+ * Optionally do "bundle install" before and "bundle exec" during launch
286
+
287
+ ## Nice to have
288
+ * ".rerun" file in $HOME
174
289
  * If the last element of the command is a `.ru` file and there's no other command then use `rackup`
175
- * --ignore pattern (currently we're using Listen's default list plus dotfiles)
176
- * ".rerun" file to specify options per project or in $HOME.
177
290
  * Figure out an algorithm so "-x" is not needed (if possible) -- maybe by accepting a "--port" option or reading `config.ru`
178
291
  * Specify (or deduce) port to listen for to determine success of a web server launch
179
- * Make sure to pass through quoted options correctly to target process [bug]
180
- * Make it work on Windows, like Guard now does. See
181
- * https://github.com/guard/guard/issues/59
182
- * https://github.com/guard/guard/issues/27
183
- * Optionally do "bundle install" before and "bundle exec" during launch
292
+ * see also [todo.md](todo.md)
293
+
294
+ ## Wacky Ideas
295
+
184
296
  * On OS X:
185
297
  * use a C library using growl's developer API <http://growl.info/developer/>
186
298
  * Use growl's AppleScript or SDK instead of relying on growlnotify
187
- * Use OS X notifications
188
299
  * "Failed" icon for notifications
189
- * On Linux:
190
- * Test on Linux.
191
- * Use libnotify or notify-send http://www.linuxjournal.com/content/tech-tip-get-notifications-your-scripts-notify-send
192
300
 
193
301
  # Other projects that do similar things
194
302
 
303
+ * Guard: <http://github.com/guard/guard>
195
304
  * Restartomatic: <http://github.com/adammck/restartomatic>
196
305
  * Shotgun: <http://github.com/rtomayko/shotgun>
197
306
  * Rack::Reloader middleware: <http://github.com/rack/rack/blob/5ca8f82fb59f0bf0e8fd438e8e91c5acf3d98e44/lib/rack/reloader.rb>
198
307
  * The Sinatra FAQ has a discussion at <http://www.sinatrarb.com/faq.html#reloading>
199
308
  * Kicker: <http://github.com/alloy/kicker/>
200
309
  * Watchr: <https://github.com/mynyml/watchr>
201
- * Guard: <http://github.com/guard/guard>
202
310
  * Autotest: <https://github.com/grosser/autotest>
203
311
 
204
312
  # Why would I use this instead of Shotgun?
@@ -279,14 +387,53 @@ Based upon and/or inspired by:
279
387
  * <https://github.com/FND>
280
388
  * Barry Sia <https://github.com/bsia>
281
389
  * Paul Rangel <https://github.com/ismell>
390
+ * James Edward Gray II <https://github.com/JEG2>
391
+ * Raul E Rangel <https://github.com/ismell> and Antonio Terceiro <https://github.com/terceiro>
392
+ * Mike Pastore <https://github.com/mwpastore>
393
+ * Andy Duncan <https://github.com/ajduncan>
394
+ * Brent Van Minnen
395
+ * Matthew O'Riordan <https://github.com/mattheworiordan>
396
+ * Antonio Terceiro <https://github.com/terceiro>
397
+ * <https://github.com/mattbrictson>
398
+ * <https://github.com/krissi>
282
399
 
283
400
  # Version History
284
401
 
285
- * v0.9.0
402
+ *
403
+ * --no-ignore-dotfiles option
404
+
405
+ * v0.13.0 26 January 2018
406
+ * bugfix: pause/unpause works again (thanks Barry!)
407
+ * `.rerun` config file
408
+
409
+ * v0.12.0 23 January 2018
410
+ * smarter `--signal` option, allowing you to specify a series of signals to try in order; also `--wait` to change how long between tries
411
+ * `--force-polling` option (thanks ajduncan)
412
+ * `f` key to force stop and start (thanks mwpastore)
413
+ * add `.c` and `.h` files to default ignore list
414
+ * support for Windows
415
+ * use `Kernel.spawn` instead of `fork`
416
+ * use `wdm` gem for Windows Directory Monitor
417
+ * support for notifications on GNU/Linux using [notify-send](http://www.linuxjournal.com/content/tech-tip-get-notifications-your-scripts-notify-send) (thanks terceiro)
418
+ * fix `Gem::LoadError - terminal-notifier is not part of the bundle` [bug](https://github.com/alexch/rerun/issues/108) (thanks mattheworiordan)
419
+
420
+ * 0.11.0 7 October 2015
421
+ * better 'changed' message
422
+ * `--notify osx` option
423
+ * `--restart` option (with bugfix by Mike Pastore)
424
+ * use Listen 3 gem
425
+ * add `.feature` files to default watchlist (thanks @jmuheim)
426
+
427
+ * v0.10.0 4 May 2014
428
+ * add '.coffee,.slim,.md' to default pattern (thanks @xylinq)
429
+ * --ignore option
430
+
431
+ * v0.9.0 6 March 2014
286
432
  * --dir (or -d) can be specified more than once, for multiple directories (thanks again Barry!)
287
433
  * --name option
288
434
  * press 'p' to pause/unpause filesystem watching (Barry is the man!)
289
435
  * works with Listen 2 (note: needs 2.3 or higher)
436
+ * cooldown works, thanks to patches to underlying Listen gem
290
437
  * ignore all dotfiles, and add actual list of ignored dirs and files
291
438
 
292
439
  * v0.8.2
data/Rakefile CHANGED
@@ -32,7 +32,7 @@ end
32
32
  desc 'Exit if git is dirty'
33
33
  task :check_git do
34
34
  state = `git status 2> /dev/null | tail -n1`
35
- clean = (state =~ /working directory clean/)
35
+ clean = (state =~ /working (directory|tree) clean/)
36
36
  unless clean
37
37
  warn "can't do that on an unclean git dir"
38
38
  exit 1
@@ -73,3 +73,10 @@ task 'release' => [:check_git, package('.gem'), package('.tar.gz')] do |t|
73
73
  sh "git tag v#{$spec.version}"
74
74
  sh "git push && git push --tags"
75
75
  end
76
+
77
+ desc 'download github issues and pull requests'
78
+ task 'github' do
79
+ %w(issues pulls).each do |type|
80
+ sh "curl -o #{type}.json https://api.github.com/repos/alexch/rerun/#{type}"
81
+ end
82
+ end
data/bin/rerun CHANGED
@@ -7,6 +7,12 @@ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
7
7
  require 'rerun'
8
8
  require 'optparse'
9
9
 
10
- options = Rerun::Options.parse
11
- exit if options.nil?
12
- runner = Rerun::Runner.keep_running(options[:cmd], options)
10
+ options = Rerun::Options.parse config_file: ".rerun"
11
+
12
+ if options and options[:verbose]
13
+ puts "\nrerun options:\n\t#{options}"
14
+ end
15
+
16
+ exit if options.nil? or options[:cmd].nil? or options[:cmd].empty?
17
+
18
+ Rerun::Runner.keep_running(options[:cmd], options)
@@ -0,0 +1,3 @@
1
+ goooo
2
+ goooo
3
+ goooo
@@ -4,13 +4,12 @@ $: << here unless $:.include?(here)
4
4
  require "listen" # pull in the Listen gem
5
5
  require "rerun/options"
6
6
  require "rerun/system"
7
+ require "rerun/notification"
7
8
  require "rerun/runner"
8
9
  require "rerun/watcher"
9
10
  require "rerun/glob"
10
11
 
11
12
  module Rerun
12
13
 
13
- DEFAULT_PATTERN = "**/*.{rb,js,css,scss,sass,erb,html,haml,ru}"
14
-
15
14
  end
16
15
 
@@ -0,0 +1,82 @@
1
+ # todo: unit tests
2
+
3
+ module Rerun
4
+ class Notification
5
+ include System
6
+
7
+ attr_reader :title, :body, :options
8
+
9
+ def initialize(title, body, options = Options::DEFAULTS.dup)
10
+ @title = title
11
+ @body = body
12
+ @options = options
13
+ end
14
+
15
+ def command
16
+ # todo: strategy or subclass
17
+
18
+ s = nil
19
+
20
+ if options[:notify] == true or options[:notify] == "growl"
21
+ if (cmd = command_named("growlnotify"))
22
+ # todo: check version of growlnotify and warn if it's too old
23
+ icon_str = ("--image \"#{icon}\"" if icon)
24
+ s = "#{cmd} -n \"#{app_name}\" -m \"#{body}\" \"#{app_name} #{title}\" #{icon_str}"
25
+ end
26
+ end
27
+
28
+ if s.nil? and options[:notify] == true or options[:notify] == "osx"
29
+ if (cmd = command_named("terminal-notifier"))
30
+ icon_str = ("-appIcon \"#{icon}\"" if icon)
31
+ s = "#{cmd} -title \"#{app_name}\" -message \"#{body}\" \"#{app_name} #{title}\" #{icon_str}"
32
+ end
33
+ end
34
+
35
+ if s.nil? and options[:notify] == true or options[:notify] == "notify-send"
36
+ if (cmd = command_named('notify-send'))
37
+ icon_str = "--icon #{icon}" if icon
38
+ s = "#{cmd} -t 500 --hint=int:transient:1 #{icon_str} \"#{app_name}: #{title}\" \"#{body}\""
39
+ end
40
+ end
41
+
42
+ s
43
+ end
44
+
45
+ def command_named(name)
46
+ which_command = windows? ? 'where.exe %{cmd}' : 'which %{cmd} 2> /dev/null'
47
+ # TODO: remove 'INFO' error message
48
+ path = `#{which_command % {cmd: name}}`.chomp
49
+ path.empty? ? nil : path
50
+ end
51
+
52
+ def send(background = true)
53
+ return unless command
54
+ with_clean_env do
55
+ `#{command}#{" &" if background}`
56
+ end
57
+ end
58
+
59
+ def app_name
60
+ options[:name]
61
+ end
62
+
63
+ def icon
64
+ "#{icon_dir}/rails_red_sml.png" if rails?
65
+ end
66
+
67
+ def icon_dir
68
+ here = File.expand_path(File.dirname(__FILE__))
69
+ File.expand_path("#{here}/../../icons")
70
+ end
71
+
72
+ def with_clean_env
73
+ if defined?(Bundler)
74
+ Bundler.with_clean_env do
75
+ yield
76
+ end
77
+ else
78
+ yield
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,5 +1,7 @@
1
1
  require 'optparse'
2
2
  require 'pathname'
3
+ require 'rerun/watcher'
4
+ require 'rerun/system'
3
5
 
4
6
  libdir = "#{File.expand_path(File.dirname(File.dirname(__FILE__)))}"
5
7
 
@@ -7,86 +9,138 @@ $spec = Gem::Specification.load(File.join(libdir, "..", "rerun.gemspec"))
7
9
 
8
10
  module Rerun
9
11
  class Options
10
- DEFAULT_PATTERN = "**/*.{rb,js,css,scss,sass,erb,html,haml,ru}"
12
+
13
+ extend Rerun::System
14
+
15
+ # If you change the default pattern, please update the README.md file -- the list appears twice therein, which at the time of this comment are lines 17 and 119
16
+ DEFAULT_PATTERN = "**/*.{rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h}"
11
17
  DEFAULT_DIRS = ["."]
12
18
 
13
19
  DEFAULTS = {
14
- :pattern => DEFAULT_PATTERN,
15
- :signal => "TERM",
16
- :growl => true,
17
- :name => Pathname.getwd.basename.to_s.capitalize
20
+ :background => false,
21
+ :dir => DEFAULT_DIRS,
22
+ :force_polling => false,
23
+ :ignore => [],
24
+ :ignore_dotfiles => true,
25
+ :name => Pathname.getwd.basename.to_s.capitalize,
26
+ :notify => true,
27
+ :pattern => DEFAULT_PATTERN,
28
+ :quiet => false,
29
+ :signal => (windows? ? "TERM,KILL" : "TERM,INT,KILL"),
30
+ :verbose => false,
31
+ :wait => 2,
18
32
  }
19
33
 
20
- def self.parse args = ARGV
21
- options = DEFAULTS.dup
22
- opts = OptionParser.new("", 24, ' ') do |opts|
23
- opts.banner = "Usage: rerun [options] [--] cmd"
34
+ def self.parse args: ARGV, config_file: nil
24
35
 
25
- opts.separator ""
26
- opts.separator "Launches an app, and restarts it when the filesystem changes."
27
- opts.separator "See http://github.com/alexch/rerun for more info."
28
- opts.separator "Version: #{$spec.version}"
29
- opts.separator ""
30
- opts.separator "Options:"
36
+ default_options = DEFAULTS.dup
37
+ options = {
38
+ ignore: []
39
+ }
40
+
41
+ if config_file && File.exist?(config_file)
42
+ require 'shellwords'
43
+ config_args = File.read(config_file).shellsplit
44
+ args = config_args + args
45
+ end
31
46
 
32
- opts.on("-d dir", "--dir dir", "directory to watch, default = \"#{DEFAULT_DIRS}\". Specify multiple paths with ',' or separate '-d dir' option pairs.") do |dir|
47
+ option_parser = OptionParser.new("", 24, ' ') do |o|
48
+ o.banner = "Usage: rerun [options] [--] cmd"
49
+
50
+ o.separator ""
51
+ o.separator "Launches an app, and restarts it when the filesystem changes."
52
+ o.separator "See http://github.com/alexch/rerun for more info."
53
+ o.separator "Version: #{$spec.version}"
54
+ o.separator ""
55
+ o.separator "Options:"
56
+
57
+ o.on("-d dir", "--dir dir", "directory to watch, default = \"#{DEFAULT_DIRS}\". Specify multiple paths with ',' or separate '-d dir' option pairs.") do |dir|
33
58
  elements = dir.split(",")
34
59
  options[:dir] = (options[:dir] || []) + elements
35
60
  end
36
61
 
37
- opts.on("-p pattern", "--pattern pattern", "file glob, default = \"#{DEFAULTS[:pattern]}\"") do |pattern|
62
+ # todo: rename to "--watch"
63
+ o.on("-p pattern", "--pattern pattern", "file glob to watch, default = \"#{DEFAULTS[:pattern]}\"") do |pattern|
38
64
  options[:pattern] = pattern
39
65
  end
40
66
 
41
- opts.on("-s", "--signal signal", "terminate process using this signal, default = \"#{DEFAULTS[:signal]}\"") do |signal|
67
+ o.on("-i pattern", "--ignore pattern", "file glob(s) to ignore. Can be set many times. To ignore a directory, you must append '/*' e.g. --ignore 'coverage/*' . Globs do not match dotfiles by default.") do |pattern|
68
+ options[:ignore] += [pattern]
69
+ end
70
+
71
+ o.on("--[no-]ignore-dotfiles", "by default, file globs do not match files that begin with a dot. Setting --no-ignore-dotfiles allows you to monitor a relevant file like .env, but you may also have to explicitly --ignore more dotfiles and dotdirs.") do |value|
72
+ options[:ignore_dotfiles] = value
73
+ end
74
+
75
+ o.on("-s signal", "--signal signal", "terminate process using this signal. To try several signals in series, use a comma-delimited list. Default: \"#{DEFAULTS[:signal]}\"") do |signal|
42
76
  options[:signal] = signal
43
77
  end
44
78
 
45
- opts.on("-c", "--clear", "clear screen before each run") do
46
- options[:clear] = true
79
+ o.on("-w sec", "--wait sec", "after asking the process to terminate, wait this long (in seconds) before either aborting, or trying the next signal in series. Default: #{DEFAULTS[:wait]} sec")
80
+
81
+ o.on("-r", "--restart", "expect process to restart itself, so just send a signal and continue watching. Sends the HUP signal unless overridden using --signal") do |signal|
82
+ options[:restart] = true
83
+ default_options[:signal] = "HUP"
47
84
  end
48
85
 
49
- opts.on("-x", "--exit", "expect the program to exit. With this option, rerun checks the return value; without it, rerun checks that the process is running.") do |dir|
50
- options[:exit] = true
86
+ o.on("-x", "--exit", "expect the program to exit. With this option, rerun checks the return value; without it, rerun checks that the process is running.") do |value|
87
+ options[:exit] = value
51
88
  end
52
89
 
53
- opts.on("-b", "--background", "disable on-the-fly commands, allowing the process to be backgrounded") do
54
- options[:background] = true
90
+ o.on("-c", "--clear", "clear screen before each run") do |value|
91
+ options[:clear] = value
55
92
  end
56
93
 
57
- opts.on("-n name", "--name name", "name of app used in logs and notifications, default = \"#{DEFAULTS[:name]}\"") do |name|
94
+ o.on("-b", "--background", "disable on-the-fly keypress commands, allowing the process to be backgrounded") do |value|
95
+ options[:background] = value
96
+ end
97
+
98
+ o.on("-n name", "--name name", "name of app used in logs and notifications, default = \"#{DEFAULTS[:name]}\"") do |name|
58
99
  options[:name] = name
59
100
  end
60
101
 
61
- opts.on("--no-growl", "don't use growl") do
102
+ o.on("--[no-]force-polling", "use polling instead of a native filesystem scan (useful for Vagrant)") do |value|
103
+ options[:force_polling] = value
104
+ end
105
+
106
+ o.on("--no-growl", "don't use growl [OBSOLETE]") do
62
107
  options[:growl] = false
108
+ $stderr.puts "--no-growl is obsolete; use --no-notify instead"
109
+ return
110
+ end
111
+
112
+ o.on("--[no-]notify [notifier]", "send messages through a desktop notification application. Supports growl (requires growlnotify), osx (requires terminal-notifier gem), and notify-send on GNU/Linux (notify-send must be installed)") do |notifier|
113
+ notifier = true if notifier.nil?
114
+ options[:notify] = notifier
63
115
  end
64
116
 
65
- opts.on_tail("-h", "--help", "--usage", "show this message") do
66
- puts opts
117
+ o.on("-q", "--[no-]quiet", "don't output any logs") do |value|
118
+ options[:quiet] = value
119
+ end
120
+
121
+ o.on("--[no-]verbose", "log extra stuff like PIDs (unless you also specified `--quiet`") do |value|
122
+ options[:verbose] = value
123
+ end
124
+
125
+ o.on_tail("-h", "--help", "--usage", "show this message and immediately exit") do
126
+ puts o
67
127
  return
68
128
  end
69
129
 
70
- opts.on_tail("--version", "show version") do
130
+ o.on_tail("--version", "show version and immediately exit") do
71
131
  puts $spec.version
72
132
  return
73
133
  end
74
134
 
75
- opts.on_tail ""
76
- opts.on_tail "On top of --pattern, we ignore any changes to files and dirs starting with a dot, ending with [#{Listen::Silencer::DEFAULT_IGNORED_EXTENSIONS.join(',')}], or named [#{Listen::Silencer::DEFAULT_IGNORED_DIRECTORIES.join(',')}]."
77
-
78
135
  end
79
136
 
80
- if args.empty?
81
- puts opts
82
- nil
83
- else
84
- opts.parse! args
85
- options[:cmd] = args.join(" ")
86
- options[:dir] ||= DEFAULT_DIRS
87
- options
88
- end
89
- end
137
+ puts option_parser if args.empty?
138
+ option_parser.parse! args
139
+ options = default_options.merge(options)
140
+ options[:cmd] = args.join(" ").strip # todo: better arg word handling
90
141
 
142
+ options
143
+ end
91
144
  end
145
+
92
146
  end
@@ -4,19 +4,25 @@ require 'io/wait'
4
4
  module Rerun
5
5
  class Runner
6
6
 
7
+ # The watcher instance that wait for changes
8
+ attr_reader :watcher
9
+
7
10
  def self.keep_running(cmd, options)
8
11
  runner = new(cmd, options)
9
12
  runner.start
10
13
  runner.join
11
14
  # apparently runner doesn't keep running anymore (as of Listen 2) so we have to sleep forever :-(
12
- sleep 10000 while true # :-(
15
+ sleep 10000 while true # :-(
13
16
  end
14
17
 
15
18
  include System
19
+ include ::Timeout
16
20
 
17
21
  def initialize(run_command, options = {})
18
22
  @run_command, @options = run_command, options
19
23
  @run_command = "ruby #{@run_command}" if @run_command.split(' ').first =~ /\.rb$/
24
+ @options[:directory] ||= options.delete(:dir) || '.'
25
+ @options[:ignore] ||= []
20
26
  end
21
27
 
22
28
  def start_keypress_thread
@@ -32,22 +38,26 @@ module Rerun
32
38
  when 'r'
33
39
  say "Restarting"
34
40
  restart
41
+ when 'f'
42
+ say "Stopping and starting"
43
+ restart(false)
35
44
  when 'p'
36
- toggle_pause if watcher_running?
45
+ toggle_pause
37
46
  when 'x', 'q'
38
47
  die
39
- break # the break will stop this thread, in case the 'die' doesn't
48
+ break # the break will stop this thread, in case the 'die' doesn't
40
49
  else
41
50
  puts "\n#{c.inspect} pressed inside rerun"
42
51
  puts [["c", "clear screen"],
43
- ["r", "restart"],
44
- ["p", "toggle pause"],
45
- ["x or q", "stop and exit"]
46
- ].map{|key, description| " #{key} -- #{description}"}.join("\n")
52
+ ["r", "restart"],
53
+ ["f", "forced restart (stop and start)"],
54
+ ["p", "toggle pause"],
55
+ ["x or q", "stop and exit"]
56
+ ].map {|key, description| " #{key} -- #{description}"}.join("\n")
47
57
  puts
48
58
  end
49
59
  end
50
- sleep 1 # todo: use select instead of polling somehow?
60
+ sleep 1 # todo: use select instead of polling somehow?
51
61
  end
52
62
  end
53
63
  @keypress_thread.run
@@ -58,24 +68,24 @@ module Rerun
58
68
  @keypress_thread = nil
59
69
  end
60
70
 
61
- def restart
71
+ def restart(with_signal = true)
62
72
  @restarting = true
63
- stop
64
- start
73
+ if @options[:restart] && with_signal
74
+ restart_with_signal(@options[:signal])
75
+ else
76
+ stop
77
+ start
78
+ end
65
79
  @restarting = false
66
80
  end
67
81
 
68
- def watcher_running?
69
- @watcher && @watcher.running?
70
- end
71
-
72
82
  def toggle_pause
73
83
  unless @pausing
74
84
  say "Pausing. Press 'p' again to resume."
75
85
  @watcher.pause
76
86
  @pausing = true
77
87
  else
78
- say "Resuming"
88
+ say "Resuming."
79
89
  @watcher.unpause
80
90
  @pausing = false
81
91
  end
@@ -86,11 +96,7 @@ module Rerun
86
96
  end
87
97
 
88
98
  def dir
89
- @options[:dir]
90
- end
91
-
92
- def dirs
93
- @options[:dir] || "."
99
+ @options[:directory]
94
100
  end
95
101
 
96
102
  def pattern
@@ -101,6 +107,14 @@ module Rerun
101
107
  @options[:clear]
102
108
  end
103
109
 
110
+ def quiet?
111
+ @options[:quiet]
112
+ end
113
+
114
+ def verbose?
115
+ @options[:verbose]
116
+ end
117
+
104
118
  def exit?
105
119
  @options[:exit]
106
120
  end
@@ -109,19 +123,19 @@ module Rerun
109
123
  @options[:name]
110
124
  end
111
125
 
112
- def start
113
- if windows?
114
- raise "Sorry, Rerun does not work on Windows."
126
+ def restart_with_signal(restart_signal)
127
+ if @pid && (@pid != 0)
128
+ notify "restarting", "We will be with you shortly."
129
+ send_signal(restart_signal)
115
130
  end
131
+ end
116
132
 
117
- if (!@already_running)
118
- taglines = [
119
- "To infinity... and beyond!",
120
- "Charge!",
121
- ]
122
- notify "launched", taglines[rand(taglines.size)]
123
- @already_running = true
124
- else
133
+ def force_polling
134
+ @options[:force_polling]
135
+ end
136
+
137
+ def start
138
+ if @already_running
125
139
  taglines = [
126
140
  "Here we go again!",
127
141
  "Keep on trucking.",
@@ -129,26 +143,33 @@ module Rerun
129
143
  "The road goes ever on and on, down from the door where it began.",
130
144
  ]
131
145
  notify "restarted", taglines[rand(taglines.size)]
146
+ else
147
+ taglines = [
148
+ "To infinity... and beyond!",
149
+ "Charge!",
150
+ ]
151
+ notify "launched", taglines[rand(taglines.size)]
152
+ @already_running = true
132
153
  end
133
154
 
134
155
  clear_screen if clear?
135
156
  start_keypress_thread unless @keypress_thread
136
157
 
137
- @pid = Kernel.fork do
138
- begin
139
- exec(@run_command)
140
- rescue => e
141
- puts "#{e.class}: #{e.message}"
142
- exit
143
- end
158
+ begin
159
+ @pid = run @run_command
160
+ say "Rerun (#{$PID}) running #{app_name} (#{@pid})"
161
+ rescue => e
162
+ puts "#{e.class}: #{e.message}"
163
+ exit
144
164
  end
165
+
145
166
  status_thread = Process.detach(@pid) # so if the child exits, it dies
146
167
 
147
168
  Signal.trap("INT") do # INT = control-C -- allows user to stop the top-level rerun process
148
169
  die
149
170
  end
150
171
 
151
- Signal.trap("TERM") do # TERM is the polite way of terminating a process
172
+ Signal.trap("TERM") do # TERM is the polite way of terminating a process
152
173
  die
153
174
  end
154
175
 
@@ -174,28 +195,47 @@ module Rerun
174
195
  end
175
196
 
176
197
  unless @watcher
177
-
178
- watcher = Watcher.new(:directory => dirs, :pattern => pattern) do |changes|
179
-
180
- message = [:modified, :added, :removed].map do |change|
181
- count = changes[change].size
182
- if count and count > 0
183
- "#{count} #{change}"
184
- end
185
- end.compact.join(", ")
198
+ watcher = Watcher.new(@options) do |changes|
199
+ message = change_message(changes)
186
200
  say "Change detected: #{message}"
187
201
  restart unless @restarting
188
202
  end
189
203
  watcher.start
190
204
  @watcher = watcher
191
- say "Watching #{dir.join(', ')} for #{pattern} using #{watcher.adapter.class.name.split('::').last} adapter"
205
+ ignore = @options[:ignore]
206
+ say "Watching #{dir.join(', ')} for #{pattern}" +
207
+ (ignore.empty? ? "" : " (ignoring #{ignore.join(',')})") +
208
+ (watcher.adapter.nil? ? "" : " with #{watcher.adapter_name} adapter")
192
209
  end
193
210
  end
194
211
 
212
+ def run command
213
+ Kernel.spawn command
214
+ end
215
+
216
+ def change_message(changes)
217
+ message = [:modified, :added, :removed].map do |change|
218
+ count = changes[change] ? changes[change].size : 0
219
+ if count > 0
220
+ "#{count} #{change}"
221
+ end
222
+ end.compact.join(", ")
223
+
224
+ changed_files = changes.values.flatten
225
+ if changed_files.count > 0
226
+ message += ": "
227
+ message += changes.values.flatten[0..3].map {|path| path.split('/').last}.join(', ')
228
+ if changed_files.count > 3
229
+ message += ", ..."
230
+ end
231
+ end
232
+ message
233
+ end
234
+
195
235
  def die
196
236
  #stop_keypress_thread # don't do this since we're probably *in* the keypress thread
197
237
  stop # stop the child process if it exists
198
- exit 0 # todo: status code param
238
+ exit 0 # todo: status code param
199
239
  end
200
240
 
201
241
  def join
@@ -203,40 +243,63 @@ module Rerun
203
243
  end
204
244
 
205
245
  def running?
206
- signal(0)
246
+ send_signal(0)
247
+ end
248
+
249
+ # Send the signal to process @pid and wait for it to die.
250
+ # @returns true if the process dies
251
+ # @returns false if either sending the signal fails or the process fails to die
252
+ def signal_and_wait(signal)
253
+
254
+ signal_sent = if windows?
255
+ force_kill = (signal == 'KILL')
256
+ system("taskkill /T #{'/F' if force_kill} /PID #{@pid}")
257
+ else
258
+ send_signal(signal)
259
+ end
260
+
261
+ if signal_sent
262
+ # the signal was successfully sent, so wait for the process to die
263
+ begin
264
+ timeout(@options[:wait]) do
265
+ Process.wait(@pid)
266
+ end
267
+ process_status = $?
268
+ say "Process ended: #{process_status}" if verbose?
269
+ true
270
+ rescue Timeout::Error
271
+ false
272
+ end
273
+ else
274
+ false
275
+ end
207
276
  end
208
277
 
209
- def signal(signal)
210
- say "Sending signal #{signal} to #{@pid}" unless signal == 0
278
+ # Send the signal to process @pid.
279
+ # @returns true if the signal is sent
280
+ # @returns false if sending the signal fails
281
+ # If sending the signal fails, the exception will be swallowed
282
+ # (and logged if verbose is true) and this method will return false.
283
+ #
284
+ def send_signal(signal)
285
+ say "Sending signal #{signal} to #{@pid}" unless signal == 0 if verbose?
211
286
  Process.kill(signal, @pid)
212
287
  true
213
- rescue
288
+ rescue => e
289
+ say "Signal #{signal} failed: #{e.class}: #{e.message}" if verbose?
214
290
  false
215
291
  end
216
292
 
217
293
  # todo: test escalation
218
294
  def stop
219
- default_signal = @options[:signal] || "TERM"
220
295
  if @pid && (@pid != 0)
221
296
  notify "stopping", "All good things must come to an end." unless @restarting
222
- begin
223
- timeout(4) do # todo: escalation timeout setting
224
- # start with a polite SIGTERM
225
- signal(default_signal) && Process.wait(@pid)
226
- end
227
- rescue Timeout::Error
228
- begin
229
- timeout(2) do
230
- # escalate to SIGINT aka control-C since some foolish process may be ignoring SIGTERM
231
- signal("INT") && Process.wait(@pid)
232
- end
233
- rescue Timeout::Error
234
- # escalate to SIGKILL aka "kill -9" which cannot be ignored
235
- signal("KILL") && Process.wait(@pid)
236
- end
297
+ @options[:signal].split(',').each do |signal|
298
+ success = signal_and_wait(signal)
299
+ return true if success
237
300
  end
238
301
  end
239
- rescue => e
302
+ rescue
240
303
  false
241
304
  end
242
305
 
@@ -251,20 +314,25 @@ module Rerun
251
314
  @git_head = File.exists?(git_head_file) && File.read(git_head_file)
252
315
  end
253
316
 
254
- def notify(title, body)
255
- growl title, body if @options[:growl]
317
+ def notify(title, body, background = true)
318
+ Notification.new(title, body, @options).send(background) if @options[:notify]
256
319
  puts
257
320
  say "#{app_name} #{title}"
258
321
  end
259
322
 
260
323
  def say msg
261
- puts "#{Time.now.strftime("%T")} [rerun] #{msg}"
324
+ puts "#{Time.now.strftime("%T")} [rerun] #{msg}" unless quiet?
325
+ end
326
+
327
+ def stty(args)
328
+ system "stty #{args}"
262
329
  end
263
330
 
264
331
  # non-blocking stdin reader.
265
332
  # returns a 1-char string if a key was pressed; otherwise nil
266
333
  #
267
334
  def key_pressed
335
+ return one_char if windows?
268
336
  begin
269
337
  # this "raw input" nonsense is because unix likes waiting for linefeeds before sending stdin
270
338
 
@@ -274,15 +342,10 @@ module Rerun
274
342
  # looks like "raw" flips off the OPOST bit 0x00000001 /* enable following output processing */
275
343
  # which disables #define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */
276
344
  # so this sets it back on again since all we care about is raw input, not raw output
277
- system("stty raw opost")
278
-
279
- c = nil
280
- if $stdin.ready?
281
- c = $stdin.getc
282
- end
283
- c.chr if c
345
+ stty "raw opost"
346
+ one_char
284
347
  ensure
285
- system "stty -raw" # turn raw input off
348
+ stty "-raw" # turn raw input off
286
349
  end
287
350
 
288
351
  # note: according to 'man tty' the proper way restore the settings is
@@ -291,7 +354,6 @@ module Rerun
291
354
  # system 'stty "#{tty_state}'
292
355
  # end
293
356
  # but this way seems fine and less confusing
294
-
295
357
  end
296
358
 
297
359
  def clear_screen
@@ -299,5 +361,14 @@ module Rerun
299
361
  $stdout.print "\033[H\033[2J"
300
362
  end
301
363
 
364
+ private
365
+ def one_char
366
+ c = nil
367
+ if $stdin.ready?
368
+ c = $stdin.getc
369
+ end
370
+ c.chr if c
371
+ end
372
+
302
373
  end
303
374
  end
@@ -6,38 +6,16 @@ module Rerun
6
6
  end
7
7
 
8
8
  def windows?
9
- RUBY_PLATFORM =~ /mswin/i
9
+ RUBY_PLATFORM =~ /(mswin|mingw32)/i
10
10
  end
11
11
 
12
12
  def linux?
13
13
  RUBY_PLATFORM =~ /linux/i
14
14
  end
15
15
 
16
- # do we have growl or not?
17
- def growl_available?
18
- mac? && (growlcmd != "")
19
- end
20
-
21
- def growlcmd
22
- growlnotify = `which growlnotify`.chomp
23
- # todo: check version of growlnotify and warn if it's too old
24
- growlnotify
25
- end
26
-
27
- def icon
28
- here = File.expand_path(File.dirname(__FILE__))
29
- icondir = File.expand_path("#{here}/../../icons")
16
+ def rails?
30
17
  rails_sig_file = File.expand_path(".")+"/config/boot.rb"
31
- "#{icondir}/rails_red_sml.png" if File.exists? rails_sig_file
32
- end
33
-
34
- def growl(title, body, background = true)
35
- if growl_available?
36
- icon_str = ("--image \"#{icon}\"" if icon)
37
- s = "#{growlcmd} -n \"#{app_name}\" -m \"#{body}\" \"#{app_name} #{title}\" #{icon_str}"
38
- s += " &" if background
39
- `#{s}`
40
- end
18
+ File.exists? rails_sig_file
41
19
  end
42
20
 
43
21
  end
@@ -14,7 +14,11 @@ module Rerun
14
14
  class Watcher
15
15
  InvalidDirectoryError = Class.new(RuntimeError)
16
16
 
17
- attr_reader :directory, :pattern, :priority
17
+ #def self.default_ignore
18
+ # Listen::Silencer.new(Listen::Listener.new).send :_default_ignore_patterns
19
+ #end
20
+
21
+ attr_reader :directory, :pattern, :priority, :ignore_dotfiles
18
22
 
19
23
  # Create a file system watcher. Start it by calling #start.
20
24
  #
@@ -29,12 +33,16 @@ module Rerun
29
33
  :directory => ".",
30
34
  :pattern => "**/*",
31
35
  :priority => 0,
36
+ :ignore_dotfiles => true,
32
37
  }.merge(options)
33
38
 
34
39
  @pattern = options[:pattern]
35
40
  @directories = options[:directory]
36
41
  @directories = sanitize_dirs(@directories)
37
42
  @priority = options[:priority]
43
+ @force_polling = options[:force_polling]
44
+ @ignore = [options[:ignore]].flatten.compact
45
+ @ignore_dotfiles = options[:ignore_dotfiles]
38
46
  @thread = nil
39
47
  end
40
48
 
@@ -55,10 +63,9 @@ module Rerun
55
63
  end
56
64
 
57
65
  @thread = Thread.new do
58
- regexp = Rerun::Glob.new(@pattern).to_regexp
59
- dotfiles = /^\.[^.]/ # at beginning of string, a real dot followed by any other character
60
- @listener = Listen.to(*@directories, only: regexp, ignore: dotfiles, wait_for_delay: 1) do |modified, added, removed|
61
- if((modified.size + added.size + removed.size) > 0)
66
+ @listener = Listen.to(*@directories, only: watching, ignore: ignoring, wait_for_delay: 1, force_polling: @force_polling) do |modified, added, removed|
67
+ count = modified.size + added.size + removed.size
68
+ if count > 0
62
69
  @client_callback.call(:modified => modified, :added => added, :removed => removed)
63
70
  end
64
71
  end
@@ -72,11 +79,16 @@ module Rerun
72
79
  at_exit { stop } # try really hard to clean up after ourselves
73
80
  end
74
81
 
75
- def adapter
76
- @listener.registry[:adapter] || (timeout(4) do
77
- sleep 1 until adapter = @listener.registry[:adapter]
78
- adapter
79
- end)
82
+ def watching
83
+ Rerun::Glob.new(@pattern).to_regexp
84
+ end
85
+
86
+ def ignoring
87
+ patterns = []
88
+ if ignore_dotfiles
89
+ patterns << /^\.[^.]/ # at beginning of string, a real dot followed by any other character
90
+ end
91
+ patterns + @ignore.map { |x| Rerun::Glob.new(x).to_regexp }
80
92
  end
81
93
 
82
94
  # kill the file watcher thread
@@ -93,7 +105,7 @@ module Rerun
93
105
  # wait for the file watcher to finish
94
106
  def join
95
107
  @thread.join if @thread
96
- rescue Interrupt => e
108
+ rescue Interrupt
97
109
  # don't care
98
110
  end
99
111
 
@@ -102,12 +114,21 @@ module Rerun
102
114
  end
103
115
 
104
116
  def unpause
105
- @listener.unpause if @listener
117
+ @listener.start if @listener
106
118
  end
107
119
 
108
120
  def running?
109
- @listener && @listener.instance_variable_get(:@adapter)
121
+ @listener && @listener.processing?
110
122
  end
111
123
 
124
+ def adapter
125
+ @listener &&
126
+ (backend = @listener.instance_variable_get(:@backend)) &&
127
+ backend.instance_variable_get(:@adapter)
128
+ end
129
+
130
+ def adapter_name
131
+ adapter && adapter.class.name.split('::').last
132
+ end
112
133
  end
113
134
  end
@@ -3,7 +3,7 @@ $spec = Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'rerun'
6
- s.version = '0.9.0'
6
+ s.version = '0.13.1'
7
7
 
8
8
  s.description = "Restarts your app when a file changes. A no-frills, command-line alternative to Guard, Shotgun, Autotest, etc."
9
9
  s.summary = "Launches an app, and restarts it whenever the filesystem changes. A no-frills, command-line alternative to Guard, Shotgun, Autotest, etc."
@@ -25,7 +25,7 @@ $spec = Gem::Specification.new do |s|
25
25
 
26
26
  s.extra_rdoc_files = %w[README.md]
27
27
 
28
- s.add_dependency 'listen', '~> 2.7'
28
+ s.add_runtime_dependency 'listen', '~> 3.0'
29
29
 
30
30
  s.homepage = "http://github.com/alexch/rerun/"
31
31
  s.require_paths = %w[lib]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rerun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Chaffee
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-06 00:00:00.000000000 Z
11
+ date: 2020-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: listen
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.7'
19
+ version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.7'
26
+ version: '3.0'
27
27
  description: Restarts your app when a file changes. A no-frills, command-line alternative
28
28
  to Guard, Shotgun, Autotest, etc.
29
29
  email: alex@stinky.com
@@ -39,8 +39,10 @@ files:
39
39
  - bin/rerun
40
40
  - icons/rails_grn_sml.png
41
41
  - icons/rails_red_sml.png
42
+ - lib/goo.rb
42
43
  - lib/rerun.rb
43
44
  - lib/rerun/glob.rb
45
+ - lib/rerun/notification.rb
44
46
  - lib/rerun/options.rb
45
47
  - lib/rerun/runner.rb
46
48
  - lib/rerun/system.rb
@@ -50,7 +52,7 @@ homepage: http://github.com/alexch/rerun/
50
52
  licenses:
51
53
  - MIT
52
54
  metadata: {}
53
- post_install_message:
55
+ post_install_message:
54
56
  rdoc_options: []
55
57
  require_paths:
56
58
  - lib
@@ -65,9 +67,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
67
  - !ruby/object:Gem::Version
66
68
  version: '0'
67
69
  requirements: []
68
- rubyforge_project:
69
- rubygems_version: 2.2.2
70
- signing_key:
70
+ rubygems_version: 3.1.2
71
+ signing_key:
71
72
  specification_version: 2
72
73
  summary: Launches an app, and restarts it whenever the filesystem changes. A no-frills,
73
74
  command-line alternative to Guard, Shotgun, Autotest, etc.