The R Project SVN R

Rev

Rev 56572 | Rev 57183 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
52960 jmc 1
## simple call, only field names
2
fg <- setRefClass("foo", c("bar", "flag"))
56211 maechler 3
f1 <- new("foo")
52960 jmc 4
f1$bar
5
f1 <- fg$new(flag = "testing")
6
f1$bar <- 1
7
stopifnot(identical(f1$bar, 1))
8
fg$methods(showAll = function() c(bar, flag))
9
stopifnot(all.equal(f1$showAll(), c(1, "testing")))
56985 maechler 10
str(f1)
52960 jmc 11
 
56211 maechler 12
fg <- setRefClass("foo", list(bar = "numeric", flag = "character",
13
                              tag = "ANY"),
14
                  methods = list(addToBar = function(incr) {
15
                      b <- bar + incr
16
                      bar <<- b
17
                      b
18
                  } )
19
                  )
20
ff <- new("foo", bar = 1.5)
52816 jmc 21
stopifnot(identical(ff$bar, 1.5))
22
ff$bar <- pi
23
stopifnot(identical(ff$bar, pi))
52903 jmc 24
## test against generator
52816 jmc 25
 
52903 jmc 26
f2 <- fg$new(bar = pi)
27
## identical does not return TRUE if *contents* of env are identical
28
stopifnot(identical(ff$bar, f2$bar), identical(ff$flag, f2$flag))
29
 
52960 jmc 30
f2$flag <- "standard flag"
31
stopifnot(identical(f2$flag, "standard flag"))
52903 jmc 32
 
56985 maechler 33
str(f2)
34
 
52960 jmc 35
## fg$lock("flag")
36
## tryCatch(f2$flag <- "other", error = function(e)e)
52816 jmc 37
 
52960 jmc 38
 
39
## add some accessor methods
40
fg$accessors("bar")
41
 
52816 jmc 42
ff$setBar(1:3)
52960 jmc 43
stopifnot(identical(ff$getBar(), 1:3))
52816 jmc 44
 
45
ff$getBar()
46
stopifnot(all.equal(ff$addToBar(1), 2:4))
47
 
52960 jmc 48
 
52903 jmc 49
## Add a method
50
fg$methods(barTimes = function(x) {
51
    "This method multiples field bar by argument x
52
and this string is self-documentation"
53
    setBar(getBar() * x)})
54
 
55
ffbar <- ff$getBar()
56
ff$barTimes(10)
57
stopifnot(all.equal(ffbar * 10, ff$getBar()))
58
ff$barTimes(.1)
59
 
52846 jmc 60
## inheritance.  redefines flag so should fail:
56211 maechler 61
stopifnot(is(tryCatch(setRefClass("foo2", list(b2 = "numeric",
62
					       flag = "complex"),
52816 jmc 63
            contains = "foo",
52903 jmc 64
            refMethods = list(addBoth = function(incr) {
52816 jmc 65
                addToBar(incr) #uses inherited class method
66
                setB2(getB2() + incr)
52903 jmc 67
                })),
68
          error = function(e)e), "error"))
52846 jmc 69
## but with flag as a subclass of "character", should work
56045 jmc 70
## Also subclasses "tag" which had class "ANY before
56211 maechler 71
setClass("ratedChar", contains = "character",
72
         representation(score = "numeric"))
73
foo2 <- setRefClass("foo2", list(b2 = "numeric", flag = "ratedChar",
74
				 tag = "numeric"),
75
	    contains = "foo",
76
	    methods = list(addBoth = function(incr) {
52846 jmc 77
                addToBar(incr) #uses inherited class method
56211 maechler 78
                b2 <<- b2 + incr
52816 jmc 79
                }))
52960 jmc 80
## now lock the flag field; should still allow one write
81
foo2$lock("flag")
56211 maechler 82
f2 <- foo2$new(bar = -3, flag = as("ANY", "ratedChar"),
83
               b2 = ff$bar, tag = 1.5)
52960 jmc 84
## but not a second one
85
stopifnot(is(tryCatch(f2$flag <- "Try again",
86
         error = function(e)e), "error"))
56985 maechler 87
str(f2)
52960 jmc 88
f22 <- foo2$new(bar = f2$bar)
89
## same story if assignment follows the initialization
90
f22$flag <- f2$flag
91
stopifnot(is(tryCatch(f22$flag <- "Try again",
92
         error = function(e)e), "error"))
