feat: define measures from documented R scripts - #3
Conversation
Derive measures from documented functions in R scripts. Every top-level function with a roxygen2 block becomes a measure: name from the function, description from @title/@description/@return, and arguments from @PARAM. Argument types are declared with a leading type code span, e.g. `enum[day, week, month]` or `string[]`; required is taken from the signature and untyped params are inferred from their defaults.
Add a 'Defining measures' section to the README showing the roxygen2 tag syntax for read_measures(), and add @Seealso cross-links between measure(), semantic_layer(), and read_measures().
Character inputs to semantic_layer() are now passed to read_measures(),
so file paths, directories, and inline measure() objects can be freely
mixed: semantic_layer("measures.R", my_inline_measure). Paths may be
scalar strings or character vectors.
read_measures() now treats @measure as a required opt-in marker: only functions whose roxygen2 block carries @measure become measures, so helper functions can live alongside measures in the same file. This replaces the old "every documented function is a measure" rule and mirrors how @export marks a function. All files passed in a single read_measures() call are sourced in order into one shared environment (parent = globalenv()), so a measure can call helpers defined in sibling files of the same call. Separate arguments to semantic_layer() remain isolated. The @measure tag is registered at runtime via a roxy_tag_parse S3 method (tag_toggle), since roxygen2 is only a suggested dependency.
Bumps the recorded roxygen2 version to 8.0.0 and regenerates the package-level and module .Rd files accordingly.
|
I like that how this makes the |
simonpcouch
left a comment
There was a problem hiding this comment.
I'm game, this is better! Let's do this!
I removed the new section in the README, which is otherwise a bit less in-the-weeds than that content. I also unexported read_measures(); I can see us providing this in the future, but I think there's still an intermediate abstraction of a "measure set" or "domain" (that includes all pieces, including some set of measures) or something that we'll need to figure out in the meantime.
| semantic layer. This layer informs how the agent will author fallback | ||
| SQL queries. | ||
|
|
||
| ### Defining measures |
There was a problem hiding this comment.
This is a lot of extra content for the README
| Encoding: UTF-8 | ||
| Roxygen: list(markdown = TRUE) | ||
| RoxygenNote: 7.3.3 | ||
| Config/roxygen2/version: 8.0.0 |
There was a problem hiding this comment.
lol i am behind the times
| #' @param ... [measure()] objects, lists of measures, or paths to R scripts or | ||
| #' directories. Paths are passed to [read_measures()], so file and inline | ||
| #' measures can be freely mixed. |
There was a problem hiding this comment.
GARRICK SHAPED MAGIC YEAHH
Summary
Adds
read_measures(), which derivesmeasure()definitions from ordinary documented R functions, so a data science team can author governed measures as plain R scripts instead of hand-assemblingmeasure()calls.A function becomes a measure when its roxygen2 block carries a
@measuretag — much like@exportmarks a function as part of a package's public interface. Its name comes from the function, its description from@title/@description/@return, and its arguments from the@paramtags. Other documented functions in the file are ignored, so helper functions can live alongside measures. Argument types are declared with a leading code span in the@paramtext —string,integer,number,boolean,enum[...], ortype[]for arrays. An argument is required when its formal has no default; untyped arguments are inferred from their defaults, falling back to string. The code-span syntax was chosen over a bare sigil so the type renders correctly in the generated.Rdrather than being mangled by the markdown roclet into a broken\link{}.All files passed in a single
read_measures()call are sourced in order into one shared environment, so a measure can call helper functions defined in a sibling file of the same call.semantic_layer()now accepts character inputs directly and routes them throughread_measures(), so file paths, directories, and inlinemeasure()objects can be freely mixed in one call (e.g.semantic_layer("measures.R", my_inline_measure)). The single-list form continues to work. Separate path arguments stay isolated —semantic_layer("a.R", "b.R")reads each file in its own environment, whilesemantic_layer(c("a.R", "b.R"))shares one. One behavior change: a non-path string passed tosemantic_layer()now errors fromread_measures()("Path does not exist") rather than the previous "must be created bymeasure()".roxygen2is added to Suggests, guarded byrlang::check_installed()so it is only required when reading measures from scripts. The@measuretag is registered at runtime via aroxy_tag_parseS3 method (tag_toggle), since roxygen2 is only a suggested dependency.This branch also adopts roxygen2 8.0.0:
DESCRIPTIONrecordsConfig/roxygen2/version: 8.0.0and the package-level man pages are regenerated to match.Verification
Full test suite passes (
devtools::test(), 163 tests), including new coverage for code-span parsing, required-from-signature, default inference, directory and vector inputs, path routing throughsemantic_layer(), the@measureopt-in (untagged functions ignored), cross-file helpers via a shared environment, and isolation across separate path arguments.