directory_monitor 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +54 -0
- data/Rakefile +10 -0
- data/bin/watch +16 -0
- data/directory_monitor.gemspec +26 -0
- data/lib/directory_monitor.rb +3 -0
- data/lib/directory_monitor/directory_monitor.rb +87 -0
- data/lib/directory_monitor/options.rb +58 -0
- data/lib/directory_monitor/version.rb +3 -0
- data/test/directory_monitor_tc.rb +182 -0
- data/test/options_tc.rb +74 -0
- data/test/test_helper.rb +8 -0
- data/test/watch_tc.rb +55 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 08b4bc2deea1a908ad8b79db41709a00fc19390c
|
4
|
+
data.tar.gz: 6bfa3afa6899ef873e3941dbd8328ff438e0c813
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 437a78ad4f72ceed502f37a4874c14a23580c89595f7b9af6879094b278885ff50acd36b01244d3475b292f979066cb692deb5b55fec347e8a9685b2a6a94736
|
7
|
+
data.tar.gz: 28ffbb06717d84e29350e8747e7ed9457f2e0f71aef400d81c30c1182faf8c2293a5d90add5f7d4703762bf849e60505a066cc16b227926fb513c90d4eafcf1e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 David A. Love
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# DirectoryMonitor
|
2
|
+
|
3
|
+
The DirectoryMonitor is a very simple utility class used to watch for file
|
4
|
+
modifications in a directory tree. In addition to the DirectoryMonitor class,
|
5
|
+
this gem also includes an executable, `watch`, which is a command line wrapper
|
6
|
+
for invoking the `DirectoryMonitor#on_change` method and executing a shell-
|
7
|
+
command whenever changes are detected.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'directory_monitor'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install directory_monitor
|
22
|
+
|
23
|
+
## Usage of Watch
|
24
|
+
|
25
|
+
To use the `watch` executable, type `watch --help` for a short description of
|
26
|
+
the command-line options supported. There are few additional hints, here.
|
27
|
+
|
28
|
+
TODO: Add hits and examples here.
|
29
|
+
|
30
|
+
My personal favorite usage of this utility is as a very simple continuous test
|
31
|
+
monitor. I usually have my project's Rakefile setup with the default task to
|
32
|
+
execute the test suites for my project. Once done, I'll open a command window
|
33
|
+
(sometimes on my desktop and other times just a pane in tumx) and run the
|
34
|
+
following command:
|
35
|
+
|
36
|
+
watch --force --delay=0.5 --suffix='\.rb' "clear; rake 2>&1 | more"
|
37
|
+
|
38
|
+
This command will cause the rake file to run once, at startup, and from that
|
39
|
+
point on, check every half second for updates to my ruby source files. This
|
40
|
+
is occasionally so helpful I'll actually add this command to my Rakefile
|
41
|
+
as a task named "autotest".
|
42
|
+
|
43
|
+
## Usage of DirectoryMonitor Class
|
44
|
+
|
45
|
+
About the best usage example of the class is provided by the `watch` executable
|
46
|
+
script. See bin/watch for the details.
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/watch
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# watch -- The executable script for monitoring file changes in a directory.
|
4
|
+
|
5
|
+
require "directory_monitor"
|
6
|
+
require "directory_monitor/options"
|
7
|
+
|
8
|
+
opts = Options.parse
|
9
|
+
dm = DirectoryMonitor::DirectoryMonitor.new(opts[:suffix], opts[:delay])
|
10
|
+
at_exit { puts "\n#{ProgName} canceled" }
|
11
|
+
dm.on_change(opts[:loop], opts[:force]) do |*changed|
|
12
|
+
shell_cmd = "#{opts[:shell_command].sub(opts[:token], changed.join(' '))}"
|
13
|
+
puts shell_cmd if opts[:verbose]
|
14
|
+
system(shell_cmd, out: $stdout, err: $stderr)
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'directory_monitor/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "directory_monitor"
|
8
|
+
spec.version = DirectoryMonitor::VERSION
|
9
|
+
spec.authors = ["David A. Love"]
|
10
|
+
spec.email = ["DALove1025@gmail.com"]
|
11
|
+
spec.description = %q{Monitor a directory for file changes.}
|
12
|
+
spec.summary = %q{A Directory Monitor}
|
13
|
+
spec.homepage = "https://github.com/DALove1025/directory_monitor#directorymonitor"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'trollop', '~> 2.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "simplecov", "~> 0.9"
|
26
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# DirectoryMonitor -- a simple monitor class for detecting file changes in the
|
2
|
+
# current directory.
|
3
|
+
#
|
4
|
+
# A DirectoryMonitor object sleeps for a specified time and then looks in the
|
5
|
+
# current directory-tree for any changed files matching a particular RegEx.
|
6
|
+
# When file changes are discovered, the DirectoryMonitor yields to the caller
|
7
|
+
# with the pathnames of the changed files. After the yield returns, the
|
8
|
+
# DirectoryMonitor sleeps once again.
|
9
|
+
#
|
10
|
+
# File deletions are not detected.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# Look every second for any differences and print the filenames, one on
|
15
|
+
# each line...
|
16
|
+
#
|
17
|
+
# DirectoryMonitor.new.on_change(true) { |file| puts file }
|
18
|
+
#
|
19
|
+
# Check for new or changed Ruby files every 5 minutes...
|
20
|
+
#
|
21
|
+
# DirectoryMonitor.new('\.rb', 300).on_change { puts "Some ruby changed" }
|
22
|
+
#
|
23
|
+
# Note that the suffix parameter to DirectoryMonitor#new is a RegEx, it is not
|
24
|
+
# a glob-style, filename wild-card. For example, to watch only those files
|
25
|
+
# ending with, say, .dat, use a backslash escape, as in '\.dat', to force a
|
26
|
+
# match on a literal period character.
|
27
|
+
|
28
|
+
require 'find'
|
29
|
+
|
30
|
+
module DirectoryMonitor
|
31
|
+
|
32
|
+
class DirectoryMonitor
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def save_ctimes(files)
|
37
|
+
files.each { |f| @ctimes[f] = @File.ctime(f) }
|
38
|
+
files
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_changed
|
42
|
+
@Find.find('.').select { |f| f =~ @re && @ctimes[f] != @File.ctime(f) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def find
|
46
|
+
save_ctimes(find_changed)
|
47
|
+
end
|
48
|
+
|
49
|
+
public
|
50
|
+
|
51
|
+
attr_writer :Find, :File # Used by the unit tests, see below.
|
52
|
+
|
53
|
+
def initialize(suffix = '.*', delay = 1)
|
54
|
+
@re, @delay = /(#{suffix})$/, delay
|
55
|
+
@Find, @File = Find, File # Dependency-injection hooks for unit-tests.
|
56
|
+
@ctimes = {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_change(loopflag = false, force = false) # loops forever.
|
60
|
+
# Unless it's inhibited by the force flag, use the find method to
|
61
|
+
# pre-populate the hash of file-names and ctimes.
|
62
|
+
find unless force
|
63
|
+
begin
|
64
|
+
loop do
|
65
|
+
(loopflag ? find : [ find.join(" ") ]).each do |str|
|
66
|
+
yield str unless str == ""
|
67
|
+
end
|
68
|
+
# Now that we are done with all our yields, use find, one more time
|
69
|
+
# to record all the current ctimes. Since we are done, we can safely
|
70
|
+
# go back to sleep.
|
71
|
+
find
|
72
|
+
sleep(@delay)
|
73
|
+
end
|
74
|
+
rescue SystemExit, Interrupt
|
75
|
+
# Since this is an infinite loop, we sort of expect that an app using
|
76
|
+
# this class will be shut down by a signal or interrupt. So, to be a
|
77
|
+
# bit more graceful, we detect these conditions and "go gently into
|
78
|
+
# that good night." If the app needs to do any clean up, it should
|
79
|
+
# probably implement a trap("EXIT") handler.
|
80
|
+
exit
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# options.rb -- command-line options processing for the watch script.
|
2
|
+
|
3
|
+
require "trollop"
|
4
|
+
|
5
|
+
# Define the strings we display to the user for version identification and the
|
6
|
+
# help message.
|
7
|
+
ProgName = File.basename($PROGRAM_NAME, File.extname($PROGRAM_NAME))
|
8
|
+
Version = "#{ProgName} (#{DirectoryMonitor::VERSION})"
|
9
|
+
Banner = <<-eos
|
10
|
+
#{Version} -- Watch a directory for changes
|
11
|
+
Synopsis
|
12
|
+
Executes a shell-command when files change. If the shell-command contains
|
13
|
+
a double-percent, %%, it is replaced with a space delimited list of path
|
14
|
+
names for the changed files. When used with the --loop option, the shell-
|
15
|
+
command is executed repeatedly, once for each changed file. In this case,
|
16
|
+
%%, if present, is replaced with one path name at a time.
|
17
|
+
|
18
|
+
The --suffix option limits the watched files to only those matching a
|
19
|
+
regular expression, anchored at the end of the file name. Note that this
|
20
|
+
is a RegEx, not a glob wildcard; therfore, to match a file name that
|
21
|
+
contains a period, for example, .txt, the period must be escaped with a
|
22
|
+
backslash. See the examples.
|
23
|
+
|
24
|
+
File deletions are not detected.
|
25
|
+
|
26
|
+
Examples
|
27
|
+
watch -l -d 10 \"echo File %% has changed\"
|
28
|
+
# Every 10 seconds, display each changed file on a separate line.
|
29
|
+
|
30
|
+
watch -s "\\.rb|\\.yaml" rake
|
31
|
+
# Every 5 seconds, run a rake task if any ruby or Yaml file changes.
|
32
|
+
|
33
|
+
Usage
|
34
|
+
#{ProgName} [-fhlvV] [-d <float>] [-st <str>] <shell-command>
|
35
|
+
eos
|
36
|
+
|
37
|
+
class Options
|
38
|
+
|
39
|
+
# Use trollop to handle all our options switches and verify we have some
|
40
|
+
# sort of shell-command specified on the command-line.
|
41
|
+
def self.parse
|
42
|
+
opts = Trollop::options do
|
43
|
+
version "#{Version}"
|
44
|
+
banner Banner.gsub(/^#{Banner[/^ +/, 0]}/, "")
|
45
|
+
opt :suffix, "Regex selecting the files being watched", :default => ".*"
|
46
|
+
opt :delay, "Seconds to sleep between watches", :default => 5.0
|
47
|
+
opt :loop, "Execute individual command for each file"
|
48
|
+
opt :force, "Force a first exectuion on all watched files"
|
49
|
+
opt :token, "String used for file name substitution", :default => "%%"
|
50
|
+
opt :verbose, "Print command on standard output", :short => "V"
|
51
|
+
end
|
52
|
+
Trollop::die "shell command is required" if ARGV.empty?
|
53
|
+
opts[:shell_command] = ARGV.join(" ")
|
54
|
+
opts
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
# Unit testing the DirectoryMonitor class requires a file system that knows
|
4
|
+
# about filenames and modification times. The following class allows us to
|
5
|
+
# mock a simple file system; and, in the process, simulate implementations
|
6
|
+
# of the two class methods that are dependencies of the DirectoryMonitor.
|
7
|
+
|
8
|
+
class ErsatzFileSystem
|
9
|
+
|
10
|
+
@@files = {}
|
11
|
+
|
12
|
+
def self.touch(file) # Creates and resets files.
|
13
|
+
@@files[file] = Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.ctime(filename) # Simulates File::ctime()
|
17
|
+
raise 'No such filename found' unless @@files[filename]
|
18
|
+
@@files[filename]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.find(dir) # Simulates Find::find()
|
22
|
+
@@files.map { |filename, modtime| filename }.to_enum
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
# Patch the DirectoryMonitor class to stub-out the call to Kernel::sleep().
|
28
|
+
# This allows the unit-tests to have synchronous control over DirectoryMonitor
|
29
|
+
# by letting us call resume on the DirectoryMonitor#on_change() fiber.
|
30
|
+
|
31
|
+
class DirectoryMonitor::DirectoryMonitor
|
32
|
+
attr_reader :last_parameter
|
33
|
+
def sleep(delay)
|
34
|
+
@last_parameter = delay
|
35
|
+
Fiber.yield
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class DirectoryMonitor_Base_TestCase < MiniTest::Unit::TestCase
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def setup_file_system
|
44
|
+
%w{
|
45
|
+
Abby.txt
|
46
|
+
Billy.rb
|
47
|
+
Cindy.rb
|
48
|
+
Dan.dat
|
49
|
+
Ellie.txt
|
50
|
+
Frank.rb
|
51
|
+
Gloria.txt
|
52
|
+
Hank.dat
|
53
|
+
Ira.rb.foo
|
54
|
+
}.each { |f| ErsatzFileSystem.touch(f) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def setup_watcher(suffix, loopflag = false, force = false)
|
58
|
+
@dm = DirectoryMonitor::DirectoryMonitor.new(suffix)
|
59
|
+
@dm.Find = ErsatzFileSystem # Dependency injection for these unit tests
|
60
|
+
@dm.File = ErsatzFileSystem
|
61
|
+
@yield_history = []
|
62
|
+
@dm_fiber = Fiber.new do
|
63
|
+
@dm.on_change(loopflag, force) do |file_response|
|
64
|
+
@yield_history << file_response
|
65
|
+
end
|
66
|
+
end
|
67
|
+
@dm_fiber.resume
|
68
|
+
end
|
69
|
+
|
70
|
+
def run_watcher(*changed_files)
|
71
|
+
changed_files.each{ |file| ErsatzFileSystem.touch(file) }
|
72
|
+
@dm_fiber.resume
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
class DirectoryMonitor_TestCase < DirectoryMonitor_Base_TestCase
|
78
|
+
|
79
|
+
# Create the "object under test" with a simulated file system and its own
|
80
|
+
# fiber for the on_change event.
|
81
|
+
def setup
|
82
|
+
setup_file_system
|
83
|
+
setup_watcher("\\.rb|xyzzy")
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_creation
|
87
|
+
assert_instance_of(DirectoryMonitor::DirectoryMonitor, @dm)
|
88
|
+
assert_equal(1, @dm.last_parameter, "Default delay should be 1 sec")
|
89
|
+
assert_equal(0, @yield_history.length, "No yields should have happend")
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_no_yields_without_file_changes
|
93
|
+
run_watcher
|
94
|
+
assert_equal(0, @yield_history.length)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_only_one_yield_per_set_of_changes
|
98
|
+
run_watcher("Cindy.rb")
|
99
|
+
assert_equal(1, @yield_history.length)
|
100
|
+
run_watcher("Billy.rb", "Cindy.rb", "Dan.dat")
|
101
|
+
assert_equal(2, @yield_history.length)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_change_one_watched_file
|
105
|
+
run_watcher("Cindy.rb")
|
106
|
+
assert_equal(1, @yield_history.length)
|
107
|
+
assert_equal("Cindy.rb", @yield_history[0])
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_change_two_watched_files
|
111
|
+
run_watcher("Cindy.rb", "Frank.rb")
|
112
|
+
assert_equal(1, @yield_history.length)
|
113
|
+
assert_equal("Cindy.rb Frank.rb", @yield_history[0])
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_no_yields_on_unwatched_file_change
|
117
|
+
run_watcher("Dan.dat")
|
118
|
+
assert_equal(0, @yield_history.length)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_yield_with_both_watched_and_unwatched_files
|
122
|
+
run_watcher("Abby.txt", "Cindy.rb", "Dan.dat", "Frank.rb")
|
123
|
+
assert_equal(1, @yield_history.length)
|
124
|
+
assert_equal("Cindy.rb Frank.rb", @yield_history[0])
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_regex_suffix_anchored_at_end_of_filename
|
128
|
+
run_watcher("Abby.txt", "Billy.rb", "Helen.dat", "Ira.rb.foo")
|
129
|
+
assert_equal(1, @yield_history.length)
|
130
|
+
assert_equal("Billy.rb", @yield_history[0])
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
class DirectoryMonitor_Looping_TestCase < DirectoryMonitor_Base_TestCase
|
136
|
+
|
137
|
+
def setup
|
138
|
+
setup_file_system
|
139
|
+
setup_watcher("\\.rb|xyzzy", true)
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_looping_yields_for_each_watched_file
|
143
|
+
run_watcher("Abby.txt", "Cindy.rb", "Dan.dat", "Frank.rb")
|
144
|
+
assert_equal(2, @yield_history.length)
|
145
|
+
assert_equal("Cindy.rb", @yield_history[0])
|
146
|
+
assert_equal("Frank.rb", @yield_history[1])
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
class DirectoryMonitor_Force_TestCase < DirectoryMonitor_Base_TestCase
|
152
|
+
|
153
|
+
def setup
|
154
|
+
setup_file_system
|
155
|
+
setup_watcher("\\.rb|xyzzy", false, true)
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_initial_forced_yields_all_watched_files
|
159
|
+
run_watcher
|
160
|
+
assert_equal(1, @yield_history.length)
|
161
|
+
assert_equal("Billy.rb Cindy.rb Frank.rb", @yield_history[0])
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
class DirectoryMonitor_Loop_And_Force_TestCase < DirectoryMonitor_Base_TestCase
|
167
|
+
|
168
|
+
def setup
|
169
|
+
setup_file_system
|
170
|
+
setup_watcher("\\.rb|xyzzy", true, true)
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_initial_forced_yields_all_watched_files
|
174
|
+
run_watcher
|
175
|
+
assert_equal(3, @yield_history.length)
|
176
|
+
assert_equal("Billy.rb", @yield_history[0])
|
177
|
+
assert_equal("Cindy.rb", @yield_history[1])
|
178
|
+
assert_equal("Frank.rb", @yield_history[2])
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
data/test/options_tc.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "directory_monitor/options"
|
3
|
+
|
4
|
+
class Optionse_TestCase < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# We need a way to set the ARGV array, to spoof trollop, which means
|
9
|
+
# redefining a constant. Doing that without generating a Ruby run-time
|
10
|
+
# warning message is tricker than I expected. Here's the simplest way
|
11
|
+
# I've come up with.
|
12
|
+
def set_argv(*args)
|
13
|
+
Object.send(:remove_const, "ARGV")
|
14
|
+
Object.const_set("ARGV", args)
|
15
|
+
end
|
16
|
+
|
17
|
+
public
|
18
|
+
|
19
|
+
def test_default_values
|
20
|
+
set_argv("echo")
|
21
|
+
opts = Options.parse
|
22
|
+
assert_equal(".*", opts[:suffix])
|
23
|
+
assert_equal(5.0, opts[:delay])
|
24
|
+
assert_equal(false, opts[:loop])
|
25
|
+
assert_equal(false, opts[:force])
|
26
|
+
assert_equal("%%", opts[:token])
|
27
|
+
assert_equal(false, opts[:verbose])
|
28
|
+
assert_equal("echo", opts[:shell_command])
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_suffix
|
32
|
+
set_argv("-s", "\\.rb", "echo")
|
33
|
+
assert_equal("\\.rb", Options.parse[:suffix])
|
34
|
+
set_argv("--suffix", "\\.rb", "echo")
|
35
|
+
assert_equal("\\.rb", Options.parse[:suffix])
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_delay
|
39
|
+
set_argv("-d", "12.3", "echo")
|
40
|
+
assert_equal(12.3, Options.parse[:delay])
|
41
|
+
set_argv("--delay", "12.3", "echo")
|
42
|
+
assert_equal(12.3, Options.parse[:delay])
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_loop
|
46
|
+
set_argv("-l", "echo")
|
47
|
+
assert_equal(true, Options.parse[:loop])
|
48
|
+
set_argv("--loop", "echo")
|
49
|
+
assert_equal(true, Options.parse[:loop])
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_force
|
53
|
+
set_argv("-f", "echo")
|
54
|
+
assert_equal(true, Options.parse[:force])
|
55
|
+
set_argv("--force", "echo")
|
56
|
+
assert_equal(true, Options.parse[:force])
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_token
|
60
|
+
set_argv("-t", "FILE", "echo")
|
61
|
+
assert_equal("FILE", Options.parse[:token])
|
62
|
+
set_argv("--token", "FILE", "echo")
|
63
|
+
assert_equal("FILE", Options.parse[:token])
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_verbose
|
67
|
+
set_argv("-V", "echo")
|
68
|
+
assert_equal(true, Options.parse[:verbose])
|
69
|
+
set_argv("--verbose", "echo")
|
70
|
+
assert_equal(true, Options.parse[:verbose])
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
data/test/test_helper.rb
ADDED
data/test/watch_tc.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class Watch_Commandline_TestCase < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
# We need a set of tests that run "out of process" in a way that let's us
|
6
|
+
# both verify that the watch script executable is sort of working, as well
|
7
|
+
# as to verify that --help, --version, and error conditions result in an
|
8
|
+
# appropriate behavior the user will understand.
|
9
|
+
|
10
|
+
# Note that since these tests are run "out of process" by using the system
|
11
|
+
# command (back-tick style), that these test runs do generate input into
|
12
|
+
# the code-coverage report. There aren't many, so it really makes little
|
13
|
+
# difference.
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Execute the "watch" script and capture all output, both stdout and
|
18
|
+
# stderr, and pass it to the caller with yield.
|
19
|
+
def watch(*commands)
|
20
|
+
commands.each do |command|
|
21
|
+
yield (`ruby -Ilib bin/watch #{command} 2>&1`)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
def test_no_command_provided
|
28
|
+
watch("") do |response|
|
29
|
+
assert_match("Error: shell command is required", response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_options_unknown
|
34
|
+
watch("-x", "--xyzzy") do |response|
|
35
|
+
assert_match("Error: unknown argument", response)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_options_version
|
40
|
+
watch("-v", "--version") do |response|
|
41
|
+
assert_match("watch (#{DirectoryMonitor::VERSION})", response)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_options_help
|
46
|
+
watch("-h", "--help") do |response|
|
47
|
+
assert_match("Synopsis", response) # Spot check the help message.
|
48
|
+
assert_match("Executes", response)
|
49
|
+
assert_match("Usage", response)
|
50
|
+
assert_match("Show this message", response)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: directory_monitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David A. Love
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: trollop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.9'
|
69
|
+
description: Monitor a directory for file changes.
|
70
|
+
email:
|
71
|
+
- DALove1025@gmail.com
|
72
|
+
executables:
|
73
|
+
- watch
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- bin/watch
|
83
|
+
- directory_monitor.gemspec
|
84
|
+
- lib/directory_monitor.rb
|
85
|
+
- lib/directory_monitor/directory_monitor.rb
|
86
|
+
- lib/directory_monitor/options.rb
|
87
|
+
- lib/directory_monitor/version.rb
|
88
|
+
- test/directory_monitor_tc.rb
|
89
|
+
- test/options_tc.rb
|
90
|
+
- test/test_helper.rb
|
91
|
+
- test/watch_tc.rb
|
92
|
+
homepage: https://github.com/DALove1025/directory_monitor#directorymonitor
|
93
|
+
licenses:
|
94
|
+
- MIT
|
95
|
+
metadata: {}
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 2.0.3
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: A Directory Monitor
|
116
|
+
test_files:
|
117
|
+
- test/directory_monitor_tc.rb
|
118
|
+
- test/options_tc.rb
|
119
|
+
- test/test_helper.rb
|
120
|
+
- test/watch_tc.rb
|