---
title: "3. Browse for blocks"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{3. Browse for blocks}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(blockr.core)
```

## Introduction

The __registry__ is a __environment__ which provides access to multiple __blocks__ as well as some
metadata:

- The block __description__.
- The block __category__.
- The block __package__.
- ...

In other words, the __registry__ is a __"supermarket"__ for data analysis. As shown below, if you develop your own blocks package and registers blocks on load, these blocks become available to the end user. Therefore this makes it powerful for __collaboration__ between data science teams.

```{r, echo=FALSE}
include_mermaid("registry")
```

## Previewing available blocks

Upon loading, `{blockr}` __registers__ its internal __blocks__ with `register_core_blocks()`.
You won't have to call this function as it is not exported anyway. This makes the __registry__ environment
ready to be queried by `available_blocks()`. A truncated output example below:

```{r, eval=FALSE}
available_blocks()[["dataset_block"]]
```

```r
function(dataset = character(), package = "datasets", ...) {
  ...
}
<environment: namespace:blockr.core>
attr(,"name")
[1] "dataset block"
attr(,"description")
[1] "Choose a dataset from a package"
attr(,"classes")
[1] "dataset_block" "data_block"    "block"         "vctrs_vctr"    "list"         
attr(,"category")
[1] "input"
attr(,"ctor_name")
[1] "new_dataset_block"
attr(,"package")
[1] "blockr.core"
attr(,"class")
[1] "block_registry_entry"
```

```{r, eval=TRUE}
names(available_blocks())
```

## Register a block
To register your own blocks, user facing functions are:

- `register_block()` to register a block in the __registry__. If a block with
the same ID is already registered, it errors unless you pass `overwrite = TRUE`.
- `register_blocks()` to register multiple blocks.

Note that, if you develop your block outside a package context, you must call `register_block` (or `register_blocks`)
passing the constructor as a function and not as a string (see below).

Let's say you want to create a new `new_dummy_block` which does nothing specific in the `./R/dummy-block.R` script:

```{r, eval=TRUE}
# ./R/dummy-block.R
new_dummy_block <- function(text = "Hello World", ...) {
  new_data_block(
    function(id) {
      moduleServer(id, function(input, output, session) {
        list(
          expr = reactive(quote(text)),
          state = list(text = text)
        )
      })
    },
    function(id) {
      shiny::tagList()
    },
    class = "dummy_block",
    ...
  )
}

register_dummy_blocks <- function() {
  register_blocks(
    c(new_dummy_block),
    name = c("dummy block"),
    description = c("A block that does nothing"),
    overwrite = TRUE
  )
}

register_dummy_blocks()
```

Finally, we create a `.R/zzz.R` script where you run this code to register the block(s) whenever the package loads:

```r
# ./R/zzz.R
.onLoad <- function(libname, pkgname) {
  register_dummy_blocks()
  invisible(NULL)
}
```

Because `.onLoad()` runs on every load of the package (including each
`devtools::load_all()`, `document()` or `check()`), this registration must be
idempotent. That is why `register_dummy_blocks()` passes `overwrite = TRUE`:
without it, reloading the package aborts with `Block dummy_block already
exists`, forcing you to restart R between reloads.

For blocks that ship in a package, the recommended alternative removes the
hand-written registration altogether. Declaring the block metadata as `@block`
roxygen tags on the constructor, and adding
`blockr.core::block_registration_roclet` to the `Roxygen` field of the
`DESCRIPTION`, lets `devtools::document()` generate an `inst/registry/blocks.yml`
registry. A one-line `.onLoad()` then registers every block from it:

```r
# ./R/zzz.R
.onLoad <- function(libname, pkgname) {
  register_package_blocks()
  invisible(NULL)
}
```

`register_package_blocks()` defaults to `overwrite = TRUE`, so it is reload-safe
by construction. See `?block_registration_roclet` for the full tag vocabulary.

If we now query the registry, the new block is available:

```{r}
available_blocks()[["dummy_block"]]
```

## Unregister a block

The counterpart of `register_block()` is `unregister_blocks()`. We can remove
our new `dummy_block` from the registry:

```{r}
unregister_blocks(uid = "dummy_block")

# Check it out
names(available_blocks())
```

where __uid__ is/are the block(s) class(s) passed in the constructor.
