filewatcher 0.5.4 → 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/filewatcher.rb +79 -138
- data/lib/filewatcher/cycles.rb +56 -0
- data/lib/filewatcher/snapshot.rb +65 -0
- data/lib/filewatcher/snapshots.rb +56 -0
- data/lib/filewatcher/spec_helper.rb +66 -0
- data/lib/filewatcher/spec_helper/watch_run.rb +74 -0
- data/lib/filewatcher/version.rb +5 -0
- data/spec/filewatcher/snapshot_spec.rb +67 -0
- data/spec/filewatcher/version_spec.rb +11 -0
- data/spec/filewatcher_spec.rb +289 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/spec_helper/ruby_watch_run.rb +82 -0
- metadata +92 -42
- data/LICENSE +0 -20
- data/README.md +0 -269
- data/Rakefile +0 -19
- data/bin/filewatcher +0 -168
- data/test/fixtures/file1.txt +0 -1
- data/test/fixtures/file2.txt +0 -1
- data/test/fixtures/file3.rb +0 -1
- data/test/fixtures/file4.rb +0 -1
- data/test/fixtures/subdir/file5.rb +0 -1
- data/test/fixtures/subdir/file6.rb +0 -1
- data/test/test_filewatcher.rb +0 -181
data/Rakefile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rake'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
task :default => :test
|
6
|
-
|
7
|
-
desc "Run tests"
|
8
|
-
task :test do
|
9
|
-
sh "bacon -Ilib -Itest --automatic --quiet"
|
10
|
-
delete_list =
|
11
|
-
%w(test/fixtures/file3.txt
|
12
|
-
test/fixtures/file4.txt
|
13
|
-
test/fixtures/file5.txt
|
14
|
-
test/fixtures/file6.txt
|
15
|
-
test/fixtures/file7.txt)
|
16
|
-
delete_list.each do |file|
|
17
|
-
FileUtils.rm(file)
|
18
|
-
end
|
19
|
-
end
|
data/bin/filewatcher
DELETED
@@ -1,168 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'rubygems'
|
3
|
-
require 'filewatcher'
|
4
|
-
require 'trollop'
|
5
|
-
require 'pathname'
|
6
|
-
require 'thread'
|
7
|
-
|
8
|
-
options = Trollop::options do
|
9
|
-
version "filewatcher, version #{FileWatcher.VERSION} by Thomas Flemming 2016"
|
10
|
-
banner <<-EOS
|
11
|
-
Filewatcher scans the filesystem and executes shell commands when files changes.
|
12
|
-
|
13
|
-
Usage:
|
14
|
-
filewatcher [--restart] '<filenames or patterns>' '<shell command>'
|
15
|
-
Where
|
16
|
-
filename: filename(s) to scan.
|
17
|
-
shell command: shell command to execute when file changes on disk.
|
18
|
-
|
19
|
-
Examples:
|
20
|
-
filewatcher "myfile" "echo 'myfile has changed'"
|
21
|
-
filewatcher '*.rb' 'ruby $FILENAME'
|
22
|
-
filewatcher '**/*.rb' 'ruby $FILENAME' # Watch subdirectories
|
23
|
-
|
24
|
-
Other available environment variables are BASENAME, ABSOLUTE_FILENAME,
|
25
|
-
RELATIVE_FILENAME, EVENT and DIRNAME.
|
26
|
-
|
27
|
-
Options:
|
28
|
-
EOS
|
29
|
-
|
30
|
-
opt :dontwait, "Do not wait for filesystem updates before running", :short => 'd', :type => :boolean, :default => false
|
31
|
-
opt :daemon, "Run in the background as system daemon.", :short => 'D', :type => :boolean, :default => false
|
32
|
-
opt :restart, "Restart process when filesystem is updated", :short => 'r', :type => :boolean, :default => false
|
33
|
-
opt :list, "Print name of files being watched"
|
34
|
-
opt :exec, "Execute file as a script when file is updated.", :short => 'e', :type => :boolean, :default => false
|
35
|
-
opt :include, "Include files", :type => :string, :default => "*"
|
36
|
-
opt :exclude, "Exclude file(s) matching", :type => :string, :default => ""
|
37
|
-
opt :interval, "Interval to scan filesystem.", :short => 'i', :type => :float, :default => 0.5
|
38
|
-
opt :spinner, "Show an ascii spinner", :short => 's', :type => :boolean, :default => false
|
39
|
-
end
|
40
|
-
|
41
|
-
Trollop::die Trollop::educate if(ARGV.size == 0)
|
42
|
-
|
43
|
-
files = []
|
44
|
-
ARGV[0...-1].each do |a|
|
45
|
-
files << a
|
46
|
-
end
|
47
|
-
|
48
|
-
if(ARGV.length == 1)
|
49
|
-
files << ARGV[0]
|
50
|
-
end
|
51
|
-
|
52
|
-
def split_files_void_escaped_whitespace(files)
|
53
|
-
splitted_filenames = []
|
54
|
-
files.each do |name|
|
55
|
-
name = name.gsub(/\\\s/,'_ESCAPED_WHITESPACE_')
|
56
|
-
splitted_filenames << name.split(/\s/)
|
57
|
-
end
|
58
|
-
files = splitted_filenames.flatten.uniq
|
59
|
-
splitted_filenames = []
|
60
|
-
files.each do |name|
|
61
|
-
splitted_filenames << name.gsub('_ESCAPED_WHITESPACE_','\ ')
|
62
|
-
end
|
63
|
-
files = splitted_filenames
|
64
|
-
end
|
65
|
-
|
66
|
-
files = split_files_void_escaped_whitespace(files)
|
67
|
-
child_pid = nil
|
68
|
-
|
69
|
-
def restart(child_pid, env, cmd)
|
70
|
-
Process.kill(9, child_pid)
|
71
|
-
Process.wait(child_pid)
|
72
|
-
rescue Errno::ESRCH
|
73
|
-
# already killed
|
74
|
-
ensure
|
75
|
-
return Process.spawn(env, cmd)
|
76
|
-
end
|
77
|
-
|
78
|
-
if(options[:exclude] != "")
|
79
|
-
options[:exclude] = split_files_void_escaped_whitespace(options[:exclude].split(" "))
|
80
|
-
end
|
81
|
-
|
82
|
-
begin
|
83
|
-
fw = FileWatcher.new(files, options)
|
84
|
-
|
85
|
-
if(options[:list])
|
86
|
-
puts 'Watching:'
|
87
|
-
fw.last_found_filenames.each do |filename|
|
88
|
-
puts " #{filename}"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
Process.daemon(true, true) if options[:daemon]
|
93
|
-
|
94
|
-
fw.watch(options[:interval]) do |filename, event|
|
95
|
-
cmd = nil
|
96
|
-
if(options[:exec] and File.exist?(filename))
|
97
|
-
extension = filename[/(\.[^\.]*)$/,0]
|
98
|
-
runners = {
|
99
|
-
".py" => "python",
|
100
|
-
".js" => "node",
|
101
|
-
".rb" => "ruby",
|
102
|
-
".pl" => "perl",
|
103
|
-
".awk" => "awk",
|
104
|
-
".php" => "php",
|
105
|
-
".phtml" => "php",
|
106
|
-
".php4" => "php",
|
107
|
-
".php3" => "php",
|
108
|
-
".php5" => "php",
|
109
|
-
".phps" => "php"
|
110
|
-
}
|
111
|
-
runner = runners[extension]
|
112
|
-
if(runner)
|
113
|
-
cmd = "env #{runner.to_s} #{filename}"
|
114
|
-
end
|
115
|
-
elsif(ARGV.length > 1)
|
116
|
-
cmd = ARGV[-1]
|
117
|
-
end
|
118
|
-
|
119
|
-
if(cmd)
|
120
|
-
path = Pathname.new(filename)
|
121
|
-
env = {
|
122
|
-
'FILENAME' => filename,
|
123
|
-
'BASENAME' => path.basename.to_s,
|
124
|
-
'FILEDIR' => File.join(Pathname.new('.').realpath.to_s, path.parent.to_s), # Deprecated
|
125
|
-
'FSEVENT' => event.to_s, # Deprecated,
|
126
|
-
'EVENT' => event.to_s,
|
127
|
-
'DIRNAME' => File.join(Pathname.new('.').realpath.to_s, path.parent.to_s),
|
128
|
-
'ABSOLUTE_FILENAME' => File.join(Pathname.new('.').realpath.to_s, path.to_s),
|
129
|
-
'RELATIVE_FILENAME' => File.join(Pathname.new('.').to_s, path.to_s)
|
130
|
-
}
|
131
|
-
|
132
|
-
if(event != :delete)
|
133
|
-
ENV['FILEPATH'] = path.realpath.to_s
|
134
|
-
end
|
135
|
-
|
136
|
-
if(options[:restart])
|
137
|
-
if child_pid.nil?
|
138
|
-
child_pid = Process.spawn(env, cmd)
|
139
|
-
else
|
140
|
-
child_pid = restart(child_pid, env, cmd)
|
141
|
-
end
|
142
|
-
else
|
143
|
-
begin
|
144
|
-
pid = Process.spawn(env, cmd)
|
145
|
-
Process.wait()
|
146
|
-
rescue SystemExit, Interrupt
|
147
|
-
exit(0)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
else
|
152
|
-
case(event)
|
153
|
-
when :changed
|
154
|
-
print "file updated"
|
155
|
-
when :delete
|
156
|
-
print "file deleted"
|
157
|
-
when :new
|
158
|
-
print "new file"
|
159
|
-
else
|
160
|
-
print event.to_s
|
161
|
-
end
|
162
|
-
puts ": " + filename
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
166
|
-
rescue SystemExit, Interrupt
|
167
|
-
fw.finalize
|
168
|
-
end
|
data/test/fixtures/file1.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
content1
|
data/test/fixtures/file2.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
content2
|
data/test/fixtures/file3.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
|
data/test/fixtures/file4.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
puts "Hello world from Ruby!!!"
|
@@ -1 +0,0 @@
|
|
1
|
-
|
@@ -1 +0,0 @@
|
|
1
|
-
puts "This is file6.rb executing"
|
data/test/test_filewatcher.rb
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bacon'
|
3
|
-
require 'fileutils'
|
4
|
-
require File.expand_path("../lib/filewatcher.rb",File.dirname(__FILE__))
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
describe FileWatcher do
|
9
|
-
|
10
|
-
fixtures =
|
11
|
-
%w(test/fixtures/file4.rb
|
12
|
-
test/fixtures/subdir/file6.rb
|
13
|
-
test/fixtures/subdir/file5.rb
|
14
|
-
test/fixtures/file2.txt
|
15
|
-
test/fixtures/file1.txt
|
16
|
-
test/fixtures/file3.rb)
|
17
|
-
|
18
|
-
explicit_relative_fixtures = fixtures.map { |it| "./#{it}" }
|
19
|
-
|
20
|
-
subfolder = 'test/fixtures/new_sub_folder'
|
21
|
-
|
22
|
-
after do
|
23
|
-
FileUtils.rm_rf subfolder
|
24
|
-
end
|
25
|
-
|
26
|
-
def includes_all(elements)
|
27
|
-
lambda { |it| elements.all? { |element| it.include? element }}
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should exclude selected file patterns" do
|
31
|
-
filewatcher = FileWatcher.new(File.expand_path('test/fixtures/**/*'), :exclude => [File.expand_path("test/fixtures/**/*.txt")])
|
32
|
-
filtered_fixtures =
|
33
|
-
%w(test/fixtures/file4.rb
|
34
|
-
test/fixtures/subdir/file6.rb
|
35
|
-
test/fixtures/subdir/file5.rb
|
36
|
-
test/fixtures/file3.rb)
|
37
|
-
filewatcher.filenames.should.satisfy &includes_all(filtered_fixtures.map { |it| File.expand_path(it) })
|
38
|
-
end
|
39
|
-
|
40
|
-
it "should handle absolute paths with globs" do
|
41
|
-
filewatcher = FileWatcher.new(File.expand_path('test/fixtures/**/*'))
|
42
|
-
|
43
|
-
filewatcher.filenames.should.satisfy &includes_all(fixtures.map { |it| File.expand_path(it) })
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should handle globs" do
|
47
|
-
filewatcher = FileWatcher.new('test/fixtures/**/*')
|
48
|
-
|
49
|
-
filewatcher.filenames.should.satisfy &includes_all(fixtures)
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
it "should handle explicit relative paths with globs" do
|
54
|
-
filewatcher = FileWatcher.new('./test/fixtures/**/*')
|
55
|
-
|
56
|
-
filewatcher.filenames.should.satisfy &includes_all(explicit_relative_fixtures)
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should handle explicit relative paths" do
|
60
|
-
filewatcher = FileWatcher.new('./test/fixtures')
|
61
|
-
|
62
|
-
filewatcher.filenames.should.satisfy &includes_all(explicit_relative_fixtures)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "should handle tilde expansion" do
|
66
|
-
filename = File.expand_path('~/file_watcher_1.txt')
|
67
|
-
open(filename, 'w') { |f| f.puts 'content1' }
|
68
|
-
|
69
|
-
filewatcher = FileWatcher.new('~/file_watcher_1.txt')
|
70
|
-
|
71
|
-
begin
|
72
|
-
filewatcher.filenames.should == [filename]
|
73
|
-
ensure
|
74
|
-
FileUtils.rm(filename)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
it "should detect file deletions" do
|
79
|
-
filename = "test/fixtures/file1.txt"
|
80
|
-
open(filename,"w") { |f| f.puts "content1" }
|
81
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
82
|
-
filewatcher.filesystem_updated?.should.be.false
|
83
|
-
FileUtils.rm(filename)
|
84
|
-
filewatcher.filesystem_updated?.should.be.true
|
85
|
-
end
|
86
|
-
|
87
|
-
it "should detect file additions" do
|
88
|
-
filename = "test/fixtures/file1.txt"
|
89
|
-
FileUtils.rm(filename) if File.exists?(filename)
|
90
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
91
|
-
filewatcher.filesystem_updated?.should.be.false
|
92
|
-
open(filename,"w") { |f| f.puts "content1" }
|
93
|
-
filewatcher.filesystem_updated?.should.be.true
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should detect file updates" do
|
97
|
-
filename = "test/fixtures/file1.txt"
|
98
|
-
open(filename,"w") { |f| f.puts "content1" }
|
99
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
100
|
-
filewatcher.filesystem_updated?.should.be.false
|
101
|
-
sleep 1
|
102
|
-
open(filename,"w") { |f| f.puts "content2" }
|
103
|
-
filewatcher.filesystem_updated?.should.be.true
|
104
|
-
end
|
105
|
-
|
106
|
-
it "should detect new files in subfolders" do
|
107
|
-
FileUtils::mkdir_p subfolder
|
108
|
-
|
109
|
-
filewatcher = FileWatcher.new(["./test/fixtures"])
|
110
|
-
filewatcher.filesystem_updated?.should.be.false
|
111
|
-
|
112
|
-
open(subfolder + "/file.txt","w") { |f| f.puts "xyz" }
|
113
|
-
filewatcher.filesystem_updated?.should.be.true
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should detect new subfolders" do
|
117
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
118
|
-
filewatcher.filesystem_updated?.should.be.false
|
119
|
-
|
120
|
-
FileUtils::mkdir_p subfolder
|
121
|
-
filewatcher.filesystem_updated?.should.be.true
|
122
|
-
end
|
123
|
-
|
124
|
-
it "should be stoppable" do
|
125
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
126
|
-
thread = Thread.new(filewatcher){filewatcher.watch(0.1)}
|
127
|
-
sleep 0.2 # thread needs a chance to start
|
128
|
-
filewatcher.stop
|
129
|
-
thread.join.should.equal thread # Proves thread successfully joined
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should be pauseable/resumable" do
|
133
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
134
|
-
filewatcher.filesystem_updated?.should.be.false
|
135
|
-
processed = []
|
136
|
-
thread = Thread.new(filewatcher,processed) do
|
137
|
-
filewatcher.watch(0.1){|f,e| processed << f }
|
138
|
-
end
|
139
|
-
sleep 0.2 # thread needs a chance to start
|
140
|
-
filewatcher.pause
|
141
|
-
(1..4).each do |n|
|
142
|
-
open("test/fixtures/file#{n}.txt","w") { |f| f.puts "content#{n}" }
|
143
|
-
end
|
144
|
-
sleep 0.2 # Give filewatcher time to respond
|
145
|
-
processed.should.equal [] #update block should not have been called
|
146
|
-
filewatcher.resume
|
147
|
-
sleep 0.2 # Give filewatcher time to respond
|
148
|
-
processed.should.equal [] #update block still should not have been called
|
149
|
-
added_files = []
|
150
|
-
(5..7).each do |n|
|
151
|
-
added_files << "test/fixtures/file#{n}.txt"
|
152
|
-
open(added_files.last,"w") { |f| f.puts "content#{n}" }
|
153
|
-
end
|
154
|
-
sleep 0.2 # Give filewatcher time to respond
|
155
|
-
filewatcher.stop
|
156
|
-
processed.should.satisfy &includes_all(added_files)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should process all remaining changes at finalize" do
|
160
|
-
filewatcher = FileWatcher.new(["test/fixtures"])
|
161
|
-
filewatcher.filesystem_updated?.should.be.false
|
162
|
-
processed = []
|
163
|
-
thread = Thread.new(filewatcher,processed) do
|
164
|
-
filewatcher.watch(0.1){|f,e| processed << f }
|
165
|
-
end
|
166
|
-
sleep 0.2 # thread needs a chance to start
|
167
|
-
filewatcher.stop
|
168
|
-
thread.join
|
169
|
-
added_files = []
|
170
|
-
(1..4).each do |n|
|
171
|
-
added_files << "test/fixtures/file#{n}.txt"
|
172
|
-
open(added_files.last,"w") { |f| f.puts "content#{n}" }
|
173
|
-
end
|
174
|
-
filewatcher.finalize
|
175
|
-
puts "What is wrong with finalize:"
|
176
|
-
puts "Expect: #{added_files.inspect}"
|
177
|
-
puts "Actual: #{processed.inspect}"
|
178
|
-
# processed.should.satisfy &includes_all(added_files)
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|