Skip to content

Commit 79f80a5

Browse files
committed
use R6 class
1 parent f7949c0 commit 79f80a5

File tree

10 files changed

+87
-100
lines changed

10 files changed

+87
-100
lines changed

DESCRIPTION

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,18 @@ Authors@R:
1111
person(given = "Posit Software, PBC",
1212
role = "cph"))
1313
Description: R binding for 'libfswatch', a filesystem monitoring library. All
14-
functions are asynchronous and do not block your session. Set watches on
15-
files or directories recursively. Filter by type of event. Log activity, or
16-
trigger an R function to run when a specified event occurs.
14+
functions are asynchronous and do not block the session. Set watches on
15+
files or directories recursively. Log activity, or trigger an R function to
16+
run when an event occurs.
1717
License: MIT + file LICENSE
1818
BugReports: https://github.com/shikokuchuo/watcher/issues
1919
URL: https://shikokuchuo.net/watcher/, https://github.com/shikokuchuo/watcher/
2020
Encoding: UTF-8
2121
SystemRequirements: 'libfswatch', or 'cmake' to compile from package sources
2222
Depends:
2323
R (>= 3.6)
24+
Imports:
25+
R6
2426
Suggests:
2527
later,
2628
testthat (>= 3.0.0)

NAMESPACE

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Generated by roxygen2: do not edit by hand
22

3-
S3method(print,watch)
43
export(watcher)
5-
export(watcher_start)
6-
export(watcher_stop)
4+
importFrom(R6,R6Class)
75
useDynLib(watcher, .registration = TRUE)

R/watch.R

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,51 @@
1313
#' @param callback (optional) a function (taking no arguments) to be called each
1414
#' time an event is triggered - requires the \pkg{later} package. The default,
1515
#' `NULL`, causes event paths and types to be written to `stdout` instead.
16-
#' @param watch a 'watch' object.
1716
#'
18-
#' @return For [watcher]: a 'watch' object. \cr
19-
#' For [watcher_start] and [watcher_stop]: invisibly, `TRUE` upon success or
20-
#' `FALSE` otherwise.
17+
#' @return A 'Watcher' R6 class object.
2118
#'
2219
#' @examples
23-
#' watch <- watcher(tempdir())
24-
#' isTRUE(watcher_start(watch))
25-
#' watch
26-
#' isTRUE(watcher_stop(watch))
27-
#' watch
20+
#' w <- watcher(tempdir())
21+
#' isTRUE(w$start())
22+
#' w
23+
#' isTRUE(w$stop())
24+
#' w
2825
#'
2926
#' @export
3027
#'
3128
watcher <- function(path = getwd(), recursive = TRUE, callback = NULL) {
32-
.Call(watcher_create, path, recursive, callback)
29+
Watcher$new(path, recursive, callback)
3330
}
3431

35-
#' @rdname watcher
36-
#' @export
37-
#'
38-
watcher_start <- function(watch) {
39-
invisible(.Call(watcher_start_monitor, watch))
40-
}
41-
42-
#' @rdname watcher
43-
#' @export
44-
#'
45-
watcher_stop <- function(watch) {
46-
invisible(.Call(watcher_stop_monitor, watch))
47-
}
48-
49-
#' @export
50-
#'
51-
print.watch <- function(x, ...) {
52-
53-
cat(sprintf("< watch >\n path: %s\n recursive: %s\n active: %s\n",
54-
attr(x, "path"), attr(x, "recursive"), attr(x, "active")))
55-
invisible(x)
56-
57-
}
32+
Watcher <- R6Class(
33+
"Watcher",
34+
public = list(
35+
active = FALSE,
36+
callback = NULL,
37+
path = NULL,
38+
recursive = TRUE,
39+
initialize = function(path = getwd(), recursive = TRUE, callback = NULL) {
40+
self$path <- path.expand(path)
41+
self$recursive <- as.logical(recursive)
42+
self$callback <- callback
43+
private$watch <- .Call(watcher_create, self$path, self$recursive, self$callback)
44+
},
45+
start = function() {
46+
res <- .Call(watcher_start_monitor, private$watch)
47+
if (res) self$active <- TRUE
48+
invisible(res)
49+
},
50+
stop = function() {
51+
res <- invisible(.Call(watcher_stop_monitor, private$watch))
52+
if (res) self$active <- FALSE
53+
invisible(res)
54+
},
55+
print = function(...) {
56+
cat(sprintf("<Watcher>\n start()\n stop()\n path: %s\n recursive: %s\n active: %s\n",
57+
self$path, self$recursive, self$active))
58+
}
59+
),
60+
private = list(
61+
watch = NULL
62+
)
63+
)

