Extending Boards

The GitHub, Kaggle, RStudio Connect and Website boards provide access to popular Data Science storage services; however, there can be many other interesting services to store resources through pins. For instance, Amazon S3, Amazon Glacier, Azure Blobs, Google File System and Hadoop, just to name a few. While those are not natively implemented in the pins package, you can create packages that extend pins to support many other services through custom boards.

For the purpose of creating custom boards, it helps to understand a board as a collection of files stored with a given name. It closely resembles a simple file systems, but it only requires the following operations to be implemented: create, get, find and remove. For read-only storage locations, you can also decide to implement only the ‘get’ and ‘find’ operations.

The most important S3 methods that your board needs to support are board_pin_create(), board_pin_get(), board_pin_find() and board_pin_remove() which define how to create, get, find and remove a pin.

The next S3 method to consider implementing is board_initialize() which is optional to implement but provides support for initialization, this method usually deals with authentication.

The last function to consider implementing is board_browse(), this function is used to browse to the location of the board. Usually implemented by calling browseURL() to open to website of the service hosting the custom board.

To make this a bit more clear, the following section implements a custom board based on folders.

Example

The following example creates a simple folder-based board, which stores pins in local folders, this is similar to the default ‘local’ board. It is advisable to create an R package that provides these methods, but you can also define them in a plain R script:

board_initialize.folder <- function(board, ...) {
  if (!dir.exists("pins")) dir.create("pins")
  board
}

board_pin_create.folder <- function(board, path, name, ...) {
  dir.create(file.path("pins", name), recursive = TRUE, showWarnings = FALSE)
  file.copy(dir(path, full.names = TRUE), file.path("pins", name), recursive = TRUE)
}

board_pin_get.folder <- function(board, name, ...) {
  file.path("pins", name)
}

board_pin_find.folder <- function(board, text, ...) {
  data.frame(name = dir("pins", text), stringsAsFactors = F)
}

board_pin_remove.folder <- function(board, name, ...) {
  unlink(file.path("pins", name), recursive = TRUE)
}

You can optionally support data versions in your board to enable pin_versions() and pin_get("name", version = "version") by extending board_pin_versions:

board_pin_versions.folder <- function(board, name, ...) {
  data.frame( 
     version = 1
  )
}

board_pin_get.folder <- function(board, name, version = NULL, ...) {
  if (!is.null(version)) message("Version ", version, " requested!")
  file.path("pins", name)
}

You can then use this folder board like any other board:

library(pins)
board_register("folder")

pin(iris, board = "folder")
pin_get("iris", board = "folder")
## # A tibble: 150 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>  
##  1          5.1         3.5          1.4         0.2 setosa 
##  2          4.9         3            1.4         0.2 setosa 
##  3          4.7         3.2          1.3         0.2 setosa 
##  4          4.6         3.1          1.5         0.2 setosa 
##  5          5           3.6          1.4         0.2 setosa 
##  6          5.4         3.9          1.7         0.4 setosa 
##  7          4.6         3.4          1.4         0.3 setosa 
##  8          5           3.4          1.5         0.2 setosa 
##  9          4.4         2.9          1.4         0.2 setosa 
## 10          4.9         3.1          1.5         0.1 setosa 
## # … with 140 more rows
# A tibble: 150 x 5
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          <dbl>       <dbl>        <dbl>       <dbl> <fct>  
 1          5.1         3.5          1.4         0.2 setosa 
 2          4.9         3            1.4         0.2 setosa 
 3          4.7         3.2          1.3         0.2 setosa 
 4          4.6         3.1          1.5         0.2 setosa 
 5          5           3.6          1.4         0.2 setosa 
 6          5.4         3.9          1.7         0.4 setosa 
 7          4.6         3.4          1.4         0.3 setosa 
 8          5           3.4          1.5         0.2 setosa 
 9          4.4         2.9          1.4         0.2 setosa 
10          4.9         3.1          1.5         0.1 setosa 
# … with 140 more rows

To extend pins in a separate package, make sure to export the s3 generics from pins. For example:

#' @importFrom pins board_initialize
#' @export
pins::board_initialize

#' @importFrom pins board_pin_create
#' @export
pins::board_pin_create

#' @importFrom pins board_pin_get
#' @export
pins::board_pin_get

#' @importFrom pins board_pin_find
#' @export
pins::board_pin_find

#' @importFrom pins board_pin_remove
#' @export
pins::board_pin_remove

#' @importFrom pins board_pin_versions
#' @export
pins::board_pin_versions