{"id":18174391,"url":"https://github.com/cynkra/constructive.example","last_synced_at":"2026-02-13T14:45:28.002Z","repository":{"id":170760705,"uuid":"646995092","full_name":"cynkra/constructive.example","owner":"cynkra","description":"Example of a package defining a class and methods for 'constructive'","archived":false,"fork":false,"pushed_at":"2024-12-31T13:21:56.000Z","size":35,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-16T01:54:21.748Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cynkra.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-29T20:34:43.000Z","updated_at":"2025-02-27T20:05:47.000Z","dependencies_parsed_at":"2024-07-12T11:13:52.518Z","dependency_job_id":null,"html_url":"https://github.com/cynkra/constructive.example","commit_stats":null,"previous_names":["cynkra/constructive.example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cynkra%2Fconstructive.example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cynkra%2Fconstructive.example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cynkra%2Fconstructive.example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cynkra%2Fconstructive.example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cynkra","download_url":"https://codeload.github.com/cynkra/constructive.example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244679476,"owners_count":20492424,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-02T16:03:05.567Z","updated_at":"2026-02-13T14:45:22.961Z","avatar_url":"https://github.com/cynkra.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n# constructive.example\n\nThis package serves as an example on how to extend {constructive}. \nFor some users it will be enough to call \n`constructive::.cstr_new_class()` and `constructive::.cstr_new_constructor()`\nwith `commented = TRUE` and figure it out from there. This is a more detailed\nwalkthrough. Follow hyperlinks to inspect relevant commits.\n\n## Implement a constructor for a new class\n\nLet's add support for a new class : \"qr\". This is the class of the object we get\nafter applying `base::qr()` to get the QR decomposition of a matrix.\n\nThe reconstruction of qr objects is not perfect due to rounding errors so the \nfeature is not a good fit for {constructive} but is a good use case for an example.\n\nFirst note that {constructive} works even if it doesn't support directly a class,\nit will then use the next relevant constructor:\n\n```{r, error = TRUE}\nlibrary(constructive)\n\n# will work but use the list constructor\nA \u003c- matrix(1:6, nrow = 3)\nqr_A \u003c- qr(A)\nconstruct(qr_A)\n```\n\nWe used the following workflow:\n\n* Call `usethis::use_package(\"constructive\", \"Suggests\")` to \n  [add a soft dependency on the 'constructive' package](https://github.com/cynkra/constructive.example/commit/e5512b8a4308364fa87ccf85a81e7e036638fabc) \n  if not already done.\n* Call `constructive::.cstr_new_class(class = \"qr\", constructor = \"qr\", commented = TRUE)` and \n  [save the template script](https://github.com/cynkra/constructive.example/commit/91063555978561cbc3201ab2685ac4c7b34cfc5d) \n  in your \"R\" folder.\n* Call `devtools::document()` to \n  [register the methods and export and document `opt_qr()`](https://github.com/cynkra/constructive.example/commit/f2b058261a368a7c88dc7f3be7e6f3f20bfd11cb)\n* restart, `devtools::load_all()`, define `qr_A`\n  \nBy this time we can already use our new constructor, it doesn't quite work yet though:\n\n```{r, eval = FALSE}\nconstructive::construct(qr_A)\n#\u003e ! The code built by {constructive} could not be evaluated.\n#\u003e ! Due to error: argument \"x\" is missing, with no default\n#\u003e qr()\n```\n  \nThis is because we haven't construct the argument to `qr()`!\n\nLet's fix this:\n\n* [Construct the argument to the constructor](https://github.com/cynkra/constructive.example/commit/27a531f2155abc56f2f22493c45863602c4f9b04). \n  The `qr.X()` function does the inverse transformation so the input we want to construct is `qr.X(x)`.\n* restart, `devtools::load_all()`, define `qr_A`\n\nAfter this we have:\n\n```{r, error = TRUE, eval = FALSE}\nlibrary(constructive)\nconstruct(qr_A)\n#\u003e {constructive} couldn't create code that reproduces perfectly the input\n#\u003e ℹ Call `construct_issues()` to inspect the last issues\n#\u003e \n#\u003e qr(\n#\u003e   matrix(\n#\u003e     c(0.99999999999999967, 2, 3, 4.000000000000002, 5.000000000000001, 6.000000000000001),\n#\u003e     nrow = 3L,\n#\u003e     ncol = 2L\n#\u003e   )\n#\u003e )\n```\n\nThe recreation was not perfect due to rounding errors but we're pretty close\n\n```{r, error = TRUE}\nconstruct_issues()\n```\n\nWe still get the previous behavior if we use the `\"next\"` constructor.\n\n```{r, eval = FALSE}\n# fall back on the next method, which is for lists\nconstruct(qr_A, opts_qr(\"next\"))\n#\u003e list(\n#\u003e   qr = matrix(\n#\u003e     c(\n#\u003e       -0x1.deeea11683f49p+1, 0.5345224838248488, 0.8017837257372732,\n#\u003e       -8.55235974119758, 1.9639610121239324, 0x1.fa35f928a0dfap-1\n#\u003e     ),\n#\u003e     nrow = 3L,\n#\u003e     ncol = 2L\n#\u003e   ),\n#\u003e   rank = 2L,\n#\u003e   qraux = c(1.26726124191242429, 1.1499536117281517),\n#\u003e   pivot = 1:2\n#\u003e ) |\u003e\n#\u003e   structure(class = \"qr\")\n```\n\nWe still fail on corrupted objects though:\n\n```{r, eval = FALSE}\ncorrupted \u003c- structure(1:3, class = \"qr\")\nconstruct(corrupted)\n#\u003e Error in `construct()`:\n#\u003e ! {constructive} could not build the requested code.\n#\u003e Caused by error in `qr.X()`:\n#\u003e ! argument is not a QR decomposition\n#\u003e Run `rlang::last_trace()` to see where the error occurred.\n```\n\nLet's fix this:\n\n* [Implement `is_corrupted_qr()`](https://github.com/cynkra/constructive.example/commit/5737217204dcad93257cb1b61915d242cd6275f3)\n* `devtools::load_all()`\n\nNow we won't fail on corrupted objects\nbut fall back to the next method.\n\n```{r, eval = FALSE}\ncorrupted \u003c- structure(1:3, class = \"qr\")\nconstruct(corrupted)\n#\u003e 1:3 |\u003e\n#\u003e   structure(class = \"qr\")\n```\n\n## Implement a new constructor for a supported class\n\nWe build a new constructor for the class \"tbl_df\" (tibbles), using the deprecated\nconstructor `tibble::data_frame()`, equivalent to `tibble::tibble()`. \n\nWe used the following workflow:\n\n* Call `usethis::use_package(\"constructive\", \"Suggests\")` \n  [to add a soft dependency on the 'constructive' package](https://github.com/cynkra/constructive.example/commit/e5512b8a4308364fa87ccf85a81e7e036638fabc) \n  if not already done (we did it already for the qr class)\n* Call `constructive::.cstr_new_constructor(class = c(\"tbl_df\", \"tbl\", \"data.frame\"), constructor = \"tibble::data_frame\", commented = TRUE)` and \n  [save the template script](https://github.com/cynkra/constructive.example/commit/caf387ae9bd5a46c18008f381264a61ec89d6626) \n  in your \"R\" folder\n* Call `devtools::document()` to [register the method](https://github.com/cynkra/constructive.example/commit/2f1f5022c7d666074b3a5f91e8dc3bdac1770d9b)\n* Call `devtools::load_all()`\n\nBy this time we can already use our new constructor, it doesn't quite work yet though:\n\n```{r, eval = FALSE}\nconstruct(dplyr::band_members, opts_tbl_df(\"data_frame\"))\n#\u003e {constructive} couldn't create code that reproduces perfectly the input\n#\u003e ℹ Call `construct_issues()` to inspect the last issues\n#\u003e \n#\u003e tibble::data_frame() |\u003e\n#\u003e   structure(row.names = c(NA, -3L))\n```\n\nWe see that we don't construct yet the arguments to data_frame and we repair\nthe `row.names` attribute that the constructor should take care of. Let's fix this:\n\n* [Construct arguments to the constructor](https://github.com/cynkra/constructive.example/commit/8d474206d44250b08bb28c039917e96a31da38c1). The arguments are\n  are the columns of the data frame, so here it's\n  as simple as `args \u003c- as.list.data.frame(x)`. We prefered `as.list.data.frame()`\n  to `as.list()` because we don't control the S3 dispatch and some corner cases\n  might break the package.\n* [Ignore attributes built by the constructor](https://github.com/cynkra/constructive.example/commit/265a72ac3ed356ddd5b4a09c8f0361b62a4f3096)\n* Call `devtools::load_all()`\n\nAfter this we have:\n\n```{r, eval = FALSE}\nconstruct(dplyr::band_members, opts_tbl_df(\"data_frame\"))\n#\u003e tibble::data_frame(name = c(\"Mick\", \"John\", \"Paul\"), band = c(\"Stones\", \"Beatles\", \"Beatles\"))\n```\n\n[our implementation of the \"tibble\" constructor in {constructive}](https://github.com/cynkra/constructive/blob/86d8d3d47081e17a691cde1188c838c80b072e56/R/s3-tbl_df.R#L54)\nis very similar but handles some corner cases that don't apply to `data_frame()`. \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcynkra%2Fconstructive.example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcynkra%2Fconstructive.example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcynkra%2Fconstructive.example/lists"}