-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
watcher-rb: first draft of ruby support
- Loading branch information
Will
committed
Jul 21, 2024
1 parent
a62beba
commit 4030e97
Showing
10 changed files
with
387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
BUNDLE_PATH: "vendor/bundle" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
vendor | ||
tmp | ||
pkg | ||
*.gem | ||
ext/watcher/watcher.cpp | ||
ext/watcher/wtr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
source 'https://rubygems.org' | ||
|
||
gemspec | ||
|
||
gem 'rake' | ||
gem 'rspec' | ||
gem 'solargraph' | ||
gem 'standard' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
PATH | ||
remote: . | ||
specs: | ||
watcher (0.11.0) | ||
|
||
GEM | ||
remote: https://rubygems.org/ | ||
specs: | ||
ast (2.4.2) | ||
backport (1.2.0) | ||
benchmark (0.3.0) | ||
diff-lcs (1.5.1) | ||
e2mmap (0.1.0) | ||
jaro_winkler (1.6.0) | ||
json (2.7.2) | ||
kramdown (2.4.0) | ||
rexml | ||
kramdown-parser-gfm (1.1.0) | ||
kramdown (~> 2.0) | ||
language_server-protocol (3.17.0.3) | ||
lint_roller (1.1.0) | ||
nokogiri (1.16.6-arm64-darwin) | ||
racc (~> 1.4) | ||
parallel (1.24.0) | ||
parser (3.3.4.0) | ||
ast (~> 2.4.1) | ||
racc | ||
racc (1.8.0) | ||
rainbow (3.1.1) | ||
rake (13.2.1) | ||
rbs (2.8.4) | ||
regexp_parser (2.9.2) | ||
reverse_markdown (2.1.1) | ||
nokogiri | ||
rexml (3.3.2) | ||
strscan | ||
rspec (3.13.0) | ||
rspec-core (~> 3.13.0) | ||
rspec-expectations (~> 3.13.0) | ||
rspec-mocks (~> 3.13.0) | ||
rspec-core (3.13.0) | ||
rspec-support (~> 3.13.0) | ||
rspec-expectations (3.13.1) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.13.0) | ||
rspec-mocks (3.13.1) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.13.0) | ||
rspec-support (3.13.1) | ||
rubocop (1.50.2) | ||
json (~> 2.3) | ||
parallel (~> 1.10) | ||
parser (>= 3.2.0.0) | ||
rainbow (>= 2.2.2, < 4.0) | ||
regexp_parser (>= 1.8, < 3.0) | ||
rexml (>= 3.2.5, < 4.0) | ||
rubocop-ast (>= 1.28.0, < 2.0) | ||
ruby-progressbar (~> 1.7) | ||
unicode-display_width (>= 2.4.0, < 3.0) | ||
rubocop-ast (1.30.0) | ||
parser (>= 3.2.1.0) | ||
rubocop-performance (1.16.0) | ||
rubocop (>= 1.7.0, < 2.0) | ||
rubocop-ast (>= 0.4.0) | ||
ruby-progressbar (1.13.0) | ||
solargraph (0.50.0) | ||
backport (~> 1.2) | ||
benchmark | ||
bundler (~> 2.0) | ||
diff-lcs (~> 1.4) | ||
e2mmap | ||
jaro_winkler (~> 1.5) | ||
kramdown (~> 2.3) | ||
kramdown-parser-gfm (~> 1.1) | ||
parser (~> 3.0) | ||
rbs (~> 2.0) | ||
reverse_markdown (~> 2.0) | ||
rubocop (~> 1.38) | ||
thor (~> 1.0) | ||
tilt (~> 2.0) | ||
yard (~> 0.9, >= 0.9.24) | ||
standard (1.28.5) | ||
language_server-protocol (~> 3.17.0.2) | ||
lint_roller (~> 1.0) | ||
rubocop (~> 1.50.2) | ||
standard-custom (~> 1.0.0) | ||
standard-performance (~> 1.0.1) | ||
standard-custom (1.0.2) | ||
lint_roller (~> 1.0) | ||
rubocop (~> 1.50) | ||
standard-performance (1.0.1) | ||
lint_roller (~> 1.0) | ||
rubocop-performance (~> 1.16.0) | ||
strscan (3.1.0) | ||
thor (1.3.1) | ||
tilt (2.4.0) | ||
unicode-display_width (2.5.0) | ||
yard (0.9.36) | ||
|
||
PLATFORMS | ||
arm64-darwin-23 | ||
|
||
DEPENDENCIES | ||
bundler (~> 2.0) | ||
rake | ||
rspec | ||
solargraph | ||
standard | ||
watcher! | ||
|
||
BUNDLED WITH | ||
2.3.26 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# frozen_string_literal: true | ||
|
||
require "bundler/gem_tasks" | ||
require "rspec/core/rake_task" | ||
|
||
RSpec::Core::RakeTask.new(:spec) | ||
|
||
task build_shared_library: ["lib"] do | ||
framework_deps = case RbConfig::CONFIG["host_os"] | ||
when /darwin/ | ||
"-framework CoreServices -framework CoreFoundation" | ||
else | ||
"" | ||
end | ||
version = File.read("../.version").strip | ||
libname = "libwatcher-c-#{version}.so" | ||
sh "c++ -shared -std=c++17 -O2 #{framework_deps} ../watcher-c/src/watcher-c.cpp -I ../watcher-c/include -o lib/#{libname}" | ||
end | ||
|
||
task default: [:clobber, :build_shared_library, :spec] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/usr/bin/env ruby | ||
# frozen_string_literal: true | ||
|
||
require "watcher" | ||
|
||
if ARGV.empty? | ||
puts("Usage: watcher <path>") | ||
exit(1) | ||
end | ||
|
||
path = ARGV[0] | ||
watcher = Watcher::Watch.new(path, method(:puts)) | ||
ObjectSpace.define_finalizer(watcher, Watcher::Watch.method(:finalize).to_proc) | ||
puts("Watching #{path}. Press Enter to stop.") | ||
gets |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'fiddle' | ||
require 'date' | ||
|
||
module Watcher | ||
class CEvent < Fiddle::Struct | ||
layout( | ||
:path_name, | ||
:pointer, | ||
:effect_type, | ||
:int8_t, | ||
:path_type, | ||
:int8_t, | ||
:effect_time, | ||
:int64_t, | ||
:associated_path_name, | ||
:pointer | ||
) | ||
end | ||
|
||
LIB = nil | ||
|
||
def native_solib_file_ending | ||
case RbConfig::CONFIG['host_os'] | ||
when /darwin/ | ||
'so' | ||
when /mswin|mingw|cygwin/ | ||
'dll' | ||
else | ||
'so' | ||
end | ||
end | ||
|
||
def libwatcher_c_lib_path | ||
version = '0.11.0' # hook: tool/release | ||
lib_name = "libwatcher-c-#{version}.#{native_solib_file_ending}" | ||
lib_path = File.join(File.dirname(__FILE__), lib_name) | ||
raise "Library does not exist: '#{lib_path}'" unless File.exist?(lib_path) | ||
|
||
puts("Using library: '#{lib_path}'") | ||
lib_path | ||
end | ||
|
||
def self.lazy_static_solib_handle | ||
return LIB if LIB | ||
|
||
@lib = Fiddle.dlopen(libwatcher_c_lib_path) | ||
@lib.extern('void* wtr_watcher_open(char*, void*, void*)') | ||
@lib.extern('bool wtr_watcher_close(void*)') | ||
@lib | ||
end | ||
|
||
def to_utf8 | ||
return '' if @value.nil? | ||
return @value if @value.is_a?(String) | ||
return @value.to_s.force_encoding('UTF-8') if @value.respond_to?(:to_s) | ||
|
||
raise TypeError | ||
end | ||
|
||
def self.c_event_to_event(c_event) | ||
path_name = as_utf8(c_event.path_name.to_s) | ||
associated_path_name = as_utf8(c_event.associated_path_name.to_s) | ||
effect_type = EffectType.new(c_event.effect_type) | ||
path_type = PathType.new(c_event.path_type) | ||
effect_time = Time.at(c_event.effect_time / 1e9) | ||
Event.new(path_name, effect_type, path_type, effect_time, associated_path_name) | ||
end | ||
|
||
class EffectType | ||
RENAME = 0 | ||
MODIFY = 1 | ||
CREATE = 2 | ||
DESTROY = 3 | ||
OWNER = 4 | ||
OTHER = 5 | ||
|
||
def initialize(value) | ||
@value = value | ||
end | ||
|
||
def to_s | ||
case @value | ||
when RENAME | ||
'RENAME' | ||
when MODIFY | ||
'MODIFY' | ||
when CREATE | ||
'CREATE' | ||
when DESTROY | ||
'DESTROY' | ||
when OWNER | ||
'OWNER' | ||
when OTHER | ||
'OTHER' | ||
else | ||
'UNKNOWN' | ||
end | ||
end | ||
end | ||
|
||
class PathType | ||
DIR = 0 | ||
FILE = 1 | ||
HARD_LINK = 2 | ||
SYM_LINK = 3 | ||
WATCHER = 4 | ||
OTHER = 5 | ||
|
||
def initialize(value) | ||
@value = value | ||
end | ||
|
||
def to_s | ||
case @value | ||
when DIR | ||
'DIR' | ||
when FILE | ||
'FILE' | ||
when HARD_LINK | ||
'HARD_LINK' | ||
when SYM_LINK | ||
'SYM_LINK' | ||
when WATCHER | ||
'WATCHER' | ||
when OTHER | ||
'OTHER' | ||
else | ||
'UNKNOWN' | ||
end | ||
end | ||
end | ||
|
||
class Event | ||
attr_reader :path_name, :effect_type, :path_type, :effect_time, :associated_path_name | ||
|
||
def initialize(path_name, effect_type, path_type, effect_time, associated_path_name) | ||
@path_name = path_name | ||
@effect_type = effect_type | ||
@path_type = path_type | ||
@effect_time = effect_time | ||
@associated_path_name = associated_path_name | ||
end | ||
|
||
def to_s | ||
pnm = "path_name: #{@path_name}" | ||
ety = "effect_type: #{@effect_type}" | ||
pty = "path_type: #{@path_type}" | ||
etm = "effect_time: #{@effect_time}" | ||
apn = "associated_path_name: #{@associated_path_name}" | ||
"Event(#{pnm}, #{ety}, #{pty}, #{etm}, #{apn})" | ||
end | ||
end | ||
|
||
class Watch | ||
def initialize(path, callback) | ||
@lib = Watcher.lazy_static_solib_handle | ||
@path = path.encode('UTF-8') | ||
@callback = callback | ||
@c_callback = Fiddle::Closure::BlockCaller.new(0, [CEvent, :void]) do |c_event, _| | ||
py_event = Watcher.c_event_to_event(c_event) | ||
@callback.call(py_event) | ||
end | ||
|
||
@watcher = @lib.wtr_watcher_open(@path, @c_callback, nil) | ||
raise 'Failed to open a watcher' unless @watcher | ||
end | ||
|
||
def close | ||
return unless @watcher | ||
|
||
@lib.wtr_watcher_close(@watcher) | ||
@watcher = nil | ||
end | ||
|
||
def finalize | ||
close | ||
end | ||
|
||
def self.finalize(id) | ||
ObjectSpace._id2ref(id).close | ||
end | ||
end | ||
end | ||
|
||
if __FILE__ == $PROGRAM_NAME | ||
events_at = ARGV[0] || '.' | ||
watcher = Watcher::Watch.new(events_at, method(:puts)) | ||
ObjectSpace.define_finalizer(watcher, Watcher::Watch.method(:finalize).to_proc) | ||
gets | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#! /usr/bin/env bash | ||
set -e | ||
cd "$(dirname "$0")/.." | ||
bundle install | ||
bundle exec rake | ||
bundle exec gem build watcher.gemspec | ||
bundle exec gem install watcher-*.gem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
|
||
Gem::Specification.new do |spec| | ||
spec.name = 'watcher' | ||
spec.version = File.read('../.version').strip | ||
spec.authors = ['Will'] | ||
spec.email = ['[email protected]'] | ||
spec.summary = 'Filesystem watcher. Simple, efficient and friendly.' | ||
spec.description = 'Watch events on (almost) any filesystem.' | ||
spec.homepage = 'https://github.com/e-dant/watcher' | ||
spec.license = 'MIT' | ||
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0') | ||
spec.metadata['homepage_uri'] = spec.homepage | ||
spec.metadata['changelog_uri'] = "#{spec.homepage}/changelog.md" | ||
spec.metadata['allowed_push_host'] = 'TODO' | ||
spec.files = `ls -1 lib`.split("\n").map { |f| "lib/#{f}" } | ||
spec.bindir = 'bin' | ||
spec.executables = 'watcher' | ||
spec.require_paths = ['lib'] | ||
spec.add_development_dependency 'bundler', '~> 2.0' | ||
spec.add_development_dependency 'rake', '~> 13.0' | ||
spec.add_development_dependency 'rspec', '~> 3.0' | ||
end |