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.
# In exampleforms/R/survey.R:
survey_model <- function() {
form.model::select_form_model(
"forms/survey-form.yaml",
id = "survey", version = "2026.1",
package = "exampleforms"
)
}package names the installed package containing the YAML
resource.
The bundled example uses exactly the same installed-resource mechanism.
path <- form_resource("examples/survey-form.yaml", "form.model")
stopifnot(file.exists(path))
m <- select_form_model(path, "survey", "2026.1")
form_references(m)
#> [1] "observers" "stations"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.
form_fields(m, sheet = "Observations")
#> $Observations.station
#> $Observations.station$mandatory
#> [1] TRUE
#>
#> $Observations.station$type
#> [1] "string"
#>
#> $Observations.station$reference
#> $Observations.station$reference$id
#> [1] "stations"
#>
#> $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$unique
#> [1] TRUE
#>
#> $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$id
#> [1] "observers"
#>
#> $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
form_layout(m, id = "Observations")
#> $kind
#> [1] "table"
#>
#> $start_row
#> [1] 6
#>
#> $column_count
#> [1] 11
form_field(m, id = "Metadata.submission")[c("row", "column")]
#> $row
#> [1] 2
#>
#> $column
#> [1] 3
form_references(m)
#> [1] "observers" "stations"Fields 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.