rerun 0.9.0 → 0.13.1

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 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.