├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── logo
├── icon.png
├── icon.svg
├── iconv2.png
├── iconv2.svg
├── logotype_horizontal.png
├── logotype_horizontal.svg
├── logotype_vertical.png
└── logotype_vertical.svg
├── shard.yml
├── spec
├── spec_helper.cr
└── watcher_spec.cr
└── src
├── watcher.cr
└── watcher
└── version.cr
/.gitignore:
--------------------------------------------------------------------------------
1 | /doc/
2 | /lib/
3 | /bin/
4 | /.shards/
5 |
6 | # Libraries don't need dependency lock
7 | # Dependencies will be locked in application that uses them
8 | /shard.lock
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: crystal
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Faustino Aguilar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://travis-ci.org/faustinoaq/watcher)
4 |
5 | Crystal shard to watch file changes. This shard use the same code implemented [here (Guardian)](https://github.com/f/guardian/blob/master/src/guardian/watcher.cr#L45) and [here (Sentry)](https://github.com/samueleaton/sentry/blob/master/src/sentry.cr#L52).
6 |
7 | ## Installation
8 |
9 | Add this to your application's `shard.yml`:
10 |
11 | ```yaml
12 | dependencies:
13 | watcher:
14 | github: faustinoaq/watcher
15 | ```
16 |
17 | ## Usage
18 |
19 | Use the keyword `watch` to watch files or file groups, for example:
20 |
21 | ```crystal
22 | require "watcher"
23 |
24 | watch "src/assets/js/*.js" do |event|
25 | event.on_change do |files|
26 | files.each do |file, {first, timestamp}|
27 | puts "File #{file} has changed at #{timestamp}"
28 | end
29 | # ...
30 | end
31 | end
32 | ```
33 |
34 | Also you can have more than one watcher, just use `spawn`
35 |
36 | ```crystal
37 | spawn do
38 | watch ["src/assets/*.css", "src/views/*.html"] do |event|
39 | event.on_change do
40 | # ...
41 | end
42 | end
43 | end
44 |
45 | # Other watcher
46 | watch ... do |event|
47 | #...
48 | end
49 | ```
50 |
51 | And you can change time interval for a watcher.
52 |
53 | ```crystal
54 | watch "public/*.json", interval: 0.5 do |event|
55 | event.on_change do
56 | # ...
57 | end
58 | end
59 | ```
60 |
61 | Also you can use `Watcher.watch` instead of `watch`.
62 |
63 | # How does it work?
64 |
65 | Watcher uses timestamps to check file changes every second, if you want some more advanced then you can use [Watchbird](https://github.com/agatan/watchbird) that uses `libnotify` to check events like modify, access and delete but just work in Linux for now.
66 |
67 | ## Contributing
68 |
69 | 1. Fork it ( https://github.com/faustinoaq/watcher/fork )
70 | 2. Create your feature branch (git checkout -b my-new-feature)
71 | 3. Commit your changes (git commit -am 'Add some feature')
72 | 4. Push to the branch (git push origin my-new-feature)
73 | 5. Create a new Pull Request
74 |
75 | ## Contributors
76 |
77 | - [faustinoaq](https://github.com/faustinoaq) Faustino Aguilar - creator, maintainer
78 |
--------------------------------------------------------------------------------
/logo/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faustinoaq/watcher/d0ffd55576bf0446fe3565bc96c28139fcac776a/logo/icon.png
--------------------------------------------------------------------------------
/logo/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
21 |
--------------------------------------------------------------------------------
/logo/iconv2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faustinoaq/watcher/d0ffd55576bf0446fe3565bc96c28139fcac776a/logo/iconv2.png
--------------------------------------------------------------------------------
/logo/iconv2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
--------------------------------------------------------------------------------
/logo/logotype_horizontal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faustinoaq/watcher/d0ffd55576bf0446fe3565bc96c28139fcac776a/logo/logotype_horizontal.png
--------------------------------------------------------------------------------
/logo/logotype_horizontal.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
49 |
--------------------------------------------------------------------------------
/logo/logotype_vertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/faustinoaq/watcher/d0ffd55576bf0446fe3565bc96c28139fcac776a/logo/logotype_vertical.png
--------------------------------------------------------------------------------
/logo/logotype_vertical.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
48 |
--------------------------------------------------------------------------------
/shard.yml:
--------------------------------------------------------------------------------
1 | name: watcher
2 | version: 0.3.0
3 |
4 | authors:
5 | - Faustino Aguilar
6 |
7 | license: MIT
8 |
--------------------------------------------------------------------------------
/spec/spec_helper.cr:
--------------------------------------------------------------------------------
1 | require "spec"
2 | require "../src/watcher"
3 |
4 | TEST_FILE = "src/watcher.cr"
5 | TIMESTAMP = Watcher.timestamp_for(TEST_FILE)
6 |
--------------------------------------------------------------------------------
/spec/watcher_spec.cr:
--------------------------------------------------------------------------------
1 | require "./spec_helper"
2 |
3 | describe Watcher do
4 | it "create timestamps correctly" do
5 | TIMESTAMP.size.should eq(18)
6 | end
7 |
8 | it "verify Watcher::WatchEvent.event.change" do
9 | Watcher.watch(TEST_FILE) do |event|
10 | event.changed = true
11 | event.changed.should eq(true)
12 | break
13 | end
14 | end
15 |
16 | it "verify Watcher::WatchEvent.event.files" do
17 | Watcher.watch(TEST_FILE) do |event|
18 | event.files.should eq({TEST_FILE => {true, TIMESTAMP}})
19 | break
20 | end
21 | end
22 |
23 | it "verify default WatcherEvent interval" do
24 | Watcher.watch(TEST_FILE) do |event|
25 | event.interval.should eq(1)
26 | break
27 | end
28 | end
29 |
30 | it "change WatcherEvent interval" do
31 | Watcher.watch(TEST_FILE, interval: 0.5) do |event|
32 | event.interval.should eq(0.5)
33 | break
34 | end
35 | end
36 |
37 | it "more than one watcher" do
38 | spawn do
39 | Watcher.watch(TEST_FILE) do |event|
40 | sleep 1
41 | File.delete("spec/foo").should eq(nil)
42 | break
43 | end
44 | end
45 | Watcher.watch(TEST_FILE) do |event|
46 | File.write("spec/foo", "")
47 | sleep 2
48 | break
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/src/watcher.cr:
--------------------------------------------------------------------------------
1 | require "./watcher/*"
2 |
3 | module Watcher
4 | # Class to save file changes
5 | private class WatchEvent
6 | property changed = false, files = {} of String => Tuple(Bool, String)
7 | getter interval
8 |
9 | def initialize(@interval : Int32 | Float64)
10 | end
11 |
12 | # Allow to yield a block when a file changes
13 | def on_change
14 | yield files if changed
15 | end
16 | end
17 |
18 | # Get file timestamp using File.stat
19 | def self.timestamp_for(file : String)
20 | File.info(file).modification_time.to_s("%Y%m%d%H%M%S.%L")
21 | end
22 |
23 | private def self.scanner(files, event)
24 | event.changed = false
25 | Dir.glob(files) do |file|
26 | timestamp = timestamp_for(file)
27 | if (event.files[file]? && event.files[file].last != timestamp)
28 | event.files[file] = {false, timestamp}
29 | event.changed = true
30 | elsif event.files[file]?.nil?
31 | event.files[file] = {true, timestamp}
32 | event.changed = true
33 | end
34 | end
35 | event
36 | end
37 |
38 | # Allow to watch file changes using Watcher.watch
39 | def self.watch(files, interval : Int32 | Float64)
40 | event = WatchEvent.new(interval)
41 | loop do
42 | event = scanner(files, event)
43 | yield event
44 | sleep event.interval
45 | end
46 | end
47 |
48 | def self.watch(files)
49 | self.watch(files, 1) do |event|
50 | yield event
51 | end
52 | end
53 | end
54 |
55 | # Allow to watch file changes
56 | def watch(files, interval)
57 | Watcher.watch(files, interval) do |event|
58 | yield event
59 | end
60 | end
61 |
62 | # :ditto:
63 | def watch(files)
64 | watch(files, 1) do |event|
65 | yield event
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/src/watcher/version.cr:
--------------------------------------------------------------------------------
1 | module Watcher
2 | VERSION = "0.3.0"
3 | end
4 |
--------------------------------------------------------------------------------