| Title: | Schema-Based Validation of 'R' Objects with User-Defined Rules |
| Version: | 1.0.0 |
| Description: | A schema-based validation framework for 'R' objects using user-defined rules. Provides three 'S7' classes 'Registry', 'Schema', and 'Validator' to manage rules, define list-based schemas, and validate data in a flexible and extensible manner. |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Suggests: | jsonlite, kableExtra, knitr, lobstr, readr, rmarkdown, stats, testthat (≥ 3.0.0), yaml |
| Config/testthat/edition: | 3 |
| Imports: | methods, S7, utils |
| URL: | https://lj-jenkins.github.io/fluffy/, https://github.com/LJ-Jenkins/fluffy |
| VignetteBuilder: | knitr |
| Config/roxygen2/version: | 8.0.0 |
| BugReports: | https://github.com/LJ-Jenkins/fluffy/issues |
| NeedsCompilation: | no |
| Packaged: | 2026-06-05 14:49:04 UTC; lukej |
| Author: | Luke Jenkins |
| Maintainer: | Luke Jenkins <luke-jenkins-dev@outlook.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-06-10 08:10:09 UTC |
fluffy: Schema-Based Validation of 'R' Objects with User-Defined Rules
Description
A schema-based validation framework for 'R' objects using user-defined rules. Provides three 'S7' classes 'Registry', 'Schema', and 'Validator' to manage rules, define list-based schemas, and validate data in a flexible and extensible manner.
Author(s)
Maintainer: Luke Jenkins luke-jenkins-dev@outlook.com (ORCID) [copyright holder]
Authors:
Luke Jenkins luke-jenkins-dev@outlook.com (ORCID) [copyright holder]
See Also
Useful links:
Report bugs at https://github.com/LJ-Jenkins/fluffy/issues
Registry
Description
Create a Registry object that defines and stores rules for validating
data against a Schema.
Usage
Registry()
is.Registry(x)
Arguments
x |
Object to be tested. |
Details
The Registry class serves as a central repository for all the rules used
in fluffy schema and data validation. It is passed to the Schema and
Validator classes, which use the rules stored in the Registry to
validate schemas and data, respectively.
To see the available builtin rules,
use the helper function show_builtins(), which lists all the builtin
rules in a Registry.
As Registry objects are automatically created in the Schema constructor,
which is in turn called in the Validator constructor, a Registry does not
need to be created separately to use fluffy's validation functionality.
Custom rules can also be added to the Registry within a Schema or
Validator object. However, rule addition will trigger re-validation of
the given object, so for the addition of many new rules, it is beneficial
to create a Registry object, add all the rules to it, and then pass it to
the other classes.
For full details see the vignettes on builtin rules, data validation, and adding custom rules.
Value
A Registry S7 object.
Additional properties
@rule_names(Read-only) All rules, in order of evaluation. Derived from the combination of the
control,transform,validate, andfinalizerules.@control_rulesRules that influence the control flow. These will be applied in the first pass when validating data.
@transform_rulesRules that transform data. These will be applied in the second pass when validating data.
@validate_rulesRules that validate data. These will be applied in the penultimate pass when validating.
@finalize_rulesRules that finalize validation. These will be applied in the last pass when validating, after all other rules have been applied. These rules will only apply if no previous rules for that schema node have failed.
@str_to_fn_rulesSchema rules that are allowed to have string or function values, with string values being converted to functions automatically during schema validation.
@str_to_fn_converterFunction that converts string values to functions for the
@str_to_fn_rules.@type_names(Read-only) Allowed type names. Derived from the keys of the
@type_map.@type_mapEnvironment mapping type names to type definition functions. Can only be added to via
add_type_rule().@coerce_names(Read-only) Allowed coercion names. Derived from the keys of the
@coerce_map.@coerce_mapEnvironment mapping coercion names to coercion functions. Can only be added to via
add_coerce_rule().@schema_rulesEnvironment mapping schema rule names to schema validation functions. Can only be added to via
add_rule().@cross_rule_names(Read-only) Schema cross rules. Derived from the keys of the
@cross_rulesenvironment.@cross_rulesEnvironment containing the schema cross rule functions (rules that check relationships between multiple schema rule values). Can only be added to via
add_cross_rule().@validator_rulesEnvironment mapping validator rule names to validator functions. Can only be added to via
add_rule().
See Also
Schema and Validator constructors. add_rule for adding
rules to a Registry.
Examples
r <- Registry()
s <- Schema(list(type = "integer"), registry = r)
is.Registry(r)
Schema
Description
Create a Schema object that defines and validates a schema for the future
validation of R objects using a Validator.
Usage
Schema(schema, registry = Registry(), error = FALSE, ...)
is.Schema(x)
Arguments
schema |
Non-empty list defining the schema. Each leaf element to be validated must be named with rules that are registered in the schema rule registry (see Registry and add_rule). Named elements must be unique at a given level of the list. |
registry |
Object of class Registry containing the schema and validator rules to use for schema validation. |
error |
Single logical value. If |
... |
Named arguments passed to internal error message formatting:
|
x |
Object to be tested. |
Details
The Schema class defines the structure and rules for validating data using
a Validator. It ingests the input schema list and then reorders and
validates the schema according to the Registry. The resulting @schema
property defines the validation behaviour and data matching.
Each element of the input list represents a field with named rules
(e.g., 'type', 'required', 'min_val') that are checked against the
schema rule registry. Cross rules (e.g., 'min_val_larger_than_max_val',
where 'min_val' < 'max_val') are also evaluated when all constituent
rules pass individually. See the Registry class for details.
The schema is re-evaluated upon any change to the schema properties,
with the @valid property indicating whether the schema is valid
(all rules pass) or invalid (any rule fails). If invalid, when passed
to a Validator the validation will fail immediately.
For full details see the vignettes on builtin rules, data validation, and adding custom rules.
Value
A Schema S7 object.
Additional properties
@schemaThe input schema list, with nodes reordered to match the order of the rules in the registry, and any strings converted to functions where applicable.
@errors(Read-only) A list mirroring the schema structure, with
NULLfor valid rules and character error messages for invalid rules.@RegistryAn object of class Registry containing the schema and validator rules used for validation.
@.schema_cache(Read-only) An environment for caching schema validation results. Used to avoid redundant validation.
@errorBoolean; whether to error on invalid schemas.
@error_print_optsA list of options for error message printing when
error = TRUE. These options are also used by theValidatorclass that ingests theSchema.Defaults are-
max_depth = 10L -
max_width = getOption("width") -
max_rows = 30L -
UTF8 = l10n_info()[["UTF-8"]]
-
@valid(Read-only) Boolean;
TRUEif schema is valid,FALSEotherwise.
See Also
add_rule for adding rules to a Registry. Validator for
validating data against a Schema.
Examples
# A valid schema
s <- Schema(list(
name = list(type = "character", required = TRUE),
age = list(type = "numeric", min_val = 0, max_val = 150)
))
s@valid # TRUE
# An invalid schema (type must be a string)
s <- Schema(list(name = list(type = 123)))
s@valid # FALSE
s@errors # error message
# To error on invalid schema
try(Schema(list(name = list(type = 123)), error = TRUE))
Validator
Description
Create a Validator object that validates/transforms R objects
using a Schema.
Usage
Validator(data, schema, error = FALSE)
is.Validator(x)
Arguments
data |
An R object. If using a nested schema, the object must have a |
schema |
Schema object or a non-empty |
error |
Single logical value. If |
x |
Object to be tested. |
Details
The Validator class validates/transforms data using a Schema. The
process checks each field in the schema against the data, applying the
rules specified. Any data element not present in the schema is ignored.
Validation is done in four passes, according to rule categories in
the Registry: control, transform, validate, and finalize.
Validation results are stored in the @errors property, and
the overall validity is indicated by the @valid property. If the
@error property/argument is set to TRUE, any validation failure will
result in an error being thrown. To customise the truncation of error
messages, see the Schema @error_print_opts property. The (possibly)
transformed data is stored in the @data property.
The Validator is re-evaluated upon any change to the object properties.
If an invalid schema is provided, the validation will fail immediately with
the validation error indicating that the schema is invalid. To see the
errors in the schema validation, see the Validator@Schema@errors property.
For full details see the vignettes on builtin rules, data validation, and adding custom rules.
Value
A Validator S7 object.
Additional properties
@dataThe validated input data, with any changes that were made during validation.
@SchemaAn object of class Schema containing the schema used for validation, as well as the Registry.
@errors(Read-only) A list mirroring the schema structure, with
NULLfor rules that pass and character error messages for rules that fail.@.validator_cache(Read-only) An environment for caching validation results. Used to avoid redundant validation.
@errorBoolean; whether to error upon failure (invalid data or schema).
@valid(Read-only) Boolean;
TRUEif the schema is valid and all data validation rules pass,FALSEotherwise.
See Also
Schema and Registry classes, and add_rule for adding custom rules.
Examples
v <- Validator(
data = list(name = "Alice", age = 30),
schema = list(
name = list(type = "character", required = TRUE),
age = list(type = "numeric", min_val = 0, max_val = 150)
)
)
v@valid # TRUE
# Schema object can be given directly
s <- Schema(list(a = list(type = "numeric"), b = list(type = "character")))
v <- Validator(list(a = "Hello", b = 42), s)
v@valid # FALSE
v@errors
# To error on invalid schema or data
try(Validator(list(a = "Hello", b = 42), s, error = TRUE))
# Invalid schemas show their errors
try(Validator(list(42), list(type = 123), error = TRUE))
# Transforms data according to rules
v <- Validator(1, list(coerce = "character"))
v@data # "1"
Add Rules to a Registry
Description
Add validator, schema, (schema) cross, type, or coerce rules to a Registry, Schema, or Validator object.
Usage
add_rule(
obj,
name,
validator_fn,
schema_fn = NULL,
rule_type = c("validate", "control", "transform", "finalize")
)
add_cross_rule(obj, name, rule_names, cross_fn)
add_type_rule(obj, type_name, type_fn)
add_coerce_rule(obj, coerce_name, coerce_fn)
Arguments
obj |
|
name |
String specifying the name of the rule to add. Rule names cannot be the same as any existing rule names. |
validator_fn |
Function that validates a data field against a schema field.
Every validator rule function must return
|
schema_fn |
Function to validate a schema field definition. If |
rule_type |
String specifying the type of rule to add. Must be one of
|
rule_names |
Character vector specifying the names of the existing schema
rules that the cross rule will operate over. Must be of
length 2 or more, and all names must be of existing schema rules
in the |
cross_fn |
Function to validate schema field definitions across multiple
schema rules. |
type_name, coerce_name |
String specifying the name of the type/coerce rule value to add. |
type_fn, coerce_fn |
Function to validate/coerce a field value. |
Details
To be able to correctly pass the required arguments for the data/schema validations, rule functions must have the following argument semantics:
type/coerce rule functions:
-
function(field)
schema/cross rule functions:
-
function(field, ...) -
function(field, .self, ...)|function(field, .schema, ...) -
function(field, .self, .schema)
validator rule functions:
-
function(field, schema_field, ...) -
function(field, schema_field, .self, ...)|function(field, schema_field, .data, ...) -
function(field, schema_field, .self, .data)
Note: field and schema_field are positional, and thus can be
named anything, whilst .self (the object itself: Schema for
schema rules, Validator for validator rules) and .schema/.data
(the full schema/data in the respective walks) are named arguments
and must be named as such.
Despite Registry using environments to store rules, which are mutable, add_rule methods copy the existing environment and the new rule into a new environment, meaning that the original object is not modified. See examples.
For full details see the vignettes on adding custom rules.
For more information on validating data with a Schema, see the
vignettes on
builtin rules and
data validation.
Value
The input object with the new rule added to the
associated Registry.
See Also
Registry, Schema, and Validator classes.
Examples
s <- Schema(list(check_my_attr = 1L))
s@errors # doesn't recognise rule
s <- add_rule(
obj = s,
name = "check_my_attr",
validator_fn = function(data_field, schema_field, ...) {
if (attr(data_field, "my_attr") != schema_field) {
list(error = "Data doesn't match schema 'my_attr'.")
}
},
schema_fn = function(schema_field, ...) {
if (!is.character(schema_field) || length(schema_field) != 1L) {
"Must be length 1 character"
}
},
rule_type = "validate"
)
# rule recognised and schema automatically re-validated
s@errors
# validation works with the new rule
s@schema$check_my_attr <- "Hi"
Validator(structure(1L, my_attr = "Hi"), s)@valid # TRUE
# schema cross rules invalidate when constituent rules are invalid
s <- add_cross_rule(
obj = s,
name = "min_and_max_val_add_to_10",
rule_names = c("min_val", "max_val"),
cross_fn = function(schema_field, ...) {
if (schema_field$min_val + schema_field$max_val == 10) {
"`min_val` and `max_val` cannot add to 10."
}
}
)
s@schema <- list(min_val = 2, max_val = 8)
s@errors # cross rule error
r <- Registry()
r <- add_type_rule(r, "my_type", function(x) {
inherits(x, "my_type")
})
s <- Schema(list(type = "my_type"), registry = r)
s@valid # TRUE
Validator(structure(1, class = "my_type"), s)@valid # TRUE
r <- add_coerce_rule(r, "my_type", function(x) {
structure(x, class = c("my_type", class(x)))
})
s <- Schema(list(coerce = "my_type"), registry = r)
v <- Validator(1, s)
class(v@data) # "my_type" "numeric"
# environments are copied, so original registry is not modified
r <- Registry()
r2 <- add_type_rule(r, "my_type", function(x) x)
identical(r@type_map, r2@type_map) # FALSE
Show Builtin Rules
Description
Helper to show the builtin rules in a Registry, and their behaviour.
Usage
show_builtins(rules = c("all", "validation", "cross"))
Arguments
rules |
Which builtin rules to show. |
Value
List of data.fames if rules = "all", otherwise a single data.frame,
with information on the builtin rules for the rule type specified.
The data.frame(s) have an attached class fluffy_rule_info.
Note
The fluffy_rule_info class has a custom print method that formats the
data.frame(s) in a more readable way. The output of this function is only
intended to be used for display/interactive purposes.
See Also
Examples
show_builtins()