Title: | Object Oriented R |
---|---|
Description: | Lightweight tools for building object-oriented R code. |
Authors: | Steve Walker [aut, cre] |
Maintainer: | Steve Walker <[email protected]> |
License: | GPL (>= 3) |
Version: | 0.0.1 |
Built: | 2024-11-13 04:27:18 UTC |
Source: | https://github.com/canmod/oor |
Initialize an empty object.
Base(starting_environment = emptyenv())
Base(starting_environment = emptyenv())
starting_environment |
An environment to enclose the empty object.
This enclosing environment is useful for making things other than |
Inherit from Base
if you want to start
from an empty class
empty_object = Base() print(empty_object) names(empty_object) Printer = function(x) { self = Base() self$.x = x self$print = function() print(self$.x) return_object(self, "Printer") } printer = Printer("something to print") printer$print() SupportivePrinter = function(x) { self = Printer(x) self$print = function() { print(paste(sQuote(self$.x), "is a very nice thing to say")) } return_object(self, "Supportive") } supportive_printer = SupportivePrinter("something to print") supportive_printer$print()
empty_object = Base() print(empty_object) names(empty_object) Printer = function(x) { self = Base() self$.x = x self$print = function() print(self$.x) return_object(self, "Printer") } printer = Printer("something to print") printer$print() SupportivePrinter = function(x) { self = Printer(x) self$print = function() { print(paste(sQuote(self$.x), "is a very nice thing to say")) } return_object(self, "Supportive") } supportive_printer = SupportivePrinter("something to print") supportive_printer$print()
Clean the environment of a method (or methods) so that they contain a single object – self – which is the environment defining an object
clean_method_environment(e)
clean_method_environment(e)
e |
Environment containing methods in an object |
There is no return value. The function is called for its side-effect of cleaning a method environment.
Initialize an object with concrete implementations of abstract method definitions.
Implementation()
Implementation()
Inherit
from Implementation
(using the
implements
utility function) if you want
your class to implement an Interface
.
BinaryOperation = function() { self = Interface() self$operate = function(x = numeric(1L), y = numeric(1L)) return(numeric(1L)) return_object(self, "BinaryOperation") } Add = function() { self = implements(BinaryOperation) self$operate = function(x = numeric(1L), y = numeric(1L)) return(x + y) return_object(self, "Add") } Multiply = function() { self = implements(BinaryOperation) self$operate = function(x = numeric(1L), y = numeric(1L)) return(x * y) return_object(self, "Multiply") } Add()$operate(1, 1) Multiply()$operate(2, 2)
BinaryOperation = function() { self = Interface() self$operate = function(x = numeric(1L), y = numeric(1L)) return(numeric(1L)) return_object(self, "BinaryOperation") } Add = function() { self = implements(BinaryOperation) self$operate = function(x = numeric(1L), y = numeric(1L)) return(x + y) return_object(self, "Add") } Multiply = function() { self = implements(BinaryOperation) self$operate = function(x = numeric(1L), y = numeric(1L)) return(x * y) return_object(self, "Multiply") } Add()$operate(1, 1) Multiply()$operate(2, 2)
Inherit methods and fields from other classes.
inherit_from(parent, traits, ...) implements(interface)
inherit_from(parent, traits, ...) implements(interface)
parent |
A single class from which to inherit methods and fields. |
traits |
A vector of |
... |
Arguments to pass to the initialization of the |
interface |
Class definition that inherits from |
There are three ways to inherit from other classes:
(1) directly
(2) using the inherit_from
function
(3) using the implements
function
Each of these ways works by adding a line that creates an object called
self
at the beginning of a class definition. The object self
is an object of class ParentClass
and you are free to add new fields
and methods to this object.
self = ParentClass(...)
Here ParentClass
is the name of the parent class being inherited from.
In most cases, direct inheritance is the most useful approach. The other
two are for more advanced use.
self = inherit_from(ParentClass, list_of_trait_classes)
Here ParentClass
is the class being directly inherited from
and list_of_trait_classes
is a list of Trait
classes containing methods to be forwarded to self
.
self = implements(ParentInterface)
Here ParentInterface
is an abstract set of method signatures.
Following this initialization, concrete definitions of these abstract
method need to be added to self
. This process is referred to as
implementing an interface.
Initialize an empty abstract class.
Interface()
Interface()
Inherit from Interface
to define the
argument signatures and return value types
of abstract methods.
BinaryOperation = function() { self = Interface() self$operate = function(x = numeric(1L), y = numeric(1L)) return(numeric(1L)) return_object(self, "BinaryOperation") }
BinaryOperation = function() { self = Interface() self$operate = function(x = numeric(1L), y = numeric(1L)) return(numeric(1L)) return_object(self, "BinaryOperation") }
Test inheritance
Is(class)
Is(class)
class |
Name of a class to test for. |
Test that all MappedTest
results are TRUE
MappedAllTest(basic_tester)
MappedAllTest(basic_tester)
basic_tester |
An object that can be converted to a function |
Test that any MappedTest
results are TRUE
MappedAnyTest(basic_tester)
MappedAnyTest(basic_tester)
basic_tester |
An object that can be converted to a function |
Apply a Summarizer
to each element of a list, in order
to test that a particular summary of each lists item meets a certain
criterion. MappedSummarizer
s are typically included in
TestPipeline
s.
MappedSummarizer(...)
MappedSummarizer(...)
... |
A list of summarizing functions. |
Object of class Test
that summarizes each
element of objects to test.
Apply a Test
to each element of a list.
MappedTest(basic_tester, boolean_aggregator)
MappedTest(basic_tester, boolean_aggregator)
basic_tester |
An object that can be converted to a function |
boolean_aggregator |
A function that summarizes a |
Call a method for each item in a list of objects.
method_apply(objects, method_name, ...)
method_apply(objects, method_name, ...)
objects |
List of objects. |
method_name |
Character string giving the name of the method. |
... |
Arguments to pass to the method. |
Assess several criteria.
MultiTest(test_function_list, boolean_aggregator) All(...) Any(...)
MultiTest(test_function_list, boolean_aggregator) All(...) Any(...)
test_function_list |
|
boolean_aggregator |
A function that summarizes a |
... |
Test functions. |
Object of class Test
that tests several
criteria at the same time.
All()
: Test that all of the criteria are met.
Any()
: Test that any of the criteria are met.
is_matrix = All( is.numeric, TestPipeline( Summarizer(dim, length), TestRange(0, 2) ) ) is_matrix$apply(array("a", c(1))) # FALSE is_matrix$apply(array("a", c(1, 1, 2))) # FALSE is_matrix$apply(array(1, c(1, 1, 2))) # FALSE is_matrix$apply(array(1, c(1, 2))) # TRUE is_matrix$apply(1) # TRUE
is_matrix = All( is.numeric, TestPipeline( Summarizer(dim, length), TestRange(0, 2) ) ) is_matrix$apply(array("a", c(1))) # FALSE is_matrix$apply(array("a", c(1, 1, 2))) # FALSE is_matrix$apply(array(1, c(1, 1, 2))) # FALSE is_matrix$apply(array(1, c(1, 2))) # TRUE is_matrix$apply(1) # TRUE
Not
Not(basic_tester)
Not(basic_tester)
basic_tester |
An object that can be converted to a function |
Object of class Test
that evaluates the
complement of basic_tester
.
Not(is.numeric)$apply(1) # FALSE Not(is.numeric)$apply("1") # TRUE
Not(is.numeric)$apply(1) # FALSE Not(is.numeric)$apply("1") # TRUE
Experimental
return_facade(self, private, class)
return_facade(self, private, class)
self |
New object. |
private |
Environment to use for containing private methods and fields. |
class |
String giving the class name. |
This should be the final function called in a class definition. Think of it like return(...)
return_object(self, class)
return_object(self, class)
self |
New object. |
class |
String giving the class name. |
New object of class given by class
.
Summarize an object to be tested, so that the test is applied to the
summary and not the object itself (e.g. length(dim(object)) == 2L
).
Summarizer
s are typically included in TestPipeline
s.
Summarizer(...)
Summarizer(...)
... |
A list of summarizing functions. |
Object of class Test
that summarizes objects
to test.
Abstract Class Testing Objects
Test() ## S3 method for class 'Test' as.function(x, ...)
Test() ## S3 method for class 'Test' as.function(x, ...)
x |
|
... |
Not used. Present for S3 method consistency. |
Object with an apply
method that takes a single
argument, x
, and returns a length-one logical
vector.
Initialize an object with functionality for validating objects
Testable()
Testable()
Inherit from Testable
if you
would like your class to provide a validity
check. Validity checking often requires differentiating between public
versus private members, as well as fields versus methods.
Printer = function(x) { self = Testable() self$.x = x self$valid = function() { if (!is.character(self$.x)) { return("can only print character strings") } if (length(self$.x) != 1L) { return("can only print length-1 character vectors") } return(TRUE) } self$print = function() print(self$.x) return_object(self, "Printer") } printer = Printer("something to print") printer$print() try(Printer(0)) ## error
Printer = function(x) { self = Testable() self$.x = x self$valid = function() { if (!is.character(self$.x)) { return("can only print character strings") } if (length(self$.x) != 1L) { return("can only print length-1 character vectors") } return(TRUE) } self$print = function() print(self$.x) return_object(self, "Printer") } printer = Printer("something to print") printer$print() try(Printer(0)) ## error
Basic Test
TestBasic(basic_tester)
TestBasic(basic_tester)
basic_tester |
An object that can be converted to a function |
Object of class Test
that evaluates
the basic_tester
.
Test that all elements in an object are identical.
TestHomo()
TestHomo()
Object of class Test
that tests that all
elements in an object are identical.
Test Pipeline
TestPipeline(...)
TestPipeline(...)
... |
Objects of class |
Object inheriting from Test
is_matrix = TestPipeline( Summarizer(dim, length), TestRange(0, 2) ) is_matrix$apply(array("a", c(1))) # TRUE is_matrix$apply(array(1, c(1, 2, 3))) # FALSE each_is_matrix = TestPipeline( MappedSummarizer(dim, length), All(TestRange(0, 2)) ) each_is_matrix$apply(list(1, matrix(1, 2, 3), "a")) # TRUE each_is_matrix$apply(list(1, array(1, c(2, 3, 4)), "a")) # FALSE
is_matrix = TestPipeline( Summarizer(dim, length), TestRange(0, 2) ) is_matrix$apply(array("a", c(1))) # TRUE is_matrix$apply(array(1, c(1, 2, 3))) # FALSE each_is_matrix = TestPipeline( MappedSummarizer(dim, length), All(TestRange(0, 2)) ) each_is_matrix$apply(list(1, matrix(1, 2, 3), "a")) # TRUE each_is_matrix$apply(list(1, array(1, c(2, 3, 4)), "a")) # FALSE
Test that all elements in an object greater than or equal to lower
and less than or equal to upper
.
TestRange(lower, upper)
TestRange(lower, upper)
lower |
Lower bound |
upper |
Upper bound |
Object of class Test
that tests that all
elements in an object numerically on a particular range.
Test that all elements in an object are in set
TestSubset(set)
TestSubset(set)
set |
Universe of possibilities. |
Object of class Test
that tests that all
elements in an object are in a particular set.
Initialize an object with methods that are intended to be forwarded to other classes.
Trait()
Trait()
Inherit from Trait
if you want to use
your class to forward public methods to other classes without direct
inheritance.
Print = function(x) { self = Testable() self$.x = x return_object(self, "Print") } Printer = function() { self = Trait() self$print = function() print(self$.x) return_object(self, "Printer") } PrintString = function(x) { self = inherit_from(Print, list(Printer), x) self$valid = function() { if (!is.character(self$.x)) return("can only print character strings") if (length(self$.x) != 1L) return("can only print length-1 character vectors") return(TRUE) } return_object(self, "PrintString") } PrintNumber = function(x) { self = inherit_from(Print, list(Printer), x) self$valid = function() { if (!is.numeric(self$.x)) return("can only print character strings") return(TRUE) } return_object(self, "PrintNumber") } PrintString("something to print")$print() PrintNumber(pi)$print() try(PrintNumber("not a number")) ## error
Print = function(x) { self = Testable() self$.x = x return_object(self, "Print") } Printer = function() { self = Trait() self$print = function() print(self$.x) return_object(self, "Printer") } PrintString = function(x) { self = inherit_from(Print, list(Printer), x) self$valid = function() { if (!is.character(self$.x)) return("can only print character strings") if (length(self$.x) != 1L) return("can only print length-1 character vectors") return(TRUE) } return_object(self, "PrintString") } PrintNumber = function(x) { self = inherit_from(Print, list(Printer), x) self$valid = function() { if (!is.numeric(self$.x)) return("can only print character strings") return(TRUE) } return_object(self, "PrintNumber") } PrintString("something to print")$print() PrintNumber(pi)$print() try(PrintNumber("not a number")) ## error
S3 generic for checking the validity of a constructed object. should either return nothing or trigger an error.
validate_object(object)
validate_object(object)
object |
Object to be validated. |
TODO – check $valid methods
Couple a test function with a failure message
ValidityMessager(test_function, ...)
ValidityMessager(test_function, ...)
test_function |
Object that is coercible to a |
... |
Length-1 |
ValidityMessager
objects have an assert
method
with one argument, x
. If the test function evaluates to
TRUE
then the argument, x
, is returned. If
it does not return TRUE
then the failure message is given.
Object of class ValidityMessager
containing a check
method that will return TRUE
or fail with fail_message
.
is_numeric = ValidityMessager(is.numeric, "not numeric") try(is_numeric$check("1")) HoldANumber = function(x) { self = Base() self$x = is_numeric$assert(x) return_object(self, "HoldANumber") } try(HoldANumber("a")) ## error message HoldANumber(1) ## success
is_numeric = ValidityMessager(is.numeric, "not numeric") try(is_numeric$check("1")) HoldANumber = function(x) { self = Base() self$x = is_numeric$assert(x) return_object(self, "HoldANumber") } try(HoldANumber("a")) ## error message HoldANumber(1) ## success