watchly 0.1.0 → 0.2.0
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 +4 -4
- data/README.md +129 -11
- data/lib/watchly/changeset.rb +4 -3
- data/lib/watchly/snapshot.rb +16 -9
- data/lib/watchly/version.rb +1 -1
- data/lib/watchly/watcher.rb +6 -5
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c133cfc132b5dcfb5c57e0835bd80e2dd7239af7c1631f9d72ccfc7acbb8f60
|
|
4
|
+
data.tar.gz: 2516f37a4cd11b70705b57a77b26d803989322d07cc265751e246dd5394db9ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51d950ab49d3e18e064ee3e24263789aefe7b2f4614d78f5a98e858fdc00b75a2992f4c24d02f841f76e2a38f058e9b4960de048b019a61e11f3916c9c6733b1
|
|
7
|
+
data.tar.gz: b0a543c5fd955d3d4fb206cbfbb793f2a18e2c67d9dca68feb09ccb98647cfc73d42a0a9b91128b8561633701c74b6a3d3af89ebd8472a61a1403753788ed2ae
|
data/README.md
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
# Watchly
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
Watchly is a lightweight, dependency-free, polling-based file watcher for Ruby.
|
|
4
|
-
It watches one or more glob patterns and reports
|
|
6
|
+
It watches one or more files, directories, or glob patterns and reports file
|
|
7
|
+
changes in a simple, predictable way.
|
|
8
|
+
|
|
9
|
+
Watchly is designed around *batch semantics*: when files change, a single
|
|
10
|
+
callback is triggered with a complete description of what changed.
|
|
11
|
+
|
|
12
|
+
An official command-line interface is available via the
|
|
13
|
+
[watchly-cli][watchly-cli] gem.
|
|
14
|
+
|
|
15
|
+
---
|
|
5
16
|
|
|
6
17
|
## Installation
|
|
7
18
|
|
|
@@ -15,9 +26,11 @@ Or in your Gemfile:
|
|
|
15
26
|
gem 'watchly'
|
|
16
27
|
```
|
|
17
28
|
|
|
29
|
+
---
|
|
30
|
+
|
|
18
31
|
## Usage
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
### Initialize a watcher
|
|
21
34
|
|
|
22
35
|
```ruby
|
|
23
36
|
require 'watchly'
|
|
@@ -28,30 +41,135 @@ watcher = Watchly::Watcher.new '**/*'
|
|
|
28
41
|
# with a different interval
|
|
29
42
|
watcher = Watchly::Watcher.new '**/*', interval: 2.0
|
|
30
43
|
|
|
31
|
-
#
|
|
44
|
+
# watch a directory recursively
|
|
45
|
+
watcher = Watchly::Watcher.new 'src'
|
|
46
|
+
|
|
47
|
+
# with multiple targets
|
|
32
48
|
watcher = Watchly::Watcher.new 'spec/**/*.rb', 'lib/*.*'
|
|
33
49
|
```
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
Directory targets are watched recursively. Like Ruby's default glob behavior,
|
|
52
|
+
implicit directory expansion excludes dotfiles. Dotfiles can still be watched
|
|
53
|
+
when passed as literal files or matched by an explicit glob pattern.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### Watch for changes
|
|
58
|
+
|
|
59
|
+
When changes are detected, Watchly yields a Changeset object describing
|
|
60
|
+
all filesystem changes since the last poll.
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
watcher.on_change do |changes|
|
|
64
|
+
changes.each do |type, path|
|
|
65
|
+
puts "#{type}: #{path}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This will output events such as:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
added: spec/new_spec.rb
|
|
74
|
+
modified: lib/watchly.rb
|
|
75
|
+
removed: tmp/debug.log
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### Accessing specific change types
|
|
81
|
+
|
|
82
|
+
The `changes` object exposes separate collections for each type of change:
|
|
83
|
+
|
|
84
|
+
- `changes.added`
|
|
85
|
+
- `changes.removed`
|
|
86
|
+
- `changes.modified`
|
|
87
|
+
|
|
88
|
+
Example:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
watcher.on_change do |changes|
|
|
92
|
+
puts "Added files:"
|
|
93
|
+
changes.added.each { |path| puts " + #{path}" }
|
|
94
|
+
|
|
95
|
+
puts "Modified files:"
|
|
96
|
+
changes.modified.each { |path| puts " * #{path}" }
|
|
97
|
+
|
|
98
|
+
puts "Removed files:"
|
|
99
|
+
changes.removed.each { |path| puts " - #{path}" }
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Working with actionable files
|
|
106
|
+
|
|
107
|
+
In many cases, you only want to work with files that currently exist.
|
|
108
|
+
|
|
109
|
+
For this purpose, `changes.files` provides a convenient, pre-filtered list of
|
|
110
|
+
**actionable files**:
|
|
111
|
+
|
|
112
|
+
- Includes: added and modified files
|
|
113
|
+
- Excludes: removed files
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
watcher.on_change do |changes|
|
|
117
|
+
next if changes.empty?
|
|
118
|
+
|
|
119
|
+
puts "Processing files:"
|
|
120
|
+
changes.files.each do |path|
|
|
121
|
+
puts " - #{path}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A changeset that only contains removed files is considered empty.
|
|
127
|
+
This prevents unnecessary processing when files are deleted.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Checking for changes
|
|
132
|
+
|
|
133
|
+
You can test whether a changeset contains any actionable files:
|
|
36
134
|
|
|
37
135
|
```ruby
|
|
38
136
|
watcher.on_change do |changes|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
137
|
+
if changes.any?
|
|
138
|
+
puts "Files changed:"
|
|
139
|
+
changes.files.each { |path| puts " - #{path}" }
|
|
140
|
+
else
|
|
141
|
+
puts "Files were removed:"
|
|
142
|
+
changes.removed.each { |path| puts " - #{path}" }
|
|
143
|
+
end
|
|
42
144
|
end
|
|
43
145
|
```
|
|
44
146
|
|
|
45
|
-
|
|
147
|
+
Or explicitly:
|
|
46
148
|
|
|
47
149
|
```ruby
|
|
48
|
-
#
|
|
150
|
+
changes.empty? # => true if no added or modified files
|
|
151
|
+
changes.any? # => true if at least one actionable file exists
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The on_change callback is triggered for any filesystem change, including deletions.
|
|
155
|
+
The `any?` and `empty?` helpers reflect whether there are actionable files
|
|
156
|
+
(added or modified).
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### Stopping the watcher
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
# Mainly useful for tests, but can be called from another thread
|
|
49
164
|
watcher.stop
|
|
50
165
|
```
|
|
51
166
|
|
|
167
|
+
---
|
|
168
|
+
|
|
52
169
|
## Contributing / Support
|
|
53
170
|
|
|
54
|
-
If you experience any issue, have a question, or
|
|
55
|
-
|
|
171
|
+
If you experience any issue, have a question, or wish to contribute,
|
|
172
|
+
feel free to [open an issue][issues].
|
|
56
173
|
|
|
57
174
|
[issues]: https://github.com/dannyben/watchly/issues
|
|
175
|
+
[watchly-cli]: https://github.com/dannyben/watchly-cli
|
data/lib/watchly/changeset.rb
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
module Watchly
|
|
2
2
|
class Changeset
|
|
3
|
-
attr_reader :added, :removed, :modified
|
|
3
|
+
attr_reader :added, :removed, :modified, :files
|
|
4
4
|
|
|
5
5
|
def initialize(added:, removed:, modified:)
|
|
6
6
|
@added = added.freeze
|
|
7
7
|
@removed = removed.freeze
|
|
8
8
|
@modified = modified.freeze
|
|
9
|
+
@files = (added + modified).freeze
|
|
9
10
|
freeze
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def empty? =
|
|
13
|
+
def empty? = files.empty?
|
|
13
14
|
def any? = !empty?
|
|
14
|
-
def to_h = { added: added, removed: removed, modified: modified }
|
|
15
|
+
def to_h = { added: added, removed: removed, modified: modified, files: files }
|
|
15
16
|
|
|
16
17
|
def each
|
|
17
18
|
return enum_for(:each) unless block_given?
|
data/lib/watchly/snapshot.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
module Watchly
|
|
2
2
|
class Snapshot
|
|
3
|
-
attr_reader :
|
|
3
|
+
attr_reader :targets, :files
|
|
4
|
+
alias globs targets
|
|
4
5
|
|
|
5
|
-
def initialize(
|
|
6
|
-
@
|
|
7
|
-
@files = capture(
|
|
6
|
+
def initialize(targets)
|
|
7
|
+
@targets = targets
|
|
8
|
+
@files = capture(targets)
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def diff(other)
|
|
@@ -32,16 +33,22 @@ module Watchly
|
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
def capture(
|
|
36
|
-
Array(
|
|
37
|
-
.flat_map { |
|
|
38
|
-
.uniq
|
|
36
|
+
def capture(targets)
|
|
37
|
+
Array(targets)
|
|
38
|
+
.flat_map { |target| expand_target target }
|
|
39
|
+
.uniq { |path| File.expand_path path }
|
|
39
40
|
.each_with_object({}) do |path, acc|
|
|
40
41
|
next unless File.file? path
|
|
41
42
|
|
|
42
43
|
stat = File.stat path
|
|
43
|
-
acc[path] = [stat.mtime
|
|
44
|
+
acc[path] = [stat.mtime, stat.size]
|
|
44
45
|
end
|
|
45
46
|
end
|
|
47
|
+
|
|
48
|
+
def expand_target(target)
|
|
49
|
+
return Dir.glob File.join(target, '**', '*') if File.directory? target
|
|
50
|
+
|
|
51
|
+
Dir.glob target
|
|
52
|
+
end
|
|
46
53
|
end
|
|
47
54
|
end
|
data/lib/watchly/version.rb
CHANGED
data/lib/watchly/watcher.rb
CHANGED
|
@@ -2,10 +2,11 @@ module Watchly
|
|
|
2
2
|
class Watcher
|
|
3
3
|
DEFAULT_INTERVAL = 1.0
|
|
4
4
|
|
|
5
|
-
attr_reader :
|
|
5
|
+
attr_reader :targets, :interval, :stopped
|
|
6
|
+
alias globs targets
|
|
6
7
|
|
|
7
|
-
def initialize(*
|
|
8
|
-
@
|
|
8
|
+
def initialize(*targets, interval: nil)
|
|
9
|
+
@targets = targets
|
|
9
10
|
@interval = interval || DEFAULT_INTERVAL
|
|
10
11
|
@stopped = false
|
|
11
12
|
end
|
|
@@ -15,12 +16,12 @@ module Watchly
|
|
|
15
16
|
def on_change
|
|
16
17
|
raise ArgumentError, 'Block required' unless block_given?
|
|
17
18
|
|
|
18
|
-
previous = Snapshot.new
|
|
19
|
+
previous = Snapshot.new targets
|
|
19
20
|
|
|
20
21
|
until stopped
|
|
21
22
|
sleep interval
|
|
22
23
|
|
|
23
|
-
current = Snapshot.new
|
|
24
|
+
current = Snapshot.new targets
|
|
24
25
|
next if previous == current
|
|
25
26
|
|
|
26
27
|
changes = previous.diff current
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: watchly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Danny Ben Shitrit
|
|
@@ -10,7 +10,7 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: A small, dependency-free, polling-based library that watches one or more
|
|
13
|
-
glob patterns and reports on change
|
|
13
|
+
files, directories, or glob patterns and reports on change
|
|
14
14
|
email: db@dannyben.com
|
|
15
15
|
executables: []
|
|
16
16
|
extensions: []
|
|
@@ -44,7 +44,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
45
|
version: '0'
|
|
46
46
|
requirements: []
|
|
47
|
-
rubygems_version: 4.0.
|
|
47
|
+
rubygems_version: 4.0.17
|
|
48
48
|
specification_version: 4
|
|
49
49
|
summary: Lightweight, polling-based file system watcher
|
|
50
50
|
test_files: []
|