|
| 1 | +--- |
| 2 | +applyTo: "**/R/*.R" |
| 3 | +--- |
| 4 | + |
| 5 | +# R Instructions |
| 6 | + |
| 7 | +## 1) Core Basics |
| 8 | + |
| 9 | +- **Main entry point (name matters):** |
| 10 | + - The R function name **must match** the case-sensitive `"function"` field in `Description.qml`. |
| 11 | + - Signature is always: |
| 12 | + ```r |
| 13 | + AnalysisName <- function(jaspResults, dataset, options) { ... } |
| 14 | + ``` |
| 15 | + - `jaspResults` is a container that stores all of the analysis output and byproducts (if they are supposed to be kept for later use). |
| 16 | + - `dataset` is the loaded dataset in JASP |
| 17 | + - `options` are the UI choices from QML; **do not rename** option keys (they’re your API). |
| 18 | + |
| 19 | +- **Recommended structure (3 roles):** |
| 20 | + 1) **Main function** orchestrates and wires output elements. |
| 21 | + 2) **create* functions** declare output markup (tables/plots/text). |
| 22 | + 3) **fill* (or compute*) functions** compute results and fill outputs. |
| 23 | + |
| 24 | +- **Dependencies (cache & reuse):** |
| 25 | + Add `$dependOn()` to every output (table/plot/text/container/state) so JASP knows when to reuse or drop it. |
| 26 | + Outputs nested within containers inherit all dependencies from the container. |
| 27 | + |
| 28 | +- **Errors:** |
| 29 | + - Catch run-time errors with `try(...)` and report via `$setError()`. |
| 30 | + - Wrap user-visible text with `gettext()` / `gettextf()` for translation. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## 2) Input Validation |
| 35 | + |
| 36 | +Only validate the `dataset`. `options` input is validated in the QML automatically. |
| 37 | + |
| 38 | +Common checks (prefix arguments with the check name): |
| 39 | +```r |
| 40 | +.hasErrors( |
| 41 | + dataset, type = c("factorLevels", "observations", "variance", "infinity", "missingValues"), |
| 42 | + factorLevels.target = options$variables, |
| 43 | + factorLevels.amount = "< 1", |
| 44 | + observations.target = options$variables, |
| 45 | + observations.amount = "< 1" |
| 46 | +) |
| 47 | +``` |
| 48 | +Other useful checks: |
| 49 | +- `limits.min/max` (inclusive bounds), |
| 50 | +- `varCovData.target/corFun` (positive-definiteness), |
| 51 | +- `modelInteractions` (ensure lower-order terms exist). |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +## 3) Output Components |
| 56 | + |
| 57 | +### Tables — `createJaspTable()` |
| 58 | +**Key methods/properties:** |
| 59 | +- `$dependOn(<optionNames>)` |
| 60 | +- `$addCitation("<text>")` |
| 61 | +- `$addColumnInfo(name, title, type = "string|number|integer|pvalue", format = "sf:4;dp:3", combine = FALSE, overtitle = NULL)` |
| 62 | +- `$showSpecifiedColumnsOnly <- TRUE` (hide unspecified stats you happen to compute) |
| 63 | +- `$setExpectedSize(nRows)` (for long computations) |
| 64 | +- `$addFootnote(message, colNames = NULL, rowNames = NULL)` |
| 65 | +- `$addRows(list(...))` or `$setData(df)` |
| 66 | +- `$setError("<message>")` |
| 67 | + |
| 68 | +**Skeleton:** |
| 69 | +```r |
| 70 | +.createMyTable <- function(jaspResults, dataset, options, ready) { |
| 71 | + if (!is.null(jaspResults[["mainTable"]])) return() |
| 72 | + tab <- createJaspTable(title = gettext("My Table")) |
| 73 | + tab$dependOn(c("variables", "alpha", "showCI")) |
| 74 | + tab$addColumnInfo("variable", gettext("Variable"), "string", combine = TRUE) |
| 75 | + tab$addColumnInfo("estimate", gettext("Estimate"), "number") |
| 76 | + if (options$showCI) { |
| 77 | + over <- gettextf("%f%% CI", 100 * options[["alpha"]]) |
| 78 | + tab$addColumnInfo("lcl", gettext("Lower"), "number", overtitle = over) |
| 79 | + tab$addColumnInfo("ucl", gettext("Upper"), "number", overtitle = over) |
| 80 | + } |
| 81 | + tab$showSpecifiedColumnsOnly <- TRUE |
| 82 | + jaspResults[["mainTable"]] <- tab |
| 83 | + if (!ready) return() |
| 84 | + .fillMyTable(tab, dataset, options) |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +### Plots — `createJaspPlot()` |
| 89 | +**Key methods/properties:** |
| 90 | +- `$dependOn(<optionNames>)`, `$addCitation()` |
| 91 | +- Set `plotObject <- ggplot2::ggplot(...)` |
| 92 | +- `$setError("<message>")` |
| 93 | + |
| 94 | +**Skeleton:** |
| 95 | +```r |
| 96 | +.createMyPlot <- function(jaspResults, dataset, options, ready) { |
| 97 | + if (!is.null(jaspResults[["descPlot"]])) return() |
| 98 | + plt <- createJaspPlot(title = gettext("My Plot"), width = 400, height = 300) |
| 99 | + plt$dependOn(c("variables", "alpha")) |
| 100 | + jaspResults[["descPlot"]] <- plt |
| 101 | + if (!ready) return() |
| 102 | + .fillMyPlot(plt, dataset, options) |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +### Text blocks — `createJaspHtml()` |
| 107 | +Display formatted messages; can depend on options like other outputs. |
| 108 | +```r |
| 109 | +if (!is.null(jaspResults[["note"]])) return() |
| 110 | +msg <- createJaspHtml(text = gettextf("The variable <b>%s</b> was omitted.", options[["variable"]])) |
| 111 | +msg$dependOn(c("variable")) |
| 112 | +jaspResults[["note"]] <- msg |
| 113 | +``` |
| 114 | + |
| 115 | +### Containers — `createJaspContainer()` |
| 116 | +Group related outputs; container dependencies propagate to children. Useful for “one-per-variable” sections. |
| 117 | +- `$dependOn(...)`, `$setError("<message>")`, `$getError()` |
| 118 | +- Nest containers freely. |
| 119 | + |
| 120 | +```r |
| 121 | +if (is.null(jaspResults[["descGroup"]])) { |
| 122 | + grp <- createJaspContainer(title = gettext("Descriptive Plots")) |
| 123 | + grp$dependOn(c("variables", "alpha")) |
| 124 | + jaspResults[["descGroup"]] <- grp |
| 125 | +} else { |
| 126 | + grp <- jaspResults[["descGroup"]] |
| 127 | +} |
| 128 | +for (v in options[["variables"]]) { |
| 129 | + if (!is.null(grp[[v]])) next |
| 130 | + p <- createJaspPlot(title = v, width = 480, height = 320) |
| 131 | + p$dependOn(optionContainsValue = list(variables = v)) |
| 132 | + grp[[v]] <- p |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +### State (cache) — `createJaspState()` |
| 137 | +Cache computed results across reruns (while dependencies hold). |
| 138 | +- `$dependOn(...)` |
| 139 | +- `$object <- results` (store) / `results <- state$object` (retrieve) |
| 140 | + |
| 141 | +```r |
| 142 | +.stateCompute <- function(jaspResults, dataset, options) { |
| 143 | + st <- createJaspState() |
| 144 | + st$dependOn(c("variables", "alpha")) |
| 145 | + jaspResults[["internalResults"]] <- st |
| 146 | + res <- colMeans(dataset[options[["variables"]]], na.rm = TRUE) |
| 147 | + st$object <- res |
| 148 | +} |
| 149 | +``` |
| 150 | + |
| 151 | +--- |
| 152 | + |
| 153 | +## 4) Style & Conventions |
| 154 | + |
| 155 | +- **Follow the project R style guide.** Keep functions short; prefer pure helpers; avoid global state; no I/O or printing in analyses. |
| 156 | +- **Naming:** |
| 157 | + - Helpers start with a dot, e.g., `.computeFoo()`, `.fillBarTable()`, `.plotBaz()`. |
| 158 | + - Stable keys in `jaspResults[["..."]]` (don’t rename them later). |
| 159 | +- **Internationalization:** All visible text via `gettext()`/`gettextf()`. |
| 160 | +- **Performance:** Read only needed columns; postpone decoding; reuse `createJaspState()` when multiple outputs share results. |
| 161 | +- **Robustness:** Validate early; guard long loops with `if (!ready) return()`; wrap risky code in `try()` and call `$setError()`. |
| 162 | +- **Reproducibility:** Set column formats explicitly in tables; document assumptions in footnotes/citations. |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | +## 5) Minimal main() template (copy/paste) |
| 167 | + |
| 168 | +```r |
| 169 | +MyAnalysis <- function(jaspResults, dataset, options) { |
| 170 | + |
| 171 | + ready <- length(options[["variables"]]) > 0 |
| 172 | + |
| 173 | + .createMyTable(jaspResults, dataset, options, ready) |
| 174 | + .createMyPlot(jaspResults, dataset, options, ready) |
| 175 | +} |
0 commit comments