alexch-rerun 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ Rerun
2
+ Copyright (c) 2009 Alex Chaffee <alex@stinky.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to
6
+ deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
+ sell copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+
21
+ ---
22
+
23
+ rerun partially based on code from Rspactor
24
+ Copyright (c) 2009 Mislav Marohnić
25
+ License as above (MIT open source).
26
+
27
+ rerun partially based on code from FileSystemWatcher
28
+ http://paulhorman.com/filesystemwatcher/
29
+ No license provided; assumed public domain.
30
+
31
+ rerun partially based on code from Shotgun
32
+ Copyright (c) 2009 Ryan Tomayko <tomayko.com/about>
33
+ License as above (MIT open source).
34
+
@@ -0,0 +1,109 @@
1
+ # Rerun
2
+
3
+ <http://github.com/alexch/rerun>
4
+
5
+ Launches your app, then watches the filesystem. If a relevant file
6
+ changes, then it restarts your app.
7
+
8
+ Currently only *.rb files are watched, anywhere under the current
9
+ directory (.). This is pretty lame so it will change soon.
10
+
11
+ If you're on Mac OS X, it uses the built-in facilities for monitoring
12
+ the filesystem, so CPU use is very light.
13
+
14
+ If you have "growlcmd" available on the PATH, it sends notifications
15
+ to growl in addition to the console.
16
+
17
+ # Installation:
18
+
19
+ sudo gem install rerun
20
+
21
+ If you want to use the latest version, grab it off Github:
22
+
23
+ gem sources -a http://gems.github.com/
24
+ sudo gem install alexch-rerun
25
+
26
+ I'll bump the version on Github for release candidates, and deploy to
27
+ Rubyforge only when it's had some time to bake.
28
+
29
+ # Usage:
30
+
31
+ rerun [options] cmd
32
+
33
+ For example, if you're running a Sinatra app whose main file is
34
+ app.rb:
35
+
36
+ rerun app.rb
37
+
38
+ Or if you're running a Rack app that's configured in config.ru
39
+ but you want it on port 4000 and in debug mode:
40
+
41
+ rerun "thin start --debug --port=4000 -R config.ru"
42
+
43
+ # Options:
44
+
45
+ Only --version and --help so far.
46
+
47
+ # To Do:
48
+
49
+ * If the cmd is, or starts with, a ".rb" file, then run it with ruby
50
+ * Allow arbitrary sets of directories and file types, possibly with "include" and "exclude" sets
51
+ * ".rerun" file to specify options per project or in $HOME.
52
+ * Test on Windows and Linux.
53
+
54
+ # Other projects that do similar things
55
+
56
+ Restartomatic: <http://github.com/adammck/restartomatic>
57
+
58
+ Shotgun: <http://github.com/rtomayko/shotgun>
59
+
60
+ Rack::Reloader middleware: <http://github.com/rack/rack/blob/5ca8f82fb59f0bf0e8fd438e8e91c5acf3d98e44/lib/rack/reloader.rb>
61
+
62
+ # Why would I use this instead of Shotgun?
63
+
64
+ Shotgun does a "fork" after the web framework has loaded but before
65
+ your application is loaded. It then loads your app, processes a
66
+ single request in the child process, then exits the child process.
67
+
68
+ Rerun launches the whole app, then when it's time to restart, uses
69
+ "kill" to shut it down and starts the whole thing up again from
70
+ scratch.
71
+
72
+ So rerun takes somewhat longer than Shotgun to restart the app, but
73
+ does it much less frequently. And once it's running it behaves more
74
+ normally and consistently with your production app.
75
+
76
+ Also, Shotgun reloads the app on every request, even if it doesn't
77
+ need to. This is fine if you're loading a single file, but my web
78
+ pages all load other files (CSS, JS, media) and that adds up quickly.
79
+ The developers of shotgun are probably using caching or a front web
80
+ server so this doesn't affect them too much.
81
+
82
+ YMMV!
83
+
84
+ # Why did you write this?
85
+
86
+ I've been using [Sinatra](http://sinatrarb.com) and loving it. In order
87
+ to simplify their system, the Rat Pack just removed auto-reloading from
88
+ Sinatra proper. I approve of this: a web application framework should be
89
+ focused on serving requests, not on munging Ruby ObjectSpace for
90
+ dev-time convenience. But I still wanted automatic reloading during
91
+ development. Shotgun wasn't working for me (see above) so I spliced
92
+ Rerun together out of code from Rspactor, FileSystemWatcher, and Shotgun
93
+ -- with a heavy amount of refactoring and rewriting.
94
+
95
+ # Credits
96
+
97
+ Rerun: Alex Chaffee, <mailto:alex@stinky.com>, <http://github.com/alexch/>
98
+
99
+ Based upon and/or inspired by:
100
+
101
+ Shotgun: <http://github.com/rtomayko/shotgun>
102
+
103
+ Rspactor: <http://github.com/mislav/rspactor>
104
+
105
+ FileSystemWatcher: <http://paulhorman.com/filesystemwatcher/>
106
+
107
+ # License
108
+
109
+ Open Source MIT License. See "LICENSE" file.
@@ -0,0 +1,64 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'spec/rake/spectask'
5
+
6
+ task :default => [:spec]
7
+ task :test => :spec
8
+
9
+ desc "Run all specs"
10
+ Spec::Rake::SpecTask.new('spec') do |t|
11
+ ENV['ENV'] = "test"
12
+ t.spec_files = FileList['spec/**/*_spec.rb']
13
+ t.ruby_opts = ['-rubygems'] if defined? Gem
14
+ end
15
+
16
+ $rubyforge_project = 'pivotalrb'
17
+
18
+ $spec =
19
+ begin
20
+ require 'rubygems/specification'
21
+ data = File.read('rerun.gemspec')
22
+ spec = nil
23
+ Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
24
+ spec
25
+ end
26
+
27
+ def package(ext='')
28
+ "pkg/#{$spec.name}-#{$spec.version}" + ext
29
+ end
30
+
31
+ desc 'Build packages'
32
+ task :package => %w[.gem .tar.gz].map { |e| package(e) }
33
+
34
+ desc 'Build and install as local gem'
35
+ task :install => package('.gem') do
36
+ sh "gem install #{package('.gem')}"
37
+ end
38
+
39
+ directory 'pkg/'
40
+ CLOBBER.include('pkg')
41
+
42
+ file package('.gem') => %W[pkg/ #{$spec.name}.gemspec] + $spec.files do |f|
43
+ sh "gem build #{$spec.name}.gemspec"
44
+ mv File.basename(f.name), f.name
45
+ end
46
+
47
+ file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
48
+ cmd = <<-SH
49
+ git archive \
50
+ --prefix=#{$spec.name}-#{$spec.version}/ \
51
+ --format=tar \
52
+ HEAD | gzip > #{f.name}
53
+ SH
54
+ sh cmd.gsub(/ +/, ' ')
55
+ end
56
+
57
+ desc 'Publish gem and tarball to rubyforge'
58
+ task 'release' => [package('.gem'), package('.tar.gz')] do |t|
59
+ sh <<-end
60
+ rubyforge add_release #{$rubyforge_project} #{$spec.name} #{$spec.version} #{package('.gem')} &&
61
+ rubyforge add_file #{$rubyforge_project} #{$spec.name} #{$spec.version} #{package('.tar.gz')}
62
+ end
63
+ end
64
+ 5
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ libdir = "#{File.expand_path(File.dirname(File.dirname(__FILE__)))}/lib"
6
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
7
+
8
+ load "#{libdir}/../rerun.gemspec" # defines "$spec" variable, which we read the version from
9
+
10
+ require 'optparse'
11
+
12
+ options = {}
13
+
14
+ opts = OptionParser.new("", 24, ' ') { |opts|
15
+ opts.banner = "Usage: rerun cmd"
16
+
17
+ opts.separator ""
18
+ opts.separator "Launches an app, and restarts it when the filesystem changes."
19
+ opts.separator "See http://github.com/alexch/rerun for more info."
20
+ opts.separator ""
21
+ opts.separator "Options:"
22
+
23
+ opts.on_tail("-h", "--help", "--usage", "show this message") do
24
+ puts opts
25
+ exit
26
+ end
27
+
28
+ opts.on_tail("--version", "show version") do
29
+ puts $spec.version
30
+ exit
31
+ end
32
+
33
+ opts.parse! ARGV
34
+ }
35
+
36
+ #config = ARGV[0] || "config.ru"
37
+ #abort "configuration #{config} not found" unless File.exist? config
38
+ #
39
+ #if config =~ /\.ru$/ && File.read(config)[/^#\\(.*)/]
40
+ # opts.parse! $1.split(/\s+/)
41
+ #end
42
+
43
+ if ARGV.empty?
44
+ puts opts
45
+ exit
46
+ end
47
+
48
+ require 'rerun'
49
+ cmd = ARGV.join(" ")
50
+ runner = Rerun::Runner.new(cmd, options)
51
+ runner.start
52
+ runner.join
@@ -0,0 +1,6 @@
1
+ require 'watcher'
2
+
3
+ module Rerun
4
+ class FSWatcher < Watcher
5
+ end
6
+ end
@@ -0,0 +1,105 @@
1
+ require "system"
2
+ require "watcher"
3
+
4
+ begin
5
+ require 'osx/foundation'
6
+ OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
7
+ rescue MissingSourceFile
8
+ # this is to not fail when running on a non-Mac
9
+ end
10
+
11
+ # stolen from RSpactor, http://github.com/mislav/rspactor
12
+ # based on http://rails.aizatto.com/2007/11/28/taming-the-autotest-beast-with-fsevents/
13
+
14
+ #TODO: make it notice deleted files
15
+ require "watcher"
16
+ module Rerun
17
+ class OSXWatcher < Rerun::Watcher
18
+ attr_reader :last_check, :valid_extensions
19
+ attr_reader :stream
20
+
21
+ def start
22
+ prime
23
+ timestamp_checked
24
+
25
+ dirs = Array(directories.map{|d| d.dir})
26
+
27
+ mac_callback = lambda do |stream, ctx, num_events, paths, marks, event_ids|
28
+ examine
29
+ # changed_files = extract_changed_files_from_paths(split_paths(paths, num_events))
30
+ # timestamp_checked
31
+ # puts "changed files:"
32
+ # p changed_files
33
+ # yield changed_files unless changed_files.empty?
34
+ end
35
+
36
+ @stream = OSX::FSEventStreamCreate(OSX::KCFAllocatorDefault, mac_callback, nil, dirs, OSX::KFSEventStreamEventIdSinceNow, @sleep_time, 0)
37
+ raise "Failed to create stream" unless stream
38
+
39
+ OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode)
40
+ unless OSX::FSEventStreamStart(stream)
41
+ raise "Failed to start stream"
42
+ end
43
+
44
+ @thread = Thread.new do
45
+ begin
46
+ OSX::CFRunLoopRun()
47
+ rescue Interrupt
48
+ OSX::FSEventStreamStop(stream)
49
+ OSX::FSEventStreamInvalidate(stream)
50
+ OSX::FSEventStreamRelease(stream)
51
+ @stream = nil
52
+ end
53
+ end
54
+
55
+ @thread.priority = @priority
56
+ end
57
+
58
+ def stop
59
+ @thread.kill
60
+ end
61
+
62
+ def timestamp_checked
63
+ @last_check = Time.now
64
+ end
65
+
66
+ def split_paths(paths, num_events)
67
+ paths.regard_as('*')
68
+ rpaths = []
69
+ num_events.times { |i| rpaths << paths[i] }
70
+ rpaths
71
+ end
72
+
73
+ def extract_changed_files_from_paths(paths)
74
+ changed_files = []
75
+ paths.each do |path|
76
+ next if ignore_path?(path)
77
+ Dir.glob(path + "*").each do |file|
78
+ next if ignore_file?(file)
79
+ changed_files << file if file_changed?(file)
80
+ end
81
+ end
82
+ changed_files
83
+ end
84
+
85
+ def file_changed?(file)
86
+ File.stat(file).mtime > last_check
87
+ end
88
+
89
+ def ignore_path?(path)
90
+ path =~ /(?:^|\/)\.(git|svn)/
91
+ end
92
+
93
+ def ignore_file?(file)
94
+ File.basename(file).index('.') == 0 or not valid_extension?(file)
95
+ end
96
+
97
+ def file_extension(file)
98
+ file =~ /\.(\w+)$/ and $1
99
+ end
100
+
101
+ def valid_extension?(file)
102
+ valid_extensions.nil? or valid_extensions.include?(file_extension(file))
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,121 @@
1
+ require "system"
2
+ require "watcher"
3
+ require "osxwatcher"
4
+ require "fswatcher"
5
+
6
+ # todo: make this work in non-Mac and non-Unix environments (also Macs without growlnotify)
7
+ module Rerun
8
+ class Runner
9
+
10
+ include System
11
+
12
+ def initialize(run_command, options = {})
13
+ @run_command, @options = run_command, options
14
+ end
15
+
16
+ def restart
17
+ @restarting = true
18
+ stop
19
+ start
20
+ @restarting = false
21
+ end
22
+
23
+ def start
24
+ if (!@already_running)
25
+ taglines = [
26
+ "To infinity... and beyond!",
27
+ "Charge!",
28
+ ]
29
+ notify "Launched", taglines[rand(taglines.size)]
30
+ @already_running = true
31
+ else
32
+ taglines = [
33
+ "Here we go again!",
34
+ "Once more unto the breach, dear friends, once more!",
35
+ ]
36
+ notify "Restarted", taglines[rand(taglines.size)]
37
+ end
38
+
39
+ @pid = Kernel.fork do
40
+ # Signal.trap("INT") { exit }
41
+ exec(@run_command)
42
+ end
43
+
44
+ Signal.trap("INT") { stop; exit }
45
+
46
+ # Process.detach(@pid)
47
+
48
+ begin
49
+ sleep 2
50
+ rescue Interrupt => e
51
+ # in case someone hits control-C immediately
52
+ stop
53
+ exit
54
+ end
55
+
56
+ unless running?
57
+ notify "Launch Failed", "See console for error output"
58
+ @already_running = false
59
+ end
60
+
61
+ unless @watcher
62
+ watcher_class = osx? ? OSXWatcher : FSWatcher
63
+ # watcher_class = FSWatcher
64
+
65
+ watcher = watcher_class.new do
66
+ restart unless @restarting
67
+ end
68
+ watcher.add_directory(".", "**/*.rb")
69
+ watcher.sleep_time = 1
70
+ watcher.start
71
+
72
+ @watcher = watcher
73
+ end
74
+
75
+ end
76
+
77
+ def join
78
+ @watcher.join
79
+ end
80
+
81
+ def running?
82
+ signal(0)
83
+ end
84
+
85
+ def signal(signal)
86
+ Process.kill(signal, @pid)
87
+ true
88
+ rescue
89
+ false
90
+ end
91
+
92
+ def stop
93
+ if @pid && (@pid != 0)
94
+ notify "Stopped", "All good things must come to an end." unless @restarting
95
+ signal("KILL") && Process.wait(@pid)
96
+ end
97
+ rescue => e
98
+ false
99
+ end
100
+
101
+ def git_head_changed?
102
+ old_git_head = @git_head
103
+ read_git_head
104
+ @git_head and old_git_head and @git_head != old_git_head
105
+ end
106
+
107
+ def read_git_head
108
+ git_head_file = File.join(dir, '.git', 'HEAD')
109
+ @git_head = File.exists?(git_head_file) && File.read(git_head_file)
110
+ end
111
+
112
+ def notify(title, body)
113
+ growl title, body if has_growl?
114
+ puts
115
+ puts "#{Time.now.strftime("%T")} - #{app_name} #{title}"
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+
@@ -0,0 +1,38 @@
1
+
2
+ # are we on OSX or not?
3
+ begin
4
+ require 'osx/foundation'
5
+ OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
6
+ $osx = true
7
+ rescue MissingSourceFile
8
+ # this is to not fail when running on a non-Mac
9
+ end
10
+
11
+ module Rerun
12
+ module System
13
+ def osx?
14
+ $osx
15
+ end
16
+
17
+ # do we have growl or not?
18
+ def has_growl?
19
+ growlcmd != ""
20
+ end
21
+
22
+ def growlcmd
23
+ `which growlnotify`.chomp
24
+ end
25
+
26
+ def app_name
27
+ # todo: make sure this works in non-Mac and non-Unix environments
28
+ File.expand_path(".").gsub(/^.*\//, '').capitalize
29
+ end
30
+
31
+ def growl(title, body, background = true)
32
+ s = "#{growlcmd} -n \"#{app_name}\" -m \"#{body}\" \"#{app_name} #{title}\""
33
+ s += " &" if background
34
+ `#{s}`
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,203 @@
1
+
2
+ Thread.abort_on_exception = true
3
+
4
+ # This class will watch a directory or a set of directories and alert you of
5
+ # new files, modified files, deleted files.
6
+ #
7
+ # Author: Paul Horman, http://paulhorman.com/filesystemwatcher/
8
+ # Author: Alex Chaffee
9
+ module Rerun
10
+ class Watcher
11
+ CREATED = 0
12
+ MODIFIED = 1
13
+ DELETED = 2
14
+
15
+ attr_accessor :sleep_time, :priority
16
+ attr_reader :directories
17
+
18
+ def initialize(&client_callback)
19
+ @client_callback = client_callback
20
+
21
+ @sleep_time = 1
22
+ @priority = 0
23
+
24
+ @directories = []
25
+ @files = []
26
+
27
+ @found = nil
28
+ @first_time = true
29
+ @thread = nil
30
+
31
+ end
32
+
33
+ # add a directory to be watched
34
+ # @param dir the directory to watch
35
+ # @param expression the glob pattern to search under the watched directory
36
+ def add_directory(dir, expression="**/*")
37
+ if FileTest.exists?(dir) && FileTest.readable?(dir) then
38
+ @directories << Directory.new(dir, expression)
39
+ else
40
+ raise InvalidDirectoryError, "Dir '#{dir}' either doesnt exist or isnt readable"
41
+ end
42
+ end
43
+
44
+ def remove_directory(dir)
45
+ @directories.delete(dir)
46
+ end
47
+
48
+ # add a specific file to the watch list
49
+ # @param file the file to watch
50
+ def add_file(file)
51
+ if FileTest.exists?(file) && FileTest.readable?(file) then
52
+ @files << file
53
+ else
54
+ raise InvalidFileError, "File '#{file}' either doesnt exist or isnt readable"
55
+ end
56
+ end
57
+
58
+ def remove_file(file)
59
+ @files.delete(file)
60
+ end
61
+
62
+ def prime
63
+ @first_time = true
64
+ @found = Hash.new()
65
+ examine
66
+ @first_time = false
67
+ end
68
+
69
+ def start
70
+ if @thread then
71
+ raise RuntimeError, "already started"
72
+ end
73
+
74
+ prime
75
+
76
+ @thread = Thread.new do
77
+ while true do
78
+ examine
79
+ sleep(@sleep_time)
80
+ end
81
+ end
82
+
83
+ @thread.priority = @priority
84
+
85
+ at_exit { stop } #?
86
+
87
+ end
88
+
89
+ # kill the filewatcher thread
90
+ def stop
91
+ begin
92
+ @thread.wakeup
93
+ rescue ThreadError => e
94
+ # ignore
95
+ end
96
+ begin
97
+ @thread.kill
98
+ rescue ThreadError => e
99
+ # ignore
100
+ end
101
+ end
102
+
103
+ # wait for the filewatcher to finish
104
+ def join
105
+ @thread.join() if @thread
106
+ rescue Interrupt => e
107
+ # don't care
108
+ end
109
+
110
+ private
111
+
112
+ def examine
113
+ already_examined = Hash.new()
114
+
115
+ @directories.each do |directory|
116
+ examine_files(directory.files(), already_examined)
117
+ end
118
+
119
+ examine_files(@files, already_examined) if not @files.empty?
120
+
121
+ # now diff the found files and the examined files to see if
122
+ # something has been deleted
123
+ all_found_files = @found.keys()
124
+ all_examined_files = already_examined.keys()
125
+ intersection = all_found_files - all_examined_files
126
+ intersection.each do |file_name|
127
+ @client_callback.call(DELETED, file_name)
128
+ @found.delete(file_name)
129
+ end
130
+
131
+ end
132
+
133
+ # loops over the file list check for new or modified files
134
+ def examine_files(files, already_examined)
135
+ files.each do |file_name|
136
+ # expand the file name to the fully qual path
137
+ full_file_name = File.expand_path(file_name)
138
+
139
+ # we cant do much if the file isnt readable anyway
140
+ if File.readable?(full_file_name) then
141
+ already_examined[full_file_name] = true
142
+ stat = File.stat(full_file_name)
143
+ mod_time = stat.mtime
144
+ size = stat.size
145
+
146
+ # on the first iteration just load all of the files into the foundList
147
+ if @first_time then
148
+ @found[full_file_name] = FoundFile.new(full_file_name, mod_time, size)
149
+ else
150
+ # see if we have found this file already
151
+ found_file = @found[full_file_name]
152
+ @found[full_file_name] = FoundFile.new(full_file_name, mod_time, size)
153
+
154
+ if found_file
155
+ if mod_time > found_file.mod_time || size != found_file.size then
156
+ @client_callback.call(MODIFIED, full_file_name)
157
+ end
158
+ else
159
+ @client_callback.call(CREATED, full_file_name)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ class Directory
167
+ attr_reader :dir, :expression
168
+
169
+ def initialize(dir, expression)
170
+ @dir, @expression = dir, expression
171
+ @dir.chop! if @dir =~ %r{/$}
172
+ end
173
+
174
+ def files()
175
+ return Dir[@dir + "/" + @expression]
176
+ end
177
+ end
178
+
179
+ class FoundFile
180
+ attr_reader :status, :file_name, :mod_time, :size
181
+
182
+ def initialize(file_name, mod_time, size)
183
+ @file_name, @mod_time, @size = file_name, mod_time, size
184
+ end
185
+
186
+ def modified(mod_time)
187
+ @mod_time = mod_time
188
+ end
189
+
190
+ def to_s
191
+ "FoundFile[file_name=#{file_name}, mod_time=#{mod_time.to_i}, size=#{size}]"
192
+ end
193
+ end
194
+
195
+ # if the directory you want to watch doesnt exist or isn't readable this is thrown
196
+ class InvalidDirectoryError < StandardError;
197
+ end
198
+
199
+ # if the file you want to watch doesnt exist or isn't readable this is thrown
200
+ class InvalidFileError < StandardError;
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,38 @@
1
+ $spec = Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+
5
+ s.name = 'rerun'
6
+ s.version = '0.2.1'
7
+ s.date = '2009-06-16'
8
+
9
+ s.description = "Restarts your app when a file changes"
10
+ s.summary = "Launches an app, and restarts it whenever the filesystem changes."
11
+
12
+ s.authors = ["Alex Chaffee"]
13
+ s.email = "alex@stinky.com"
14
+
15
+ s.files = %w[
16
+ README.md
17
+ LICENSE
18
+ Rakefile
19
+ rerun.gemspec
20
+ bin/rerun
21
+ lib/rerun.rb
22
+ lib/fswatcher.rb
23
+ lib/osxwatcher.rb
24
+ lib/system.rb
25
+ lib/watcher.rb
26
+ ]
27
+ s.executables = ['rerun']
28
+ s.test_files = s.files.select {|path| path =~ /^spec\/.*_spec.rb/}
29
+
30
+ s.extra_rdoc_files = %w[README.md]
31
+ #s.add_dependency 'rack', '>= 0.9.1'
32
+ #s.add_dependency 'launchy', '>= 0.3.3', '< 1.0'
33
+
34
+ s.homepage = "http://github.com/alexch/rerun/"
35
+ s.require_paths = %w[lib]
36
+ s.rubyforge_project = 'pivotalrb'
37
+ s.rubygems_version = '1.1.1'
38
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alexch-rerun
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Chaffee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Restarts your app when a file changes
17
+ email: alex@stinky.com
18
+ executables:
19
+ - rerun
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.md
24
+ files:
25
+ - README.md
26
+ - LICENSE
27
+ - Rakefile
28
+ - rerun.gemspec
29
+ - bin/rerun
30
+ - lib/rerun.rb
31
+ - lib/fswatcher.rb
32
+ - lib/osxwatcher.rb
33
+ - lib/system.rb
34
+ - lib/watcher.rb
35
+ has_rdoc: false
36
+ homepage: http://github.com/alexch/rerun/
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project: pivotalrb
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: Launches an app, and restarts it whenever the filesystem changes.
61
+ test_files: []
62
+