93
## Exporting superclass object
52903 jmc 94
f22 <- fg$new(bar = f2$bar, flag = f2$flag)
95
f2e <- f2$export("foo")
96
stopifnot(identical(f2e$bar, f22$bar), identical(f2e$flag, f22$flag),
97
          identical(class(f2e), class(f22)))
56211 maechler 98
stopifnot(identical(f2$flag,  as("ANY", "ratedChar")),
99
          identical(f2$bar, -3),
52816 jmc 100
          all.equal(f2$b2, 2:4+0))
101
f2$addBoth(-1)
102
stopifnot(all.equal(f2$bar, -4), all.equal(f2$b2, 1:3+0))
103
 
52960 jmc 104
## test callSuper()
105
setRefClass("foo3", fields = list(flag2 = "ratedChar"),
106
            contains = "foo2",
56211 maechler 107
	    methods = list(addBoth = function(incr) {
108
		callSuper(incr)
109
		flag2 <<- as(paste(flag, paste(incr, collapse = ", "),
110
				   sep = "; "),
111
                             "ratedChar")
52816 jmc 112
                incr
113
            }))
114
 
52960 jmc 115
f2 <- foo2$new(bar = -3, flag = as("ANY", "ratedChar"), b2 =  1:3)
52846 jmc 116
f3 <- new("foo3")
117
f3$import(f2)
56211 maechler 118
stopifnot(all.equal(f3$b2, f2$b2), all.equal(f3$bar, f2$bar),
119
          all.equal(f3$flag, f2$flag))
52871 jmc 120
f3$addBoth(1)
52960 jmc 121
stopifnot(all.equal(f3$bar, -2), all.equal(f3$b2, 2:4+0),
122
          all.equal(f3$flag2, as("ANY; 1", "ratedChar")))
52871 jmc 123
 
52960 jmc 124
## but the import should have used up the one write for $flag
125
stopifnot(is(tryCatch(f3$flag <- "Try again",
126
         error = function(e)e), "error"))
56985 maechler 127
str(f3)
52960 jmc 128
 
129
## a class with an initialize method, and an extra slot
130
setOldClass(c("simple.list", "list"))
131
fg4 <- setRefClass("foo4",
132
            contains = "foo2",
133
            methods = list(
134
              initialize = function(...) {
55779 jmc 135
                  .self$initFields(...)
136
                  .self@made <<- R.version
52960 jmc 137
                  .self
138
              }),
139
            representation = list(made = "simple.list")
140
            )
141
 
142
f4 <- new("foo4", flag = "another test", bar = 1:3)
143
stopifnot(identical(f4@made, R.version))
52985 jmc 144
 
56036 jmc 145
## a trivial class with no fields, using fields = list(), failed up to rev 56035
146
foo5 <- setRefClass("foo5", fields = list(),
56211 maechler 147
                    methods = list(bar = function(test)
148
                    paste("*",test,"*")))
56036 jmc 149
 
150
f5 <- foo5$new()
151
stopifnot(identical( f5$bar("xxx"), paste("*","xxx", "*")))
152
 
153
 
52985 jmc 154
## simple active binding test
155
abGen <- setRefClass("ab",
156
                  fields = list(a = "ANY",
157
                  b = function(x) if(missing(x)) a else {a <<- x; x}))
158
 
159
ab1 <- abGen$new(a = 1)
160
 
161
stopifnot(identical(ab1$a, 1), identical(ab1$b, 1))
162
 
163
ab1$b <- 2
164
 
165
stopifnot(identical(ab1$a, 2), identical(ab1$b, 2))
53068 jmc 166
 
167
## a simple editor for matrix objects.  Method  $edit() changes some
168
## range of values; method $undo() undoes the last edit.
169
mEditor <- setRefClass("matrixEditor",
56211 maechler 170
        fields = list(data = "matrix",
171
		     edits = "list"),
172
       methods = list(
53068 jmc 173
     edit = function(i, j, value) {
174
       ## the following string documents the edit method
175
       'Replaces the range [i, j] of the
56211 maechler 176
	object by value.
53068 jmc 177
        '
178
         backup <-
179
             list(i, j, data[i,j])
180
         data[i,j] <<- value
181
         edits <<- c(list(backup),
182
                     edits)
183
         invisible(value)
184
     },
185
     undo = function() {
186
       'Undoes the last edit() operation
187
        and update the edits field accordingly.
188
        '
189
         prev <- edits
190
         if(length(prev)) prev <- prev[[1]]
191
         else stop("No more edits to undo")
192
         edit(prev[[1]], prev[[2]], prev[[3]])
193
         ## trim the edits list
194
         length(edits) <<- length(edits) - 2
195
         invisible(prev)
196
     }
197
     ))
