fswatch-rb 0.1.1
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +21 -0
- data/README.md +76 -0
- data/Rakefile +13 -0
- data/ext/fswatch/Makefile +264 -0
- data/ext/fswatch/callback.c +56 -0
- data/ext/fswatch/configure.c +192 -0
- data/ext/fswatch/enums.c +95 -0
- data/ext/fswatch/extconf.rb +33 -0
- data/ext/fswatch/fswatch.c +10 -0
- data/ext/fswatch/fswatch.h +34 -0
- data/ext/fswatch/monitor.c +113 -0
- data/fswatch-rb.gemspec +28 -0
- data/lib/fswatch.rb +10 -0
- data/lib/fswatch/monitor.rb +21 -0
- data/lib/fswatch/watcher.rb +59 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '068664ab465511d6e4efc65056b4647b5782b2ac2918031a7229d2aa58c757da'
|
4
|
+
data.tar.gz: 23dd203bae6b26f25ddd9d3f2d27016ef8389dbb10d3ecf0ff4a22673704e076
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6554d417c403504051503bb0e646f8d5d41b5cb7aa52bb732a29dfdcbf8d72fbe807ea828c1aa29ecba5b781d4afe8f1579ae21458b31c9d7b41a4571238c1ac
|
7
|
+
data.tar.gz: 47a793d84e9dad183bf68710203655fcffccfb7b4149df776ade6bf09ef3dc20183d8d1a18bb650acc9205df1ed9f17dba3eaa22a65d5139040136af67fe7ec5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fswatch-rb (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.3)
|
10
|
+
rake (10.5.0)
|
11
|
+
rake-compiler (1.0.4)
|
12
|
+
rake
|
13
|
+
rspec (3.7.0)
|
14
|
+
rspec-core (~> 3.7.0)
|
15
|
+
rspec-expectations (~> 3.7.0)
|
16
|
+
rspec-mocks (~> 3.7.0)
|
17
|
+
rspec-core (3.7.0)
|
18
|
+
rspec-support (~> 3.7.0)
|
19
|
+
rspec-expectations (3.7.0)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.7.0)
|
22
|
+
rspec-mocks (3.7.0)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.7.0)
|
25
|
+
rspec-support (3.7.0)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
bundler (~> 1.16)
|
32
|
+
fswatch-rb!
|
33
|
+
rake (~> 10.0)
|
34
|
+
rake-compiler (~> 1.0)
|
35
|
+
rspec (~> 3.7)
|
36
|
+
|
37
|
+
BUNDLED WITH
|
38
|
+
1.16.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Igor Yamolov
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# fswatch-rb
|
2
|
+
|
3
|
+
fswatch-rb is binding for multi-platform fswatch library.
|
4
|
+
|
5
|
+
https://github.com/emcrisostomo/fswatch/
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
First you need to install fswatch >= 1.11.3
|
10
|
+
|
11
|
+
```bash
|
12
|
+
$ brew install fswatch
|
13
|
+
```
|
14
|
+
|
15
|
+
Then add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'fswatch-rb'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install fswatch-rb
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'fswatch'
|
33
|
+
|
34
|
+
events_array = []
|
35
|
+
|
36
|
+
watcher = Fswatch::Watcher.new(
|
37
|
+
path: '~/my/awesome/project/directory/',
|
38
|
+
event_flags: [:created, :updated, :is_file, :renamed, :removed],
|
39
|
+
filters: { # global filters, high-perfomance
|
40
|
+
/\.tmp$/ix => :exclude, # if file ends with .tmp it will be ignored
|
41
|
+
/\(.rb|.slim)$/x => :include # include only .rb and .slim files
|
42
|
+
# Please note these reg exps are executed OUTSIDE ruby and not by ruby regexp implementation, so be careful
|
43
|
+
},
|
44
|
+
latency: 0.1, # 100ms
|
45
|
+
recursive: true,
|
46
|
+
follow_symlinks: true,
|
47
|
+
)
|
48
|
+
|
49
|
+
watcher.watch do |file, timestamp, flags|
|
50
|
+
puts "File #{file} has been changed at #{timestamp} with #{flags}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# local filename filter, low-perfomance
|
54
|
+
watcher.watch(match: /\.css$/) do |file, timestamp, flags|
|
55
|
+
puts "CSS File #{file} has been changed!"
|
56
|
+
end
|
57
|
+
|
58
|
+
watcher.start! # this will spawn new thread in background
|
59
|
+
|
60
|
+
watcher.running? # => true
|
61
|
+
|
62
|
+
# ... do evil stuff ...
|
63
|
+
|
64
|
+
watcher.stop!
|
65
|
+
|
66
|
+
watcher.running? # => false
|
67
|
+
|
68
|
+
```
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/t3hk0d3/fswatch-rb.
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rake/extensiontask'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
Rake::ExtensionTask.new do |ext|
|
8
|
+
ext.name = 'fswatch'
|
9
|
+
ext.ext_dir = 'ext/fswatch'
|
10
|
+
ext.lib_dir = 'lib/fswatch'
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => [:compile, :spec]
|
@@ -0,0 +1,264 @@
|
|
1
|
+
|
2
|
+
SHELL = /bin/sh
|
3
|
+
|
4
|
+
# V=0 quiet, V=1 verbose. other values don't work.
|
5
|
+
V = 0
|
6
|
+
Q1 = $(V:1=)
|
7
|
+
Q = $(Q1:0=@)
|
8
|
+
ECHO1 = $(V:1=@ :)
|
9
|
+
ECHO = $(ECHO1:0=@ echo)
|
10
|
+
NULLCMD = :
|
11
|
+
|
12
|
+
#### Start of system configuration section. ####
|
13
|
+
|
14
|
+
srcdir = .
|
15
|
+
topdir = /Users/tehkode/.rubies/ruby-2.4.2/include/ruby-2.4.0
|
16
|
+
hdrdir = $(topdir)
|
17
|
+
arch_hdrdir = /Users/tehkode/.rubies/ruby-2.4.2/include/ruby-2.4.0/x86_64-darwin16
|
18
|
+
PATH_SEPARATOR = :
|
19
|
+
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
20
|
+
prefix = $(DESTDIR)/Users/tehkode/.rubies/ruby-2.4.2
|
21
|
+
rubysitearchprefix = $(rubylibprefix)/$(sitearch)
|
22
|
+
rubyarchprefix = $(rubylibprefix)/$(arch)
|
23
|
+
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
24
|
+
exec_prefix = $(prefix)
|
25
|
+
vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
|
26
|
+
sitearchhdrdir = $(sitehdrdir)/$(sitearch)
|
27
|
+
rubyarchhdrdir = $(rubyhdrdir)/$(arch)
|
28
|
+
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
29
|
+
sitehdrdir = $(rubyhdrdir)/site_ruby
|
30
|
+
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
|
31
|
+
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
32
|
+
vendorlibdir = $(vendordir)/$(ruby_version)
|
33
|
+
vendordir = $(rubylibprefix)/vendor_ruby
|
34
|
+
sitearchdir = $(sitelibdir)/$(sitearch)
|
35
|
+
sitelibdir = $(sitedir)/$(ruby_version)
|
36
|
+
sitedir = $(rubylibprefix)/site_ruby
|
37
|
+
rubyarchdir = $(rubylibdir)/$(arch)
|
38
|
+
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
39
|
+
sitearchincludedir = $(includedir)/$(sitearch)
|
40
|
+
archincludedir = $(includedir)/$(arch)
|
41
|
+
sitearchlibdir = $(libdir)/$(sitearch)
|
42
|
+
archlibdir = $(libdir)/$(arch)
|
43
|
+
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
44
|
+
mandir = $(datarootdir)/man
|
45
|
+
localedir = $(datarootdir)/locale
|
46
|
+
libdir = $(exec_prefix)/lib
|
47
|
+
psdir = $(docdir)
|
48
|
+
pdfdir = $(docdir)
|
49
|
+
dvidir = $(docdir)
|
50
|
+
htmldir = $(docdir)
|
51
|
+
infodir = $(datarootdir)/info
|
52
|
+
docdir = $(datarootdir)/doc/$(PACKAGE)
|
53
|
+
oldincludedir = $(SDKROOT)/usr/include
|
54
|
+
includedir = $(prefix)/include
|
55
|
+
localstatedir = $(prefix)/var
|
56
|
+
sharedstatedir = $(prefix)/com
|
57
|
+
sysconfdir = $(prefix)/etc
|
58
|
+
datadir = $(datarootdir)
|
59
|
+
datarootdir = $(prefix)/share
|
60
|
+
libexecdir = $(exec_prefix)/libexec
|
61
|
+
sbindir = $(exec_prefix)/sbin
|
62
|
+
bindir = $(exec_prefix)/bin
|
63
|
+
archdir = $(rubyarchdir)
|
64
|
+
|
65
|
+
|
66
|
+
CC = clang
|
67
|
+
CXX = clang++
|
68
|
+
LIBRUBY = $(LIBRUBY_A)
|
69
|
+
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
70
|
+
LIBRUBYARG_SHARED =
|
71
|
+
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework CoreFoundation
|
72
|
+
empty =
|
73
|
+
OUTFLAG = -o $(empty)
|
74
|
+
COUTFLAG = -o $(empty)
|
75
|
+
CSRCFLAG = $(empty)
|
76
|
+
|
77
|
+
RUBY_EXTCONF_H =
|
78
|
+
cflags = $(optflags) $(debugflags) $(warnflags)
|
79
|
+
cxxflags = $(optflags) $(debugflags) $(warnflags)
|
80
|
+
optflags = -O3 -fno-fast-math
|
81
|
+
debugflags = -ggdb3
|
82
|
+
warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wno-tautological-compare -Wno-parentheses-equality -Wno-constant-logical-operand -Wno-self-assign -Wunused-variable -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration -Wdivision-by-zero -Wdeprecated-declarations -Wextra-tokens
|
83
|
+
CCDLFLAGS = -fno-common
|
84
|
+
CFLAGS = $(CCDLFLAGS) -O3 -Wno-error=shorten-64-to-32 -pipe $(ARCH_FLAG)
|
85
|
+
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
86
|
+
DEFS =
|
87
|
+
CPPFLAGS = -I/usr/local/include -I/usr/include -I/Users/tehkode/.rubies/ruby-2.4.2/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
|
88
|
+
CXXFLAGS = $(CCDLFLAGS) $(cxxflags) -std=c++11 $(ARCH_FLAG)
|
89
|
+
ldflags = -L. -L/Users/tehkode/.rubies/ruby-2.4.2/lib -fstack-protector -L/usr/local/lib
|
90
|
+
dldflags = -L/Users/tehkode/.rubies/ruby-2.4.2/lib -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress
|
91
|
+
ARCH_FLAG =
|
92
|
+
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
93
|
+
LDSHARED = $(CC) -dynamic -bundle
|
94
|
+
LDSHAREDXX = $(CXX) -dynamic -bundle
|
95
|
+
AR = ar
|
96
|
+
EXEEXT =
|
97
|
+
|
98
|
+
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
|
99
|
+
RUBY_SO_NAME = ruby
|
100
|
+
RUBYW_INSTALL_NAME =
|
101
|
+
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
|
102
|
+
RUBYW_BASE_NAME = rubyw
|
103
|
+
RUBY_BASE_NAME = ruby
|
104
|
+
|
105
|
+
arch = x86_64-darwin16
|
106
|
+
sitearch = $(arch)
|
107
|
+
ruby_version = 2.4.0
|
108
|
+
ruby = $(bindir)/$(RUBY_BASE_NAME)
|
109
|
+
RUBY = $(ruby)
|
110
|
+
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
|
111
|
+
|
112
|
+
RM = rm -f
|
113
|
+
RM_RF = $(RUBY) -run -e rm -- -rf
|
114
|
+
RMDIRS = rmdir -p
|
115
|
+
MAKEDIRS = mkdir -p
|
116
|
+
INSTALL = /usr/bin/install -c
|
117
|
+
INSTALL_PROG = $(INSTALL) -m 0755
|
118
|
+
INSTALL_DATA = $(INSTALL) -m 644
|
119
|
+
COPY = cp
|
120
|
+
TOUCH = exit >
|
121
|
+
|
122
|
+
#### End of system configuration section. ####
|
123
|
+
|
124
|
+
preload =
|
125
|
+
libpath = . $(libdir) /usr/local/lib /usr/lib
|
126
|
+
LIBPATH = -L. -L$(libdir) -L/usr/local/lib -L/usr/lib
|
127
|
+
DEFFILE =
|
128
|
+
|
129
|
+
CLEANFILES = mkmf.log
|
130
|
+
DISTCLEANFILES =
|
131
|
+
DISTCLEANDIRS =
|
132
|
+
|
133
|
+
extout =
|
134
|
+
extout_prefix =
|
135
|
+
target_prefix = /fswatch
|
136
|
+
LOCAL_LIBS =
|
137
|
+
LIBS = -lfswatch -lpthread -ldl -lobjc
|
138
|
+
ORIG_SRCS = callback.c enums.c fswatch.c listener.c
|
139
|
+
SRCS = $(ORIG_SRCS) enums.c callback.c fswatch.c listener.c
|
140
|
+
OBJS = enums.o callback.o fswatch.o listener.o
|
141
|
+
HDRS = $(srcdir)/fswatch.h
|
142
|
+
LOCAL_HDRS =
|
143
|
+
TARGET = fswatch
|
144
|
+
TARGET_NAME = fswatch
|
145
|
+
TARGET_ENTRY = Init_$(TARGET_NAME)
|
146
|
+
DLLIB = $(TARGET).bundle
|
147
|
+
EXTSTATIC =
|
148
|
+
STATIC_LIB =
|
149
|
+
|
150
|
+
TIMESTAMP_DIR = .
|
151
|
+
BINDIR = $(bindir)
|
152
|
+
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
153
|
+
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
154
|
+
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
155
|
+
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
|
156
|
+
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
|
157
|
+
TARGET_SO_DIR =
|
158
|
+
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
|
159
|
+
CLEANLIBS = $(TARGET_SO)
|
160
|
+
CLEANOBJS = *.o *.bak
|
161
|
+
|
162
|
+
all: $(DLLIB)
|
163
|
+
static: $(STATIC_LIB)
|
164
|
+
.PHONY: all install static install-so install-rb
|
165
|
+
.PHONY: clean clean-so clean-static clean-rb
|
166
|
+
|
167
|
+
clean-static::
|
168
|
+
clean-rb-default::
|
169
|
+
clean-rb::
|
170
|
+
clean-so::
|
171
|
+
clean: clean-so clean-static clean-rb-default clean-rb
|
172
|
+
-$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
|
173
|
+
|
174
|
+
distclean-rb-default::
|
175
|
+
distclean-rb::
|
176
|
+
distclean-so::
|
177
|
+
distclean-static::
|
178
|
+
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
|
179
|
+
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
180
|
+
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
181
|
+
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
182
|
+
|
183
|
+
realclean: distclean
|
184
|
+
install: install-so install-rb
|
185
|
+
|
186
|
+
install-so: $(DLLIB) $(TIMESTAMP_DIR)/.sitearchdir.-.fswatch.time
|
187
|
+
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
188
|
+
clean-static::
|
189
|
+
-$(Q)$(RM) $(STATIC_LIB)
|
190
|
+
install-rb: pre-install-rb do-install-rb install-rb-default
|
191
|
+
install-rb-default: pre-install-rb-default do-install-rb-default
|
192
|
+
pre-install-rb: Makefile
|
193
|
+
pre-install-rb-default: Makefile
|
194
|
+
do-install-rb:
|
195
|
+
do-install-rb-default:
|
196
|
+
pre-install-rb-default:
|
197
|
+
@$(NULLCMD)
|
198
|
+
$(TIMESTAMP_DIR)/.sitearchdir.-.fswatch.time:
|
199
|
+
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
|
200
|
+
$(Q) $(TOUCH) $@
|
201
|
+
|
202
|
+
site-install: site-install-so site-install-rb
|
203
|
+
site-install-so: install-so
|
204
|
+
site-install-rb: install-rb
|
205
|
+
|
206
|
+
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
|
207
|
+
|
208
|
+
.cc.o:
|
209
|
+
$(ECHO) compiling $(<)
|
210
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
211
|
+
|
212
|
+
.cc.S:
|
213
|
+
$(ECHO) translating $(<)
|
214
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
215
|
+
|
216
|
+
.mm.o:
|
217
|
+
$(ECHO) compiling $(<)
|
218
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
219
|
+
|
220
|
+
.mm.S:
|
221
|
+
$(ECHO) translating $(<)
|
222
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
223
|
+
|
224
|
+
.cxx.o:
|
225
|
+
$(ECHO) compiling $(<)
|
226
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
227
|
+
|
228
|
+
.cxx.S:
|
229
|
+
$(ECHO) translating $(<)
|
230
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
231
|
+
|
232
|
+
.cpp.o:
|
233
|
+
$(ECHO) compiling $(<)
|
234
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
235
|
+
|
236
|
+
.cpp.S:
|
237
|
+
$(ECHO) translating $(<)
|
238
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
239
|
+
|
240
|
+
.c.o:
|
241
|
+
$(ECHO) compiling $(<)
|
242
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
243
|
+
|
244
|
+
.c.S:
|
245
|
+
$(ECHO) translating $(<)
|
246
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
247
|
+
|
248
|
+
.m.o:
|
249
|
+
$(ECHO) compiling $(<)
|
250
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
251
|
+
|
252
|
+
.m.S:
|
253
|
+
$(ECHO) translating $(<)
|
254
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
255
|
+
|
256
|
+
$(TARGET_SO): $(OBJS) Makefile
|
257
|
+
$(ECHO) linking shared-object fswatch/$(DLLIB)
|
258
|
+
-$(Q)$(RM) $(@)
|
259
|
+
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
260
|
+
$(Q) $(POSTLINK)
|
261
|
+
|
262
|
+
|
263
|
+
|
264
|
+
$(OBJS): $(HDRS) $(ruby_headers)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#include <stdint.h>
|
2
|
+
|
3
|
+
#include "fswatch.h"
|
4
|
+
|
5
|
+
|
6
|
+
void* fswatch_start_monitor_no_gvl(void* data) {
|
7
|
+
FSW_HANDLE handle = (FSW_HANDLE)data;
|
8
|
+
FSW_STATUS status = fsw_start_monitor(handle);
|
9
|
+
|
10
|
+
return (void *)(intptr_t)status; // return integer hidden as pointer
|
11
|
+
}
|
12
|
+
|
13
|
+
void* fswatch_callback_handler_with_gvl(void* data) {
|
14
|
+
FSW_EVENT_CONTEXT *context = data;
|
15
|
+
|
16
|
+
VALUE rb_aEvents = rb_ary_new2(context->event_num);
|
17
|
+
|
18
|
+
for (int i = 0 ; i < context->event_num ; i++) {
|
19
|
+
const fsw_cevent event = context->events[i];
|
20
|
+
|
21
|
+
VALUE rb_aEvent = rb_ary_new2(2 + event.flags_num);
|
22
|
+
|
23
|
+
rb_ary_push(rb_aEvent, rb_str_new2(event.path));
|
24
|
+
rb_ary_push(rb_aEvent, INT2NUM(event.evt_time));
|
25
|
+
|
26
|
+
for (int c = 0 ; c < event.flags_num ; c++) {
|
27
|
+
rb_ary_push(rb_aEvent, fswatch_event_name(event.flags[c]));
|
28
|
+
}
|
29
|
+
|
30
|
+
rb_ary_push(rb_aEvents, rb_aEvent);
|
31
|
+
}
|
32
|
+
|
33
|
+
VALUE rb_proc_obj = rb_ivar_get(context->object, rb_intern("callback"));
|
34
|
+
|
35
|
+
rb_proc_call(rb_proc_obj, rb_ary_new_from_args(1, rb_aEvents));
|
36
|
+
|
37
|
+
return NULL;
|
38
|
+
}
|
39
|
+
|
40
|
+
void fswatch_interrupt_monitor_no_gvl(void* data) {
|
41
|
+
FSW_HANDLE handle = (FSW_HANDLE)data;
|
42
|
+
FSW_STATUS status = fsw_stop_monitor(handle);
|
43
|
+
|
44
|
+
if (status != FSW_OK) {
|
45
|
+
rb_raise(rb_eRuntimeError, "Failed to interrupt monitor - err: %d", status);
|
46
|
+
}
|
47
|
+
|
48
|
+
}
|
49
|
+
|
50
|
+
void fswatch_callback_handler_no_gvl(fsw_cevent const *const events,
|
51
|
+
const unsigned int event_num,
|
52
|
+
void *data) {
|
53
|
+
FSW_EVENT_CONTEXT context = { .events = events, .event_num = event_num, .object = (VALUE)data };
|
54
|
+
|
55
|
+
rb_thread_call_with_gvl(fswatch_callback_handler_with_gvl, &context);
|
56
|
+
}
|
@@ -0,0 +1,192 @@
|
|
1
|
+
#include "fswatch.h"
|
2
|
+
|
3
|
+
#define REGEXP_IGNORECASE (1 << 0) // 1
|
4
|
+
#define REGEXP_EXTENDED (1 << 1) // 2
|
5
|
+
|
6
|
+
static void fswatch_add_path(FSW_HANDLE handle, VALUE rb_settings) {
|
7
|
+
VALUE rb_path = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("path")));
|
8
|
+
|
9
|
+
VALUE path_arr;
|
10
|
+
|
11
|
+
switch (TYPE(rb_path)) {
|
12
|
+
case T_STRING:
|
13
|
+
path_arr = rb_ary_new_from_args(1, rb_path);
|
14
|
+
break;
|
15
|
+
case T_ARRAY:
|
16
|
+
path_arr = rb_path;
|
17
|
+
break;
|
18
|
+
default:
|
19
|
+
rb_raise(rb_eTypeError, "path is not valid");
|
20
|
+
break;
|
21
|
+
}
|
22
|
+
|
23
|
+
long length = RARRAY_LEN(path_arr);
|
24
|
+
|
25
|
+
for ( long i = 0 ; i < length ; i++ ){
|
26
|
+
VALUE rb_arr_item = rb_ary_entry(path_arr, i);
|
27
|
+
|
28
|
+
char* path_cstr = StringValueCStr(rb_arr_item);
|
29
|
+
|
30
|
+
|
31
|
+
if(fsw_add_path(handle, path_cstr) != FSW_OK) {
|
32
|
+
rb_raise(rb_eRuntimeError, "Unable to monitor specified path - '%s'", path_cstr);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
static void fswatch_add_properties(FSW_HANDLE handle, VALUE rb_settings) {
|
38
|
+
VALUE rb_properties = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("properties")));
|
39
|
+
|
40
|
+
if (rb_properties == Qnil) {
|
41
|
+
return;
|
42
|
+
}
|
43
|
+
|
44
|
+
Check_Type(rb_properties, T_HASH);
|
45
|
+
|
46
|
+
VALUE rb_property_keys = rb_funcall(rb_properties, rb_intern("keys"), 0);
|
47
|
+
|
48
|
+
long arr_len = RARRAY_LEN(rb_property_keys);
|
49
|
+
for ( long i = 0 ; i < arr_len ; i++ ){
|
50
|
+
VALUE rb_key = rb_ary_entry(rb_property_keys, i);
|
51
|
+
VALUE rb_value = rb_hash_fetch(rb_properties, rb_key);
|
52
|
+
|
53
|
+
if (TYPE(rb_key) == T_SYMBOL) {
|
54
|
+
rb_key = rb_sym2str(rb_key);
|
55
|
+
}
|
56
|
+
|
57
|
+
Check_Type(rb_key, T_STRING);
|
58
|
+
Check_Type(rb_value, T_STRING);
|
59
|
+
|
60
|
+
char* key_cstr = StringValueCStr(rb_key);
|
61
|
+
char* value_cstr = StringValueCStr(rb_value);
|
62
|
+
|
63
|
+
|
64
|
+
if(fsw_add_property(handle, key_cstr, value_cstr) != FSW_OK) {
|
65
|
+
rb_raise(rb_eRuntimeError, "Unable to add specified property - '%s' = '%s'", key_cstr, value_cstr);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
static void fswatch_add_event_type_filters(FSW_HANDLE handle, VALUE rb_settings) {
|
71
|
+
VALUE rb_event_types = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("event_flags")));
|
72
|
+
|
73
|
+
if (rb_event_types == Qnil) {
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
|
77
|
+
Check_Type(rb_event_types, T_ARRAY);
|
78
|
+
|
79
|
+
long arr_len = RARRAY_LEN(rb_event_types);
|
80
|
+
for ( long i = 0 ; i < arr_len ; i++ ){
|
81
|
+
VALUE rb_event_type = rb_ary_entry(rb_event_types, i);
|
82
|
+
|
83
|
+
fsw_event_type_filter filter = { .flag = fswatch_event_type(rb_event_type) };
|
84
|
+
|
85
|
+
VALUE rb_str = rb_sym2str(rb_event_type);
|
86
|
+
|
87
|
+
|
88
|
+
if (fsw_add_event_type_filter(handle, filter) != FSW_OK) {
|
89
|
+
rb_raise(rb_eRuntimeError, "Unable to event type filter - '%s' (%d)", StringValueCStr(rb_str), filter.flag);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
static void fswatch_add_monitor_filters(FSW_HANDLE handle, VALUE rb_settings) {
|
95
|
+
VALUE rb_monitor_filters = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("filters")));
|
96
|
+
|
97
|
+
if (rb_monitor_filters == Qnil) {
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
|
101
|
+
Check_Type(rb_monitor_filters, T_HASH);
|
102
|
+
|
103
|
+
VALUE rb_monitor_keys = rb_funcall(rb_monitor_filters, rb_intern("keys"), 0);
|
104
|
+
|
105
|
+
long arr_len = RARRAY_LEN(rb_monitor_keys);
|
106
|
+
for ( long i = 0 ; i < arr_len ; i++ ){
|
107
|
+
VALUE rb_key = rb_ary_entry(rb_monitor_keys, i);
|
108
|
+
VALUE rb_value = rb_hash_fetch(rb_monitor_filters, rb_key);
|
109
|
+
|
110
|
+
Check_Type(rb_key, T_REGEXP);
|
111
|
+
Check_Type(rb_value, T_SYMBOL);
|
112
|
+
|
113
|
+
VALUE rb_regexp = RREGEXP_SRC(rb_key);
|
114
|
+
int options = rb_reg_options(rb_key);
|
115
|
+
|
116
|
+
enum fsw_filter_type filter_type;
|
117
|
+
|
118
|
+
if (SYM2ID(rb_value) == rb_intern("exclude")) {
|
119
|
+
filter_type = filter_exclude;
|
120
|
+
} else if (SYM2ID(rb_value) == rb_intern("include")) {
|
121
|
+
filter_type = filter_include;
|
122
|
+
} else {
|
123
|
+
VALUE rb_str = rb_sym2str(rb_value);
|
124
|
+
rb_raise(rb_eRuntimeError, "Unknown filter type - '%s'", StringValueCStr(rb_str));
|
125
|
+
}
|
126
|
+
|
127
|
+
const fsw_cmonitor_filter filter = {
|
128
|
+
.text = StringValueCStr(rb_regexp),
|
129
|
+
.type = filter_type,
|
130
|
+
.case_sensitive = (options & REGEXP_IGNORECASE) == 0,
|
131
|
+
.extended = (options & REGEXP_EXTENDED) != 0
|
132
|
+
};
|
133
|
+
|
134
|
+
|
135
|
+
if(fsw_add_filter(handle, filter) != FSW_OK) {
|
136
|
+
rb_raise(rb_eRuntimeError, "Unable to path filter - '%s' => '%s'", filter.text, StringValueCStr(rb_value));
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
void fswatch_configure(FSW_HANDLE handle, VALUE rb_settings) {
|
142
|
+
fswatch_add_path(handle, rb_settings);
|
143
|
+
fswatch_add_properties(handle, rb_settings);
|
144
|
+
|
145
|
+
fswatch_add_event_type_filters(handle, rb_settings);
|
146
|
+
fswatch_add_monitor_filters(handle, rb_settings);
|
147
|
+
|
148
|
+
VALUE rb_latency = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("latency")));
|
149
|
+
if (rb_latency != Qnil) {
|
150
|
+
Check_Type(rb_latency, T_FLOAT);
|
151
|
+
|
152
|
+
|
153
|
+
if (fsw_set_latency(handle, NUM2DBL(rb_latency)) != FSW_OK) {
|
154
|
+
rb_raise(rb_eRuntimeError, "Failed to set latency = %0.3f", NUM2DBL(rb_latency));
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
VALUE rb_allow_overflow = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("allow_overflow")));
|
159
|
+
if (rb_allow_overflow != Qnil) {
|
160
|
+
|
161
|
+
if (fsw_set_allow_overflow(handle, rb_allow_overflow != Qfalse) != FSW_OK) {
|
162
|
+
rb_raise(rb_eRuntimeError, "Failed to set overflow mode = %d", rb_allow_overflow != Qfalse);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
VALUE rb_recursive = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("recursive")));
|
167
|
+
if (rb_recursive != Qnil) {
|
168
|
+
if (fsw_set_recursive(handle, rb_recursive != Qfalse) != FSW_OK) {
|
169
|
+
rb_raise(rb_eRuntimeError, "Failed to set recursive mode = %d", rb_recursive != Qfalse);
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
VALUE rb_directory_only = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("directory_only")));
|
174
|
+
if (rb_directory_only != Qnil) {
|
175
|
+
if (fsw_set_directory_only(handle, rb_directory_only != Qfalse) != FSW_OK) {
|
176
|
+
rb_raise(rb_eRuntimeError, "Failed to set directory-only mode = %d", rb_directory_only != Qfalse);
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
VALUE rb_follow_symlinks = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("follow_symlinks")));
|
181
|
+
if (rb_follow_symlinks != Qnil) {
|
182
|
+
if (fsw_set_directory_only(handle, rb_follow_symlinks != Qfalse) != FSW_OK) {
|
183
|
+
rb_raise(rb_eRuntimeError, "Failed to set follow-symlinks mode = %d", rb_follow_symlinks != Qfalse);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
VALUE rb_verbose = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("debug")));
|
188
|
+
if (rb_verbose != Qnil) {
|
189
|
+
fsw_set_verbose(rb_verbose != Qfalse);
|
190
|
+
}
|
191
|
+
|
192
|
+
}
|
data/ext/fswatch/enums.c
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#include "fswatch.h"
|
2
|
+
|
3
|
+
VALUE fswatch_event_name(enum fsw_event_flag flag) {
|
4
|
+
ID rb_idFlagName;
|
5
|
+
|
6
|
+
switch (flag) {
|
7
|
+
case NoOp: rb_idFlagName = rb_intern("no_op"); break;
|
8
|
+
case PlatformSpecific: rb_idFlagName = rb_intern("platform_specific"); break;
|
9
|
+
case Created: rb_idFlagName = rb_intern("created"); break;
|
10
|
+
case Updated: rb_idFlagName = rb_intern("updated"); break;
|
11
|
+
case Removed: rb_idFlagName = rb_intern("removed"); break;
|
12
|
+
case Renamed: rb_idFlagName = rb_intern("renamed"); break;
|
13
|
+
case OwnerModified: rb_idFlagName = rb_intern("owner_modified"); break;
|
14
|
+
case AttributeModified: rb_idFlagName = rb_intern("attribute_modified"); break;
|
15
|
+
case MovedFrom: rb_idFlagName = rb_intern("moved_from"); break;
|
16
|
+
case MovedTo: rb_idFlagName = rb_intern("moved_to"); break;
|
17
|
+
case IsFile: rb_idFlagName = rb_intern("is_file"); break;
|
18
|
+
case IsDir: rb_idFlagName = rb_intern("is_dir"); break;
|
19
|
+
case IsSymLink: rb_idFlagName = rb_intern("is_symlink"); break;
|
20
|
+
case Link: rb_idFlagName = rb_intern("link"); break;
|
21
|
+
case Overflow: rb_idFlagName = rb_intern("overflow"); break;
|
22
|
+
default:
|
23
|
+
rb_idFlagName = rb_intern("unknown");
|
24
|
+
}
|
25
|
+
|
26
|
+
return ID2SYM(rb_idFlagName);
|
27
|
+
}
|
28
|
+
|
29
|
+
enum fsw_event_flag fswatch_event_type(VALUE rb_flag) {
|
30
|
+
ID rb_idFlagName = rb_to_id(rb_flag);
|
31
|
+
|
32
|
+
if ( rb_idFlagName == rb_intern("no_op") ) {
|
33
|
+
return NoOp;
|
34
|
+
} else if ( rb_idFlagName == rb_intern("platform_specific") ) {
|
35
|
+
return PlatformSpecific;
|
36
|
+
} else if ( rb_idFlagName == rb_intern("created") ) {
|
37
|
+
return Created;
|
38
|
+
} else if ( rb_idFlagName == rb_intern("updated") ) {
|
39
|
+
return Updated;
|
40
|
+
} else if ( rb_idFlagName == rb_intern("removed") ) {
|
41
|
+
return Removed;
|
42
|
+
} else if ( rb_idFlagName == rb_intern("renamed") ) {
|
43
|
+
return Renamed;
|
44
|
+
} else if ( rb_idFlagName == rb_intern("owner_modified") ) {
|
45
|
+
return OwnerModified;
|
46
|
+
} else if ( rb_idFlagName == rb_intern("attribute_modified") ) {
|
47
|
+
return AttributeModified;
|
48
|
+
} else if ( rb_idFlagName == rb_intern("moved_from") ) {
|
49
|
+
return MovedFrom;
|
50
|
+
} else if ( rb_idFlagName == rb_intern("moved_to") ) {
|
51
|
+
return MovedTo;
|
52
|
+
} else if ( rb_idFlagName == rb_intern("is_file") ) {
|
53
|
+
return IsFile;
|
54
|
+
} else if ( rb_idFlagName == rb_intern("is_dir") ) {
|
55
|
+
return IsDir;
|
56
|
+
} else if ( rb_idFlagName == rb_intern("is_symlink") ) {
|
57
|
+
return IsSymLink;
|
58
|
+
} else if ( rb_idFlagName == rb_intern("link") ) {
|
59
|
+
return Link;
|
60
|
+
} else if ( rb_idFlagName == rb_intern("overflow") ) {
|
61
|
+
return Overflow;
|
62
|
+
}
|
63
|
+
|
64
|
+
VALUE rb_str = rb_sym2str(rb_flag);
|
65
|
+
|
66
|
+
rb_raise(rb_eRuntimeError, "Unknown event type '%s'", StringValueCStr(rb_str));
|
67
|
+
|
68
|
+
return NoOp;
|
69
|
+
}
|
70
|
+
|
71
|
+
enum fsw_monitor_type fswatch_monitor_type_by(VALUE name) {
|
72
|
+
if (name == Qnil) {
|
73
|
+
return system_default_monitor_type;
|
74
|
+
}
|
75
|
+
|
76
|
+
VALUE rb_str = StringValue(name);
|
77
|
+
|
78
|
+
if (rb_str_equal(rb_str, rb_str_new_literal("fsevents"))) {
|
79
|
+
return fsevents_monitor_type;
|
80
|
+
} else if (rb_str_equal(rb_str, rb_str_new_literal("kqueue"))) {
|
81
|
+
return kqueue_monitor_type;
|
82
|
+
} else if (rb_str_equal(rb_str, rb_str_new_literal("inotify"))) {
|
83
|
+
return inotify_monitor_type;
|
84
|
+
} else if (rb_str_equal(rb_str, rb_str_new_literal("windows"))) {
|
85
|
+
return windows_monitor_type;
|
86
|
+
} else if (rb_str_equal(rb_str, rb_str_new_literal("poll"))) {
|
87
|
+
return poll_monitor_type;
|
88
|
+
} else if (rb_str_equal(rb_str, rb_str_new_literal("fen"))) {
|
89
|
+
return fen_monitor_type;
|
90
|
+
}
|
91
|
+
|
92
|
+
rb_raise(rb_eRuntimeError, "Unknown monitor type '%s'", StringValueCStr(rb_str));
|
93
|
+
|
94
|
+
return Qnil;
|
95
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
$CXXFLAGS += " -std=c++11 "
|
4
|
+
|
5
|
+
HEADER_DIRS = [
|
6
|
+
'/opt/local/include',
|
7
|
+
'/usr/local/include',
|
8
|
+
'/usr/include',
|
9
|
+
].select { |d| Dir.exists?(d) }
|
10
|
+
|
11
|
+
LIB_DIRS = [
|
12
|
+
'/opt/local/lib',
|
13
|
+
'/usr/local/lib',
|
14
|
+
'/usr/lib',
|
15
|
+
].select { |d| Dir.exists?(d) }
|
16
|
+
|
17
|
+
$srcs = Dir.glob(File.join(File.expand_path('..', __FILE__), '*.c'))
|
18
|
+
|
19
|
+
dir_config('fswatch', HEADER_DIRS, LIB_DIRS)
|
20
|
+
|
21
|
+
unless find_header('libfswatch/c/libfswatch.h')
|
22
|
+
abort "libfswatch is missing. please install libfswatch"
|
23
|
+
end
|
24
|
+
|
25
|
+
unless find_library('fswatch', 'fsw_init_library')
|
26
|
+
abort "libfswatch is missing. please install libfswatch"
|
27
|
+
end
|
28
|
+
|
29
|
+
unless have_func('fsw_is_running')
|
30
|
+
abort 'libfswatch version >= 1.11.3 is required. Please upgrade!'
|
31
|
+
end
|
32
|
+
|
33
|
+
create_makefile('fswatch/fswatch')
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#ifndef FSWATCH_H
|
2
|
+
#define FSWATCH_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <ruby/st.h>
|
6
|
+
#include <ruby/encoding.h>
|
7
|
+
#include <ruby/thread.h>
|
8
|
+
|
9
|
+
#include <libfswatch/c/libfswatch.h>
|
10
|
+
|
11
|
+
typedef struct FSW_EVENT_CONTEXT {
|
12
|
+
const fsw_cevent *const events;
|
13
|
+
unsigned int event_num;
|
14
|
+
VALUE object;
|
15
|
+
} FSW_EVENT_CONTEXT;
|
16
|
+
|
17
|
+
/* Configuration */
|
18
|
+
extern void fswatch_configure(FSW_HANDLE handle, VALUE rb_settings);
|
19
|
+
|
20
|
+
/* Callbacks */
|
21
|
+
extern void* fswatch_start_monitor_no_gvl(void* data);
|
22
|
+
extern void* fswatch_callback_handler_with_gvl(void* data);
|
23
|
+
extern void fswatch_callback_handler_no_gvl(fsw_cevent const *const events, const unsigned int event_num, void *data);
|
24
|
+
extern void fswatch_interrupt_monitor_no_gvl(void* data);
|
25
|
+
|
26
|
+
/* Enum functions */
|
27
|
+
extern VALUE fswatch_event_name(enum fsw_event_flag flag);
|
28
|
+
extern enum fsw_monitor_type fswatch_monitor_type_by(VALUE name);
|
29
|
+
extern enum fsw_event_flag fswatch_event_type(VALUE rb_flag);
|
30
|
+
|
31
|
+
/* Monitor Class */
|
32
|
+
extern VALUE init_fswatch_monitor();
|
33
|
+
|
34
|
+
#endif /* FSWATCH_H */
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#include "fswatch.h"
|
2
|
+
|
3
|
+
static VALUE fswatch_monitor_init(VALUE self, VALUE rb_settings) {
|
4
|
+
FSW_HANDLE *handle;
|
5
|
+
Data_Get_Struct(self, FSW_HANDLE, handle);
|
6
|
+
|
7
|
+
Check_Type(rb_settings, T_HASH);
|
8
|
+
|
9
|
+
VALUE rb_system_monitor = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("monitor_type")));
|
10
|
+
|
11
|
+
enum fsw_monitor_type monitor_type = fswatch_monitor_type_by(rb_system_monitor);
|
12
|
+
|
13
|
+
*handle = fsw_init_session(monitor_type);
|
14
|
+
|
15
|
+
VALUE rb_callback = rb_hash_lookup(rb_settings, ID2SYM(rb_intern("callback")));
|
16
|
+
|
17
|
+
if(rb_obj_is_proc(rb_callback) != Qtrue &&
|
18
|
+
rb_obj_is_method(rb_callback) != Qtrue) {
|
19
|
+
rb_raise(rb_eArgError, "Callback is not specified!");
|
20
|
+
return self;
|
21
|
+
}
|
22
|
+
|
23
|
+
// Saving callback to instance variable to prevent callback being garbage-collected
|
24
|
+
rb_ivar_set(self, rb_intern("callback"), rb_callback);
|
25
|
+
|
26
|
+
fsw_set_callback(*handle, fswatch_callback_handler_no_gvl, (void*)self);
|
27
|
+
|
28
|
+
fswatch_configure(*handle, rb_settings);
|
29
|
+
|
30
|
+
return self;
|
31
|
+
}
|
32
|
+
|
33
|
+
static VALUE fswatch_monitor_start(VALUE self) {
|
34
|
+
FSW_HANDLE *handle;
|
35
|
+
Data_Get_Struct(self, FSW_HANDLE, handle);
|
36
|
+
|
37
|
+
// fswatch spend most of the time doing nothing
|
38
|
+
// so its good idea to remove GIL during wait phase
|
39
|
+
// only to temporarily reaquire it for calling ruby callbacks
|
40
|
+
FSW_STATUS status = (FSW_STATUS)(intptr_t)
|
41
|
+
rb_thread_call_without_gvl(
|
42
|
+
fswatch_start_monitor_no_gvl, (void*)(*handle),
|
43
|
+
fswatch_interrupt_monitor_no_gvl, (void*)(*handle)
|
44
|
+
);
|
45
|
+
|
46
|
+
if (status != FSW_OK) {
|
47
|
+
rb_raise(rb_eRuntimeError, "Failed to start monitor - err: %d", status);
|
48
|
+
}
|
49
|
+
|
50
|
+
return Qtrue;
|
51
|
+
}
|
52
|
+
|
53
|
+
static VALUE fswatch_monitor_stop(VALUE self) {
|
54
|
+
FSW_HANDLE *handle;
|
55
|
+
Data_Get_Struct(self, FSW_HANDLE, handle);
|
56
|
+
|
57
|
+
FSW_STATUS status = fsw_stop_monitor(*handle);
|
58
|
+
if (status != FSW_OK) {
|
59
|
+
rb_raise(rb_eRuntimeError, "Failed to stop monitor - err: %d", status);
|
60
|
+
}
|
61
|
+
|
62
|
+
return Qtrue;
|
63
|
+
}
|
64
|
+
|
65
|
+
static VALUE fswatch_monitor_is_running(VALUE self) {
|
66
|
+
FSW_HANDLE *handle;
|
67
|
+
Data_Get_Struct(self, FSW_HANDLE, handle);
|
68
|
+
|
69
|
+
return fsw_is_running(*handle) ? Qtrue : Qfalse;
|
70
|
+
}
|
71
|
+
|
72
|
+
static void fswatch_monitor_free(void *ptr) {
|
73
|
+
FSW_HANDLE handle = *(FSW_HANDLE*)ptr;
|
74
|
+
|
75
|
+
if (handle) {
|
76
|
+
FSW_STATUS status = fsw_destroy_session(handle);
|
77
|
+
|
78
|
+
if (status == FSW_ERR_MONITOR_ALREADY_RUNNING) {
|
79
|
+
fsw_stop_monitor(handle);
|
80
|
+
fsw_destroy_session(handle); // try to destroy monitor in a blind fate
|
81
|
+
} else if (status != FSW_OK) {
|
82
|
+
rb_warn("Failed to destroy fswatch session - err:%d", status);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
ruby_xfree(ptr); // free allocated memory for pointer
|
87
|
+
}
|
88
|
+
|
89
|
+
|
90
|
+
static VALUE fswatch_monitor_alloc(VALUE klass) {
|
91
|
+
FSW_HANDLE *handle = ruby_xmalloc(sizeof(FSW_HANDLE));
|
92
|
+
|
93
|
+
*handle = NULL; // IMPORTANT! Initialize handler with NULL
|
94
|
+
|
95
|
+
return Data_Wrap_Struct(klass, NULL, fswatch_monitor_free, handle);
|
96
|
+
}
|
97
|
+
|
98
|
+
|
99
|
+
extern VALUE init_fswatch_monitor() {
|
100
|
+
VALUE rb_mFswatch = rb_define_module("Fswatch");
|
101
|
+
VALUE rb_cMonitor = rb_define_class_under(rb_mFswatch, "Monitor", rb_cObject);
|
102
|
+
|
103
|
+
rb_define_alloc_func(rb_cMonitor, fswatch_monitor_alloc);
|
104
|
+
|
105
|
+
rb_define_method(rb_cMonitor, "initialize", fswatch_monitor_init, 1);
|
106
|
+
|
107
|
+
rb_define_method(rb_cMonitor, "start", fswatch_monitor_start, 0);
|
108
|
+
rb_define_method(rb_cMonitor, "stop", fswatch_monitor_stop, 0);
|
109
|
+
|
110
|
+
rb_define_method(rb_cMonitor, "running?", fswatch_monitor_is_running, 0);
|
111
|
+
|
112
|
+
return rb_cMonitor;
|
113
|
+
}
|
data/fswatch-rb.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fswatch-rb"
|
7
|
+
spec.version = '0.1.1'
|
8
|
+
spec.authors = ['Igor Yamolov']
|
9
|
+
spec.email = %w(clouster@yandex.ru)
|
10
|
+
|
11
|
+
spec.summary = %q{Ruby binding for libfswatch library}
|
12
|
+
spec.description = %q{Ruby library to watch filesystem changes}
|
13
|
+
spec.homepage = "https://github.com/t3hk0d3/fswatch-rb"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
spec.extensions = %w[ext/fswatch/extconf.rb]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency 'rake-compiler', '~> 1.0'
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.7"
|
28
|
+
end
|
data/lib/fswatch.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fswatch
|
2
|
+
class Monitor
|
3
|
+
def initialize(options = {})
|
4
|
+
# stub method
|
5
|
+
# actual implementation is located in C extension - `ext/fswatch/monitor.c`
|
6
|
+
raise NotImplementedError, "Called stub method. Looks like C Extension failed to load"
|
7
|
+
end
|
8
|
+
|
9
|
+
def start
|
10
|
+
# stub method
|
11
|
+
# actual implementation is located in C extension - `ext/fswatch/monitor.c`
|
12
|
+
raise NotImplementedError, "Called stub method. Looks like C Extension failed to load"
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
# stub method
|
17
|
+
# actual implementation is located in C extension - `ext/fswatch/monitor.c`
|
18
|
+
raise NotImplementedError, "Called stub method. Looks like C Extension failed to load"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Fswatch
|
2
|
+
class Watcher
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
@event_handlers = []
|
6
|
+
|
7
|
+
options[:callback] = self.method(:_event_handler)
|
8
|
+
|
9
|
+
@monitor = Fswatch::Monitor.new(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def watch(options = {}, &block)
|
13
|
+
raise 'Invalid handler' unless block
|
14
|
+
|
15
|
+
options = nil if options.empty? # reduce memory usage
|
16
|
+
|
17
|
+
@event_handlers << [block, options]
|
18
|
+
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def start!
|
23
|
+
raise 'Already running!' if running?
|
24
|
+
|
25
|
+
@thread = Thread.new { @monitor.start }
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop!
|
29
|
+
raise 'Not running!' if running?
|
30
|
+
|
31
|
+
@monitor.stop
|
32
|
+
end
|
33
|
+
|
34
|
+
def running?
|
35
|
+
@monitor.running?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def _event_handler(events)
|
41
|
+
events.each do |event|
|
42
|
+
filename, timestamp, *event_flags = event
|
43
|
+
|
44
|
+
@event_handlers.each do |handler, options|
|
45
|
+
if options
|
46
|
+
# match filename
|
47
|
+
next if options[:match] && !options[:match].match?(filename)
|
48
|
+
|
49
|
+
# event
|
50
|
+
next if options[:on] && (options[:on] & event_flags).empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
handler.call(filename, timestamp, event_flags, options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fswatch-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Igor Yamolov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake-compiler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.7'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.7'
|
69
|
+
description: Ruby library to watch filesystem changes
|
70
|
+
email:
|
71
|
+
- clouster@yandex.ru
|
72
|
+
executables: []
|
73
|
+
extensions:
|
74
|
+
- ext/fswatch/extconf.rb
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- LICENSE.txt
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- ext/fswatch/Makefile
|
85
|
+
- ext/fswatch/callback.c
|
86
|
+
- ext/fswatch/configure.c
|
87
|
+
- ext/fswatch/enums.c
|
88
|
+
- ext/fswatch/extconf.rb
|
89
|
+
- ext/fswatch/fswatch.c
|
90
|
+
- ext/fswatch/fswatch.h
|
91
|
+
- ext/fswatch/monitor.c
|
92
|
+
- fswatch-rb.gemspec
|
93
|
+
- lib/fswatch.rb
|
94
|
+
- lib/fswatch/monitor.rb
|
95
|
+
- lib/fswatch/watcher.rb
|
96
|
+
homepage: https://github.com/t3hk0d3/fswatch-rb
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.7.3
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: Ruby binding for libfswatch library
|
120
|
+
test_files: []
|