R/watcher-package.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#' @importFrom R6 R6Class
12
#' @useDynLib watcher, .registration = TRUE
23
#' @keywords internal
34
"_PACKAGE"

README.Rmd

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,19 @@ dir <- file.path(tempdir(), "watcher-example")
5151
dir.create(dir)
5252
5353
w <- watcher(dir, recursive = TRUE, callback = function() print("event triggered"))
54+
w
55+
w$start()
5456
55-
watcher_start(w)
5657
Sys.sleep(1L)
5758
file.create(file.path(dir, "oldfile"))
5859
later::run_now(2L)
60+
5961
file.rename(file.path(dir, "oldfile"), file.path(dir, "newfile"))
6062
later::run_now(2L)
63+
6164
file.remove(file.path(dir, "newfile"))
6265
later::run_now(2L)
63-
watcher_stop(w)
6466
67+
w$stop()
6568
unlink(dir, force = TRUE)
6669
```

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,31 @@ dir <- file.path(tempdir(), "watcher-example")
4545
dir.create(dir)
4646

4747
w <- watcher(dir, recursive = TRUE, callback = function() print("event triggered"))
48+
w
49+
#> <Watcher>
50+
#> start()
51+
#> stop()
52+
#> path: /tmp/RtmpTjz7M7/watcher-example
53+
#> recursive: TRUE
54+
#> active: FALSE
55+
w$start()
4856

49-
watcher_start(w)
5057
Sys.sleep(1L)
5158
file.create(file.path(dir, "oldfile"))
5259
#> [1] TRUE
5360
later::run_now(2L)
5461
#> [1] "event triggered"
62+
5563
file.rename(file.path(dir, "oldfile"), file.path(dir, "newfile"))
5664
#> [1] TRUE
5765
later::run_now(2L)
5866
#> [1] "event triggered"
67+
5968
file.remove(file.path(dir, "newfile"))
6069
#> [1] TRUE
6170
later::run_now(2L)
6271
#> [1] "event triggered"
63-
watcher_stop(w)
6472

73+
w$stop()
6574
unlink(dir, force = TRUE)
6675
```

man/watcher-package.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/watcher.Rd

