A downstream package owns concrete models. Store each definition as
<name>-form.yaml under inst/forms/.
Use explicit subdirectories when shipping several versions of the same named model.
exampleforms/
DESCRIPTION
NAMESPACE
R/survey.R
inst/forms/survey-form.yaml
Declare Imports: form.model in DESCRIPTION.
Supply a documented wrapper with explicit resource selection.
# Expose the packaged survey definition through a domain-specific function.
survey_model <- function() {
# Select the packaged survey definition with its exact identity and version.
form.model::select_form_model(
"forms/survey-form.yaml",
id = "survey", version = "2026.2",
package = "exampleforms"
)
}package names the installed package containing the YAML
resource.
The bundled example uses exactly the same installed-resource mechanism.
# Locate the installed survey definition.
model_resource_path <- form_resource("examples/survey/form.yaml", "form.model")
# Confirm that the installed resource exists.
stopifnot(file.exists(model_resource_path))
# Load the definition with the exact survey identity and version.
model_definition <- select_form_model(model_resource_path, "survey", "2026.2")
# Inspect its catalogue and table requirements.
form_references(model_definition)
#> $code_lists
#> $code_lists[[1]]
#> [1] "observers"
#>
#> $code_lists[[2]]
#> [1] "stations"
#>
#>
#> $registries
#> $registries[[1]]
#> [1] "contacts"Selection reads only supplied paths, validates all of them, and requires exactly one matching identity tuple.
Supply an explicit list of files to support multiple versions. There is no directory scan or latest-version fallback.
Package-relative paths cannot escape the installed package, including through symbolic links.
# Inspect observation fields in workbook column order.
form_fields(model_definition, sheet = "Observations")
#> $Observations.station
#> $Observations.station$mandatory
#> [1] TRUE
#>
#> $Observations.station$type
#> [1] "string"
#>
#> $Observations.station$reference
#> $Observations.station$reference$catalogue
#> [1] "code_lists"
#>
#> $Observations.station$reference$table
#> [1] "stations"
#>
#> $Observations.station$reference$lookup
#> [1] "code"
#>
#> $Observations.station$reference$severity
#> [1] "error"
#>
#>
#> $Observations.station$id
#> [1] "Observations.station"
#>
#> $Observations.station$name
#> [1] "station"
#>
#> $Observations.station$sheet
#> [1] "Observations"
#>
#> $Observations.station$column
#> [1] 1
#>
#>
#> $Observations.count
#> $Observations.count$mandatory
#> [1] TRUE
#>
#> $Observations.count$type
#> [1] "integer"
#>
#> $Observations.count$minimum
#> [1] 0
#>
#> $Observations.count$maximum
#> [1] 1000
#>
#> $Observations.count$id
#> [1] "Observations.count"
#>
#> $Observations.count$name
#> [1] "count"
#>
#> $Observations.count$sheet
#> [1] "Observations"
#>
#> $Observations.count$column
#> [1] 2
#>
#>
#> $Observations.length
#> $Observations.length$type
#> [1] "number"
#>
#> $Observations.length$minimum
#> [1] 0
#>
#> $Observations.length$id
#> [1] "Observations.length"
#>
#> $Observations.length$name
#> [1] "length"
#>
#> $Observations.length$sheet
#> [1] "Observations"
#>
#> $Observations.length$mandatory
#> [1] FALSE
#>
#> $Observations.length$column
#> [1] 3
#>
#>
#> $Observations.unit
#> $Observations.unit$type
#> [1] "string"
#>
#> $Observations.unit$required_field
#> [1] "length"
#>
#> $Observations.unit$enum
#> $Observations.unit$enum[[1]]
#> [1] "cm"
#>
#> $Observations.unit$enum[[2]]
#> [1] "m"
#>
#>
#> $Observations.unit$id
#> [1] "Observations.unit"
#>
#> $Observations.unit$name
#> [1] "unit"
#>
#> $Observations.unit$sheet
#> [1] "Observations"
#>
#> $Observations.unit$mandatory
#> [1] FALSE
#>
#> $Observations.unit$column
#> [1] 4
#>
#>
#> $Observations.active
#> $Observations.active$mandatory
#> [1] TRUE
#>
#> $Observations.active$type
#> [1] "boolean"
#>
#> $Observations.active$id
#> [1] "Observations.active"
#>
#> $Observations.active$name
#> [1] "active"
#>
#> $Observations.active$sheet
#> [1] "Observations"
#>
#> $Observations.active$column
#> [1] 5
#>
#>
#> $Observations.observed_at
#> $Observations.observed_at$type
#> [1] "datetime"
#>
#> $Observations.observed_at$id
#> [1] "Observations.observed_at"
#>
#> $Observations.observed_at$name
#> [1] "observed_at"
#>
#> $Observations.observed_at$sheet
#> [1] "Observations"
#>
#> $Observations.observed_at$mandatory
#> [1] FALSE
#>
#> $Observations.observed_at$column
#> [1] 6
#>
#>
#> $Observations.latitude
#> $Observations.latitude$type
#> [1] "latitude"
#>
#> $Observations.latitude$id
#> [1] "Observations.latitude"
#>
#> $Observations.latitude$name
#> [1] "latitude"
#>
#> $Observations.latitude$sheet
#> [1] "Observations"
#>
#> $Observations.latitude$mandatory
#> [1] FALSE
#>
#> $Observations.latitude$column
#> [1] 7
#>
#>
#> $Observations.longitude
#> $Observations.longitude$type
#> [1] "longitude"
#>
#> $Observations.longitude$id
#> [1] "Observations.longitude"
#>
#> $Observations.longitude$name
#> [1] "longitude"
#>
#> $Observations.longitude$sheet
#> [1] "Observations"
#>
#> $Observations.longitude$mandatory
#> [1] FALSE
#>
#> $Observations.longitude$column
#> [1] 8
#>
#>
#> $Observations.observation_id
#> $Observations.observation_id$type
#> [1] "string"
#>
#> $Observations.observation_id$pattern
#> [1] "^OBS-[0-9]{3}$"
#>
#> $Observations.observation_id$id
#> [1] "Observations.observation_id"
#>
#> $Observations.observation_id$name
#> [1] "observation_id"
#>
#> $Observations.observation_id$sheet
#> [1] "Observations"
#>
#> $Observations.observation_id$mandatory
#> [1] FALSE
#>
#> $Observations.observation_id$column
#> [1] 9
#>
#>
#> $Observations.quality
#> $Observations.quality$type
#> [1] "integer"
#>
#> $Observations.quality$enum
#> $Observations.quality$enum[[1]]
#> [1] 1
#>
#> $Observations.quality$enum[[2]]
#> [1] 2
#>
#> $Observations.quality$enum[[3]]
#> [1] 3
#>
#>
#> $Observations.quality$id
#> [1] "Observations.quality"
#>
#> $Observations.quality$name
#> [1] "quality"
#>
#> $Observations.quality$sheet
#> [1] "Observations"
#>
#> $Observations.quality$mandatory
#> [1] FALSE
#>
#> $Observations.quality$column
#> [1] 10
#>
#>
#> $Observations.observer
#> $Observations.observer$type
#> [1] "string"
#>
#> $Observations.observer$reference
#> $Observations.observer$reference$catalogue
#> [1] "code_lists"
#>
#> $Observations.observer$reference$table
#> [1] "observers"
#>
#> $Observations.observer$reference$lookup
#> [1] "code"
#>
#> $Observations.observer$reference$severity
#> [1] "warning"
#>
#>
#> $Observations.observer$id
#> [1] "Observations.observer"
#>
#> $Observations.observer$name
#> [1] "observer"
#>
#> $Observations.observer$sheet
#> [1] "Observations"
#>
#> $Observations.observer$mandatory
#> [1] FALSE
#>
#> $Observations.observer$column
#> [1] 11
# Inspect the observation table layout.
form_layout(model_definition, id = "Observations")
#> $kind
#> [1] "table"
#>
#> $start_row
#> [1] 6
#>
#> $column_count
#> [1] 11
# Locate the submission identifier's row and column.
form_field(model_definition, id = "Metadata.submission")[c("row", "column")]
#> $row
#> [1] 2
#>
#> $column
#> [1] 3Fields retain declaration order; coordinates are one-based. Field IDs
use <sheet>.<field>.
Accessors expose declarations without executing constraints or loading reference data.
See ?form_fields, ?form_field,
?form_layout and ?form_references for
details.
A future validator accepts a model, submitted data, and caller-supplied reference snapshots.
A form package must not fetch snapshots, connect to a database, or write files during package loading.
Keep concrete domain definitions in the downstream package. Keep loader destinations and mappings in loader packages.
Run model checks in downstream tests for every shipped resource. Reserve validation equivalence tests for the full proof.