watchly 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f99ea235b8e5f9266131b1e25353c54c6a5c725cb9fca3c2ec7a503ae84bd73b
4
+ data.tar.gz: 170a74f2b7e9642f57d19a141b65b3ab50d20199de44c9f48f0d5a16692732c5
5
+ SHA512:
6
+ metadata.gz: 61e82c4f0f9077a6af68fe057ece3bffacd1030eaeab69f77b6476b71f2dfa172d0f3f934552a45b9e6cd2d1d898f35e896dd5bf1d8dca59d53e16b2dca47a1c
7
+ data.tar.gz: 30bcceb7691daab5d6def2ff15a5284ebcb89c2a492c83020713a8bea3db9ec470ed732dccc197e65f8f507e1e6a882a1e7ec884635a1286a2d797dac4c4d6f1
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Watchly
2
+
3
+ Watchly is a lightweight, dependency-free, polling-based file watcher for Ruby.
4
+ It watches one or more glob patterns and reports changes.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ gem install watchly
10
+ ```
11
+
12
+ Or in your Gemfile:
13
+
14
+ ```ruby
15
+ gem 'watchly'
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ **Initialize a watcher:**
21
+
22
+ ```ruby
23
+ require 'watchly'
24
+
25
+ # with the default interval (1 second)
26
+ watcher = Watchly::Watcher.new '**/*'
27
+
28
+ # with a different interval
29
+ watcher = Watchly::Watcher.new '**/*', interval: 2.0
30
+
31
+ # with multiple glob patterns
32
+ watcher = Watchly::Watcher.new 'spec/**/*.rb', 'lib/*.*'
33
+ ```
34
+
35
+ **Watch for changes:**
36
+
37
+ ```ruby
38
+ watcher.on_change do |changes|
39
+ puts "Added: #{changes.added.join(', ')}" if changes.added.any?
40
+ puts "Removed: #{changes.removed.join(', ')}" if changes.removed.any?
41
+ puts "Modified: #{changes.modified.join(', ')}" if changes.modified.any?
42
+ end
43
+ ```
44
+
45
+ **Stop the watcher:**
46
+
47
+ ```ruby
48
+ # Mainly for tests, but can be called from another thread
49
+ watcher.stop
50
+ ```
51
+
52
+ ## Contributing / Support
53
+
54
+ If you experience any issue, have a question, or if you wish
55
+ to contribute, feel free to [open an issue][issues].
56
+
57
+ [issues]: https://github.com/dannyben/watchly/issues
@@ -0,0 +1,24 @@
1
+ module Watchly
2
+ class Changeset
3
+ attr_reader :added, :removed, :modified
4
+
5
+ def initialize(added:, removed:, modified:)
6
+ @added = added.freeze
7
+ @removed = removed.freeze
8
+ @modified = modified.freeze
9
+ freeze
10
+ end
11
+
12
+ def empty? = added.empty? && removed.empty? && modified.empty?
13
+ def any? = !empty?
14
+ def to_h = { added: added, removed: removed, modified: modified }
15
+
16
+ def each
17
+ return enum_for(:each) unless block_given?
18
+
19
+ added.each { |path| yield :added, path }
20
+ removed.each { |path| yield :removed, path }
21
+ modified.each { |path| yield :modified, path }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ module Watchly
2
+ class Snapshot
3
+ attr_reader :globs, :files
4
+
5
+ def initialize(globs)
6
+ @globs = globs
7
+ @files = capture(globs)
8
+ end
9
+
10
+ def diff(other)
11
+ Changeset.new(
12
+ added: added_files(other),
13
+ removed: removed_files(other),
14
+ modified: modified_files(other)
15
+ )
16
+ end
17
+
18
+ def ==(other)
19
+ return false unless other.is_a? self.class
20
+
21
+ files == other.files
22
+ end
23
+
24
+ private
25
+
26
+ def added_files(other) = other.files.keys - files.keys
27
+ def removed_files(other) = files.keys - other.files.keys
28
+
29
+ def modified_files(other)
30
+ (files.keys & other.files.keys).reject do |path|
31
+ files[path] == other.files[path]
32
+ end
33
+ end
34
+
35
+ def capture(globs)
36
+ Array(globs)
37
+ .flat_map { |glob| Dir.glob glob }
38
+ .uniq
39
+ .each_with_object({}) do |path, acc|
40
+ next unless File.file? path
41
+
42
+ stat = File.stat path
43
+ acc[path] = [stat.mtime.to_i, stat.size]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ module Watchly
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,33 @@
1
+ module Watchly
2
+ class Watcher
3
+ DEFAULT_INTERVAL = 1.0
4
+
5
+ attr_reader :globs, :interval, :stopped
6
+
7
+ def initialize(*globs, interval: nil)
8
+ @globs = globs
9
+ @interval = interval || DEFAULT_INTERVAL
10
+ @stopped = false
11
+ end
12
+
13
+ def stop = @stopped = true
14
+
15
+ def on_change
16
+ raise ArgumentError, 'Block required' unless block_given?
17
+
18
+ previous = Snapshot.new globs
19
+
20
+ until stopped
21
+ sleep interval
22
+
23
+ current = Snapshot.new globs
24
+ next if previous == current
25
+
26
+ changes = previous.diff current
27
+ yield changes
28
+
29
+ previous = current
30
+ end
31
+ end
32
+ end
33
+ end
data/lib/watchly.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'watchly/changeset'
2
+ require 'watchly/snapshot'
3
+ require 'watchly/watcher'
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: watchly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Danny Ben Shitrit
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: A small, dependency-free, polling-based library that watches one or more
13
+ glob patterns and reports on change
14
+ email: db@dannyben.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - README.md
20
+ - lib/watchly.rb
21
+ - lib/watchly/changeset.rb
22
+ - lib/watchly/snapshot.rb
23
+ - lib/watchly/version.rb
24
+ - lib/watchly/watcher.rb
25
+ homepage: https://github.com/dannyben/watchly
26
+ licenses:
27
+ - MIT
28
+ metadata:
29
+ bug_tracker_uri: https://github.com/dannyben/watchly/issues
30
+ changelog_uri: https://github.com/dannyben/watchly/blob/master/CHANGELOG.md
31
+ source_code_uri: https://github.com/dannyben/watchly
32
+ rubygems_mfa_required: 'true'
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubygems_version: 4.0.3
48
+ specification_version: 4
49
+ summary: Lightweight, polling-based file system watcher
50
+ test_files: []