zwerg 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cc6b79e2842c2d7fd73d23c994432be87e4a97f3a1e23c3596fe4658a1f0a6b
4
- data.tar.gz: c3f81dcdba96f0255be18f863c28f2cbfe08eecee52d06b7917308b682f6e031
3
+ metadata.gz: 9f5101ab9cfd3931ba280fd1f99ea072f390a0b729d746f733a53de3b984c81c
4
+ data.tar.gz: ae2a722ad3f32d7942d89e22e4afc5b6de3094dcf5d411e68a51827dbb50dbe1
5
5
  SHA512:
6
- metadata.gz: bad591c47d47f91a80feb8c0fb3284165d62790dd2ac1e6c0577db42f7dfecd07a92e366897a8d7a4d1734bb984e644ec22fb6838c37107f5b0ecc406afdcc82
7
- data.tar.gz: a4be1ecd56849f66b15cc99911258e498a450714cdfa310e3be33b7ddb8a51dfcd6804ae92191937e95f8a8ca0247421e42d8a71574b8578578a9e6bac734b5d
6
+ metadata.gz: b7d330a786f9d42e790df64840ee513195ce15505d899d2cb4f8e6228415eca0fcfa8374b1c4625aaa7fd2c83b0c83b8adfcc210341d4937f7ddf7f7191819b6
7
+ data.tar.gz: 77c468b8cb4ff0e8d8da43ebaccee257a0b629855ba6ff92c8be62e5f845ff2872f5440d1c0b95c345afb9785583a24344a55b3a7f3ec847b8f3f517a7815400
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ## 0.1.2
2
+
3
+ * Ignore "access" event by default
4
+
5
+ ## 0.1.1
6
+
7
+ * Imeplement "debounce" feature
8
+
9
+ ## 0.1.0
10
+
11
+ * Initial Release
data/README.md CHANGED
@@ -40,6 +40,7 @@ gem install zwerg
40
40
  watches:
41
41
  - path: "./src"
42
42
  recursive: true
43
+ debounce: 500
43
44
  patterns:
44
45
  - "*.rb"
45
46
  - "*.yml"
@@ -63,6 +64,7 @@ Each watch entry can have the following properties:
63
64
 
64
65
  - `path`: The file or directory to watch (required)
65
66
  - `recursive`: Whether to watch subdirectories (default: true)
67
+ - `debounce`: Wait time in milliseconds before firing actions for the same file (default: 500)
66
68
  - `patterns`: Array of glob patterns to match files (optional, matches all if empty)
67
69
  - `actions`: Array of actions to execute when files change (required)
68
70
 
@@ -78,6 +80,32 @@ You can use any shell command, including complex operations with pipes, redirect
78
80
  - command: "if [ -f {{file_path}} ]; then echo 'File exists: {{file_name}}' >> changes.log; fi"