198
xMat <- matrix(1:12,4,3)
199
xx <- mEditor$new(data = xMat)
200
xx$edit(2, 2, 0)
201
xx$data
202
xx$undo()
203
mEditor$help("undo")
204
stopifnot(all.equal(xx$data, xMat))
205
 
206
## add a method to save the object
207
mEditor$methods(
208
     save = function(file) {
209
       'Save the current object on the file
210
        in R external object format.
211
       '
212
         base::save(.self, file = file)
213
     }
214
)
215
 
216
tf <- tempfile()
217
xx$save(tf) #$
218
load(tf)
219
unlink(tf)
220
stopifnot(identical(xx$data, .self$data))
221
 
222
markViewer <- ""
223
setMarkViewer <- function(what)
224
    markViewer <<- what
225
 
226
## Inheriting a reference class:  a matrix viewer
227
mv <- setRefClass("matrixViewer",
228
    fields = c("viewerDevice", "viewerFile"),
229
    contains = "matrixEditor",
230
    methods = list( view = function() {
231
        dd <- dev.cur(); dev.set(viewerDevice)
232
        devAskNewPage(FALSE)
233
        matplot(data, main = paste("After",length(edits),"edits"))
234
        dev.set(dd)},
235
        edit = # invoke previous method, then replot
236
          function(i, j, value) {
237
            callSuper(i, j, value)
238
            view()
239
          }))
240
 
241
## initialize and finalize methods
53470 jmc 242
mv$methods( initialize = function(file = "./matrixView.pdf", ...) {
243
    viewerFile <<- file
53068 jmc 244
    pdf(viewerFile)
245
    viewerDevice <<- dev.cur()
246
    message("Plotting to ", viewerFile)
247
    dev.set(dev.prev())
248
    setMarkViewer("ON")
249
    initFields(...)
250
  },
251
  finalize = function() {
252
    dev.off(viewerDevice)
253
    setMarkViewer("OFF")
254
  })
255
 
56211 maechler 256
ff <- mv$new( data = xMat)
53068 jmc 257
stopifnot(identical(markViewer, "ON")) # check initialize
258
ff$edit(2,2,0)
259
ff$data
260
ff$undo()
261
stopifnot(all.equal(ff$data, xMat))
262
rm(ff)
263
gc()
264
stopifnot(identical(markViewer, "OFF")) #check finalize
53212 jmc 265
 
53669 jmc 266
## tests of copying
267
viewerPlus <- setRefClass("viewerPlus",
268
                   fields = list( text = "character",
269
                      viewer = "matrixViewer"))
270
ff <- mv$new( data = xMat)
271
v1 <- viewerPlus$new(text = letters, viewer = ff)
272
v2 <- v1$copy()
273
v3 <- v1$copy(TRUE)
274
v2$text <- "Hello, world"
275
v2$viewer$data <- t(xMat) # change a field in v2$viewer
276
v3$text <- LETTERS
277
v3$viewer <- mv$new( data = matrix(nrow=1,ncol=1))
278
## with a deep copy all is protected, with a shallow copy
279
## the environment of a copied field remains the same,
280
## but replacing the whole field should be local
281
stopifnot(identical(v1$text, letters),
282
          identical(v1$viewer, ff),
283
          identical(v2$text, "Hello, world"))
284
v3 <- v1$copy(TRUE)
285
v3$viewer$data <- t(xMat) # should modify v1$viewer as well
286
stopifnot(identical(v1$viewer$data, t(xMat)))
287
 
53693 jmc 288
## the field() method
289
stopifnot(identical(v1$text, v1$field("text")))
290
v1$field("text", "Now is the time")
291
stopifnot(identical(v1$field("text"), "Now is the time"))
292
 