Lines changed: 6 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/watcher.c

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ void* watcher_thread(void *args) {
5959

6060
SEXP watcher_create(SEXP path, SEXP recursive, SEXP callback) {
6161

62-
const char *watch_path = R_ExpandFileName(CHAR(STRING_ELT(path, 0)));
62+
const char *watch_path = CHAR(STRING_ELT(path, 0));
6363
const int recurse = LOGICAL(recursive)[0];
6464
if (callback != R_NilValue) {
6565
SEXPTYPE typ = TYPEOF(callback);
@@ -93,14 +93,9 @@ SEXP watcher_create(SEXP path, SEXP recursive, SEXP callback) {
9393
}
9494
}
9595

96-
SEXP out, PathSymbol;
97-
PathSymbol = Rf_install("path");
96+
SEXP out;
9897
PROTECT(out = R_MakeExternalPtr(handle, R_NilValue, callback));
9998
R_RegisterCFinalizerEx(out, session_finalizer, TRUE);
100-
Rf_setAttrib(out, PathSymbol, Rf_mkString(watch_path));
101-
Rf_setAttrib(out, Rf_install("recursive"), Rf_ScalarLogical(recurse));
102-
Rf_setAttrib(out, Rf_install("active"), Rf_ScalarLogical(0));
103-
Rf_classgets(out, Rf_mkString("watch"));
10499

105100
UNPROTECT(1);
106101
return out;
@@ -109,33 +104,21 @@ SEXP watcher_create(SEXP path, SEXP recursive, SEXP callback) {
109104

110105
SEXP watcher_start_monitor(SEXP session) {
111106

112-
if (TYPEOF(session) != EXTPTRSXP)
113-
return Rf_ScalarLogical(0);
114-
115107
FSW_HANDLE handle = (FSW_HANDLE) R_ExternalPtrAddr(session);
116108
pthread_t thr;
117109

118110
if (eln2 == NULL && R_ToplevelExec(load_later_safe, NULL)) {
119111
eln2 = (void (*)(void (*)(void *), void *, double, int)) R_GetCCallable("later", "execLaterNative2");
120112
}
121113

122-
const int started = pthread_create(&thr, NULL, &watcher_thread, handle) == 0;
123-
Rf_setAttrib(session, Rf_install("active"), Rf_ScalarLogical(started));
124-
125-
return Rf_ScalarLogical(started);
114+
return Rf_ScalarLogical(pthread_create(&thr, NULL, &watcher_thread, handle) == 0);
126115

127116
}
128117

129118
SEXP watcher_stop_monitor(SEXP session) {
130119

131-
if (TYPEOF(session) != EXTPTRSXP)
132-
return Rf_ScalarLogical(0);
133-
134120
FSW_HANDLE handle = (FSW_HANDLE) R_ExternalPtrAddr(session);
135121

136-
const int stopped = fsw_stop_monitor(handle) == FSW_OK;
137-
Rf_setAttrib(session, Rf_install("active"), Rf_ScalarLogical(!stopped));
138-
139-
return Rf_ScalarLogical(stopped);
122+
return Rf_ScalarLogical(fsw_stop_monitor(handle) == FSW_OK);
140123

141124
}

tests/testthat/test-watch.R

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,24 @@ test_that("watcher() watches", {
55
dir.create(dir)
66
dir.create(subdir)
77
w <- watcher(dir, recursive = FALSE, callback = NULL)
8-
expect_s3_class(w, "watch")
9-
expect_false(attr(w, "recursive"))
10-
expect_false(attr(w, "active"))
11-
expect_true(watcher_start(w))
12-
expect_true(attr(w, "active"))
8+
expect_s3_class(w, "Watcher")
9+
expect_false(w$recursive)
10+
expect_false(w$active)
11+
expect_true(w$start())
12+
expect_true(w$active)
1313
Sys.sleep(1L)
1414
file.create(file.path(dir, "testfile"))
1515
Sys.sleep(1L)
16-
expect_true(watcher_stop(w))
17-
expect_false(attr(w, "active"))
16+
expect_true(w$stop())
17+
expect_false(w$active)
1818
w <- watcher(dir, recursive = TRUE, callback = function() x <<- x + 1L)
1919
expect_output(print(w))
20-
expect_s3_class(w, "watch")
21-
expect_true(attr(w, "recursive"))
22-
expect_false(attr(w, "active"))
20+
expect_s3_class(w, "Watcher")
21+
expect_true(w$recursive)
22+
expect_false(w$active)
2323
skip_if_not_installed("later")
24-
expect_true(watcher_start(w))
25-
expect_true(attr(w, "active"))
24+
expect_true(w$start())
25+
expect_true(w$active)
2626
Sys.sleep(1L)
2727
file.create(file.path(subdir, "oldfile"))
2828
later::run_now(2L)
@@ -42,13 +42,8 @@ test_that("watcher() watches", {
4242
file.remove(file.path(dir, "newfile"))
4343
later::run_now(2L)
4444
expect_equal(x, 6L)
45-
expect_true(watcher_stop(w))
46-
expect_false(attr(w, "active"))
45+
expect_true(w$stop())
46+
expect_false(w$active)
4747
unlink(dir, recursive = TRUE, force = TRUE)
48-
})
49-
50-
test_that("watcher() error handling", {
5148
expect_error(watcher(callback = "callback"), "must be a function")
52-
expect_false(watcher_start("test"))
53-
expect_false(watcher_stop("test"))
5449
})

0 commit comments

Comments
 (0)