79
81
  ```
80
82
 
83
+ ### Debounce Feature
84
+
85
+ The debounce feature prevents actions from firing too frequently for the same file. When a file changes multiple times within the debounce period, only the last change will trigger the actions. This is implemented using Zwerg's built-in debouncer.
86
+
87
+ ```yaml
88
+ - path: "./src"
89
+ debounce: 1000 # Wait 1000ms (1 second) before firing actions
90
+ actions:
91
+ - command: "echo 'File changed: {{file_path}}'"
92
+ ```
93
+
94
+ **How it works:**
95
+ - Each file path is tracked separately with its own debounce timer
96
+ - When a file changes, any existing timer for that file is cancelled
97
+ - A new timer is started for the specified debounce period
98
+ - If the file changes again before the timer expires, the timer is reset
99
+ - Actions only execute when the timer completes without interruption
100
+
101
+ This is particularly useful for:
102
+ - Files that are saved multiple times in quick succession by editors
103
+ - Build processes that modify multiple files rapidly
104
+ - Log files that are updated frequently
105
+ - Preventing duplicate actions when files are modified in batches
106
+
107
+ The debounce value is specified in milliseconds. If not specified, the default is 500ms.
108
+
81
109
  ### Variable Substitution
82
110
 
83
111
  The following variables can be used in action configurations:
@@ -87,7 +115,6 @@ The following variables can be used in action configurations:
87
115
  - `{{file_name}}`: Name of the file (including extension)
88
116
  - `{{file_base}}`: Name of the file without extension
89
117
  - `{{file_ext}}`: File extension (including the dot)
90
- - `{{event_type}}`: Type of event (create, modify, remove, access)
91
118
 
92
119
  ## Example Configuration
93
120
 
data/example_zwerg.yml CHANGED
@@ -5,6 +5,7 @@ watches:
5
5
  # Watch a source directory for Ruby files
6
6
  - path: "./src"
7
7
  recursive: true
8
+ debounce: 300 # Wait 300ms before firing actions for the same file
8
9
  patterns:
9
10
  - "*.rb"
10
11
  - "*.yml"
@@ -15,6 +16,7 @@ watches:
15
16
  # Watch a documentation directory
16
17
  - path: "./docs"
17
18
  recursive: true
19
+ debounce: 1000 # Wait 1 second for documentation files
18
20
  patterns:
19
21
  - "*.md"
20
22
  - "*.txt"
@@ -25,23 +27,32 @@ watches:
25
27
  # Watch configuration files in the root directory
26
28
  - path: "."
27
29
  recursive: false
30
+ debounce: 2000 # Wait 2 seconds for config files (they might be edited multiple times)
28
31
  patterns:
29
32
  - "*.yml"
30
33
  - "*.yaml"
31
34
  - "*.json"
32
35
  actions:
33
- - command: "echo '[{{event_type}}] Config file changed: {{file_name}}'"
36
+ - command: "echo 'Config file changed: {{file_name}}'"
34
37
  - command: "echo 'Config changed: {{file_path}}' | mail -s 'Config Alert' admin@example.com"
35
38
 
36
39
  # Watch for log files and compress them when they get large
37
40
  - path: "./logs"
38
41
  recursive: false
42
+ debounce: 5000 # Wait 5 seconds for log files (they change frequently)
39
43
  patterns:
40
44
  - "*.log"
41
45
  actions:
42
46
  - command: "if [ $(stat -c%s {{file_path}}) -gt 1048576 ]; then gzip {{file_path}}; fi"
43
47
 
44
48
  # Each action is a shell command that will be executed when files change
49
+ #
50
+ # Configuration options:
51
+ # - path: Directory or file to watch (required)
52
+ # - recursive: Watch subdirectories (default: true)
53
+ # - debounce: Wait time in milliseconds before firing actions (default: 500ms)
54
+ # - patterns: Glob patterns to match files (optional, matches all if empty)
55
+ # - actions: Commands to execute when files change (required)
45
56
 
46
57
  # Available variables for substitution:
47
58
  # - {{file_path}}: Full path to the changed file
@@ -49,4 +60,3 @@ watches:
49
60
  # - {{file_name}}: Name of the file (including extension)
50
61
  # - {{file_base}}: Name of the file without extension
51
62
  # - {{file_ext}}: File extension (including the dot)
52
- # - {{event_type}}: Type of event (create, modify, remove, access)
data/exe/zwerg CHANGED
@@ -26,6 +26,7 @@ def show_help
26
26
  watches:
27
27
  - path: "./src"
28
28
  recursive: true
29
+ debounce: 500
29
30
  patterns:
30
31
  - "*.rb"
31
32
  - "*.yml"
@@ -39,7 +40,6 @@ def show_help
39
40
  {{file_name}} Name of the file (including extension)
40
41
  {{file_base}} Name of the file without extension
41
42
  {{file_ext}} File extension (including the dot)
42
- {{event_type}} Type of event (create, modify, remove, access)
43
43
 
44
44
  For more information, visit: https://github.com/y-yagi/zwerg
45
45
  HELP
@@ -38,15 +38,6 @@ module Zwerg
38
38
  .gsub("{{file_name}}", @file_name)
39
39
  .gsub("{{file_base}}", @file_base)
40
40
  .gsub("{{file_ext}}", @file_ext)
41
- .gsub("{{event_type}}", determine_event_type)
42
- end
43
-
44
- def determine_event_type
45
- return "create" if @event.kind.create?
46
- return "modify" if @event.kind.modify?
47
- return "remove" if @event.kind.remove?
48
- return "access" if @event.kind.access?
49
- "unknown"
50
41
  end
51
42
  end
52
43
  end
data/lib/zwerg/config.rb CHANGED
@@ -31,7 +31,8 @@ module Zwerg
31
31
  path: watch_config["path"],
32
32
  recursive: watch_config.fetch("recursive", true),
33
33
  patterns: watch_config["patterns"] || [],
34
- actions: watch_config["actions"] || []
34
+ actions: watch_config["actions"] || [],
35
+ debounce: watch_config.fetch("debounce", 500)
35
36
  }
36
37
  end
37
38
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zwerg
4
+ class Debouncer
5
+ def initialize
6
+ @timers = {}
7
+ @mutex = Mutex.new
8
+ end
9
+
10
+ def debounce(key, delay_ms, &block)
11
+ @mutex.synchronize do
12
+ # Cancel existing timer for this key
13
+ if @timers[key]
14
+ @timers[key].kill
15
+ end
16
+
17
+ # Create new timer
18
+ @timers[key] = Thread.new do
19
+ sleep(delay_ms / 1000.0) # Convert ms to seconds
20
+
21
+ @mutex.synchronize do
22
+ @timers.delete(key)
23
+ end
24
+
25
+ block.call
26
+ end
27
+ end
28
+ end
29
+
30
+ def clear
31
+ @mutex.synchronize do
32
+ @timers.each_value(&:kill)
33
+ @timers.clear
34
+ end
35
+ end
36
+
37
+ def pending_count
38
+ @mutex.synchronize do
39
+ @timers.size
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/zwerg/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Zwerg
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/zwerg/watcher.rb CHANGED
@@ -7,6 +7,7 @@ module Zwerg
7
7
  def initialize(config)
8
8
  @config = config
9
9
  @watchers = []
10
+ @debouncer = Debouncer.new
10
11
  end
11
12
 
12
13
  def start
@@ -28,6 +29,7 @@ module Zwerg
28
29
  end
29
30
 
30
31
  def stop
32
+ @debouncer.clear
31
33
  @watchers.each(&:stop)
32
34
  @watchers.clear
33
35
  end
@@ -42,7 +44,7 @@ module Zwerg
42
44
  return
43
45
  end
44
46
 
45
- puts "Watching: #{path} (recursive: #{watch_config[:recursive]})"
47
+ puts "Watching: #{path} (recursive: #{watch_config[:recursive]}, debounce: #{watch_config[:debounce]}ms)"
46
48
 
47
49
  watcher = Watchcat.watch(
48
50
  path,
@@ -56,11 +58,17 @@ module Zwerg
56
58
  end
57
59
 
58
60
  def handle_file_event(event, watch_config)
61
+ return if event.kind.access?
62
+
59
63
  event.paths.each do |file_path|
60
64
  next unless should_process_file?(file_path, watch_config[:patterns])
61
65
 
62
- puts "File changed: #{file_path}"
63
- execute_actions(file_path, event, watch_config[:actions])
66
+ # Use debouncer to delay action execution
67
+ debounce_key = "#{watch_config[:path]}:#{file_path}"
68
+ @debouncer.debounce(debounce_key, watch_config[:debounce]) do
69
+ puts "File changed: #{file_path}"
70
+ execute_actions(file_path, event, watch_config[:actions])
71
+ end
64
72
  end
65
73
  end
66
74
 
data/lib/zwerg.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "zwerg/version"
4
4
  require_relative "zwerg/watcher"
5
5
  require_relative "zwerg/config"
6
6
  require_relative "zwerg/action_executor"
7
+ require_relative "zwerg/debouncer"
7
8
 
8
9
  module Zwerg
9
10
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zwerg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuji Yaginuma
@@ -44,6 +44,7 @@ executables:
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
+ - CHANGELOG.md
47
48
  - CODE_OF_CONDUCT.md
48
49
  - LICENSE.txt
49
50
  - README.md
@@ -53,6 +54,7 @@ files:
53
54
  - lib/zwerg.rb
54
55
  - lib/zwerg/action_executor.rb
55
56
  - lib/zwerg/config.rb
57
+ - lib/zwerg/debouncer.rb
56
58
  - lib/zwerg/version.rb
57
59
  - lib/zwerg/watcher.rb
58
60
  homepage: https://github.com/y-yagi/zwerg