293
## setting a non-existent field, or a method, should throw an error
294
stopifnot(is(tryCatch(v1$field("foobar", 0), error = function(e)e), "error"),
295
         is(tryCatch(v1$field("copy", 0), error = function(e)e), "error") )
296
 
53669 jmc 297
## the methods to extract class definition and generator
53693 jmc 298
stopifnot(identical(v3$getRefClass()$def, getRefClass("viewerPlus")$def),
299
          identical(v3$getClass(), getClass("viewerPlus")))
53669 jmc 300
 
53212 jmc 301
## deal correctly with inherited methods and overriding existing
302
## methods from $methods(...)
303
refClassA <- setRefClass("refClassA", methods=list(foo=function() "A"))
304
refClassB <- setRefClass("refClassB", contains="refClassA")
305
mnames <- objects(getClass("refClassB")@refMethods)
306
refClassB$methods(foo=function() callSuper())
307
stopifnot(identical(refClassB$new()$foo(), "A"))
308
mnames2 <- objects(getClass("refClassB")@refMethods)
309
stopifnot(identical(mnames2[is.na(match(mnames2,mnames))], "foo#refClassA"))
310
refClassB$methods(foo=function() paste(callSuper(), "Version 2"))
311
stopifnot(identical(refClassB$new()$foo(), "A Version 2"))
312
stopifnot(identical(mnames2, objects(getClass("refClassB")@refMethods)))
53274 jmc 313
 
314
if(methods:::.hasCodeTools()) {
54575 jmc 315
    ## code warnings assigning locally to field names
56211 maechler 316
    stopifnot(is(tryCatch(mv$methods(test = function(x)
317
                                 { data <- x[!is.na(x)]; mean(data)}),
318
                          warning = function(e)e), "warning"))
53274 jmc 319
 
54575 jmc 320
    ## warnings for nonlocal assignment that is not a field
321
    stopifnot(is(tryCatch(mv$methods(test2 = function(x) {something <<- data[!is.na(x)]}), warning = function(e)e), "warning"))
322
 
323
    ## error for trying to assign to a method name
324
    stopifnot(is(tryCatch(mv$methods(test3 = function(x) {edit <<- data[!is.na(x)]}), error = function(e)e), "error"))
53274 jmc 325
} else
326
    warning("Can't run some tests:  recommended package codetools is not available")
53330 jmc 327
 
328
## tests (fragmentary by necessity) of promptClass for reference class
53470 jmc 329
ccon <- textConnection("ctxt", "w")
330
suppressMessages(promptClass("refClassB", filename = ccon))
53330 jmc 331
## look for a method, inheritance, inherited method
332
stopifnot(length(c(grep("foo.*refClassA", ctxt),
333
                   grep("code{foo()}", ctxt, fixed = TRUE),
334
                   grep("linkS4class{refClassA", ctxt, fixed = TRUE))) >= 3)
53470 jmc 335
close(ccon)
53330 jmc 336
rm(ctxt)
53382 jmc 337
 
338
 
339
## tests related to subclassing environments.  These really test code in the core, viz. builtin.c
340
a <- refClassA$new()
341
ev <- new.env(parent = a) # parent= arg
342
stopifnot(is.environment(ev))
343
foo <- function()"A"; environment(foo) <- a # environment of function
344
stopifnot(identical(as.environment(a), environment(foo)))
345
xx <- 1:10; environment(xx) <- a # environment attribute
346
stopifnot(identical(as.environment(a), environment(xx)))
347
 
348
 
56211 maechler 349
## tests of [[<- and $<- for subclasses of environment.  At one point
350
## methods for these assignments were defined and caused
351
## inf. recursion when the arguments to the [[<- case were changed in base.
53385 jmc 352
setClass("myEnv", contains = "environment")
53641 jmc 353
m <- new("myEnv", a="test")
354
m2 <- new("myEnv"); m3 <- new("myEnv")
355
## test that new.env() is called for each new object
356
stopifnot(!identical(as.environment(m), as.environment(m2)),
357
          !identical(as.environment(m3), as.environment(m2)))
53385 jmc 358
m[["x"]] <- 1; m$y <- 2
56211 maechler 359
stopifnot(identical(c(m[["x"]], m$y), c(1,2)), is(m, "myEnv"))
53641 jmc 360
rm(x, envir = m) # check rm() works, does not clobber class
361
stopifnot(identical(sort(objects(m)), sort(c("a", "y"))),
362
          is(m, "myEnv"))
53465 jmc 363
 
53564 jmc 364
## tests of binding & environment tools with subclases of environment
365
lockBinding("y", m)
366
stopifnot(bindingIsLocked("y", m))
367
unlockBinding("y", m)
368
stopifnot(!bindingIsLocked("y", m))
369
 
370
makeActiveBinding("z", function(value) {
371
    if(missing(value))
372
        "dummy"
373
    else
374
        "dummy assignment"
375
}, m)
376
stopifnot(identical(get("z", m),"dummy"))
377
## assignment will return the value but do nothing
378
stopifnot(identical(assign("z","other", m), "other"),
379
          identical(get("z", m),"dummy"))
380
 
381
 
382
## this has to be last--Seems no way to unlock an environment!
383
lockEnvironment(m)
384
stopifnot(environmentIsLocked(m))
385
 
53645 maechler 386
rm(m)
387
m <- new("myEnv")
388
stopifnot(length(ls(m)) == 0)
389
## used to contain the previous content
53564 jmc 390
 
53645 maechler 391
 
53465 jmc 392
## test of callSuper() to a hidden default method for initialize() (== initFields)
393
TestClass <- setRefClass ("TestClass",
53470 jmc 394
     fields = list (text = "character"),
395
     methods = list(
396
       print = function ()  {cat(text)},
397
       initialize = function(text = "", ...) callSuper(text = paste(text, ":", sep=""),...)
398
  ))
399
tt <- TestClass$new("hello world")
400
stopifnot(identical(tt$text, "hello world:"))
401
## now a subclass with another field & another layer of callSuper()
402
TestClass2 <- setRefClass("TestClass2",
403
        contains = "TestClass",
404
        fields = list( version = "integer"),
405
        methods = list(
56572 jmc 406
          initialize = function(..., version = 0L)
407
              callSuper(..., version = version+1L))
53470 jmc 408
  )
56572 jmc 409
tt <- TestClass2$new("test", version = 1L)
53470 jmc 410
stopifnot(identical(tt$text, "test:"), identical(tt$version, as.integer(2)))
56572 jmc 411
tt <- TestClass2$new(version=3L) # default text
53470 jmc 412
stopifnot(identical(tt$text, ":"), identical(tt$version, as.integer(4)))
54575 jmc 413
 
414
 
55779 jmc 415
## test some capabilities but read-only for .self
416
.changeAllFields <- function(replacement) {
417
    fields <- names(.refClassDef@fieldClasses)
418
    for(field in fields)
419
        eval(substitute(.self$FIELD <- replacement$FIELD,
420
                        list(FIELD = field)))
421
}
422
 
423
mEditor$methods(change = .changeAllFields)
424
xx <- mEditor$new(data = xMat)
425
xx$edit(2, 2, 0)
426
 
427
yy <- mEditor$new(data = xMat+1)
428
yy$change(xx)
429
stopifnot(identical(yy$data, xx$data), identical(yy$edits, xx$edits))
430
 
431
## but don't allow assigment
432
if(methods:::.hasCodeTools())
433
        stopifnot(is(tryCatch(yy$.self$data <- xMat, error = function(e)e), "error"))
55988 jmc 434
 
435
## the locked binding of refObjectGenerator class should prevent modifying
436
## methods, locking fields or setting accessor methods
437
evr <- getRefClass("refObjectGenerator") # in methods
438
stopifnot(is(tryCatch(evr$methods(foo = function()"..."), error = function(e)e), "error"),
439
         is(tryCatch(evr$lock("def"), error = function(e)e), "error"),
440
         is(tryCatch(evr$accessors("def"), error = function(e)e), "error"))
56119 jmc 441
 
442
##getRefClass() method and function should work with either
443
## a class name or a class representation (bug report 14600)
444
tg <- setRefClass("tg", fields = "a")
445
t1 <- tg$new(a=1)
446
tgg <- t1$getRefClass()
447
tggg <- getRefClass("tg")
448
stopifnot(identical(tgg$def, tggg$def),
449
          identical(tg$def, tgg$def))
450
## TODO:  the className returned by setRefClass should have
451
## a package attribute, which would allow:
452
##          identical(tg$className, tgg$className))
453