Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: nested routers initial pass #73

Merged
merged 7 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ docker/
^makefile$
^test\.html$
^\.vscode$
^\.lintr$
16 changes: 16 additions & 0 deletions .lintr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
linters: linters_with_defaults(
line_length_linter(120),
trailing_whitespace_linter = NULL,
commented_code_linter = NULL,
function_left_parentheses_linter = NULL,
spaces_left_parentheses_linter = NULL,
paren_body_linter = NULL,
brace_linter = NULL,
indentation_linter(
indent = 2L,
hanging_indent_style = "never"
),
object_usage_linter = NULL, # this uses eval()
object_name_linter = NULL
)
encoding: "UTF-8"
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ License: GPL (>= 3)
Encoding: UTF-8
LazyData: false
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.1
RoxygenNote: 7.3.2
Depends: R (>= 4.1.0)
Imports:
fs,
Expand Down
221 changes: 112 additions & 109 deletions R/ambiorix.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ Ambiorix <- R6::R6Class(
not_found = NULL,
error = NULL,
on_stop = NULL,
#' @details Define the webserver.
#'
#' @param host A string defining the host.
#' @param port Integer defining the port, defaults to `ambiorix.port` option: uses a random port if `NULL`.
#' @param log Whether to generate a log of events.
#' @details Define the webserver.
#'
#' @param host A string defining the host.
#' @param port Integer defining the port, defaults to `ambiorix.port` option: uses a random port if `NULL`.
#' @param log Whether to generate a log of events.
initialize = function(
host = getOption("ambiorix.host", "0.0.0.0"),
port = getOption("ambiorix.port", NULL),
Expand All @@ -68,91 +68,91 @@ Ambiorix <- R6::R6Class(
.globals$cache_tmpls <- TRUE
invisible(self)
},
#' @details Specifies the port to listen on.
#' @param port Port number.
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$listen(3000L)
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start()
#' @details Specifies the port to listen on.
#' @param port Port number.
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$listen(3000L)
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start()
listen = function(port){
assert_that(not_missing(port))
private$.port <- as.integer(port)
invisible(self)
},
#' @details Sets the 404 page.
#' @param handler Function that accepts the request and returns an object
#' describing an httpuv response, e.g.: [response()].
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$set_404(function(req, res){
#' res$send("Nothing found here")
#' })
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start()
#' @details Sets the 404 page.
#' @param handler Function that accepts the request and returns an object
#' describing an httpuv response, e.g.: [response()].
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$set_404(function(req, res){
#' res$send("Nothing found here")
#' })
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start()
set_404 = function(handler){
assert_that(not_missing(handler))
assert_that(is_handler(handler))
self$not_found <- handler
invisible(self)
},
#' @details Sets the error handler.
#' @param handler Function that accepts a request, response and an error object.
#'
#' @examples
#' # my custom error handler:
#' error_handler <- \(req, res, error) {
#' if (!is.null(error)) {
#' error_msg <- conditionMessage(error)
#' cli::cli_alert_danger("Error: {error_msg}")
#' }
#' response <- list(
#' code = 500L,
#' msg = "Uhhmmm... Looks like there's an error from our side :("
#' )
#' res$
#' set_status(500L)$
#' json(response)
#' }
#'
#' # handler for GET at /whoami:
#' whoami <- \(req, res) {
#' # simulate error (object 'Pikachu' is not defined)
#' print(Pikachu)
#' }
#'
#' app <- Ambiorix$
#' new()$
#' set_error(error_handler)$
#' get("/whoami", whoami)
#'
#' if (interactive()) {
#' app$start(open = FALSE)
#' }
#' @details Sets the error handler.
#' @param handler Function that accepts a request, response and an error object.
#'
#' @examples
#' # my custom error handler:
#' error_handler <- \(req, res, error) {
#' if (!is.null(error)) {
#' error_msg <- conditionMessage(error)
#' cli::cli_alert_danger("Error: {error_msg}")
#' }
#' response <- list(
#' code = 500L,
#' msg = "Uhhmmm... Looks like there's an error from our side :("
#' )
#' res$
#' set_status(500L)$
#' json(response)
#' }
#'
#' # handler for GET at /whoami:
#' whoami <- \(req, res) {
#' # simulate error (object 'Pikachu' is not defined)
#' print(Pikachu)
#' }
#'
#' app <- Ambiorix$
#' new()$
#' set_error(error_handler)$
#' get("/whoami", whoami)
#'
#' if (interactive()) {
#' app$start(open = FALSE)
#' }
set_error = function(handler) {
assert_that(not_missing(handler))
assert_that(is_error_handler(handler))
self$error <- handler
invisible(self)
},
#' @details Static directories
#'
#' @param path Local path to directory of assets.
#' @param uri URL path where the directory will be available.
#' @details Static directories
#'
#' @param path Local path to directory of assets.
#' @param uri URL path where the directory will be available.
static = function(path, uri = "www"){
assert_that(not_missing(uri))
assert_that(not_missing(path))
Expand All @@ -162,21 +162,21 @@ Ambiorix <- R6::R6Class(
private$.static <- append(private$.static, lst)
invisible(self)
},
#' @details Start
#' Start the webserver.
#' @param host A string defining the host.
#' @param port Integer defining the port, defaults to `ambiorix.port` option: uses a random port if `NULL`.
#' @param open Whether to open the app the browser.
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start(port = 3000L)
#' @details Start
#' Start the webserver.
#' @param host A string defining the host.
#' @param port Integer defining the port, defaults to `ambiorix.port` option: uses a random port if `NULL`.
#' @param open Whether to open the app the browser.
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start(port = 3000L)
start = function(
port = NULL,
host = NULL,
Expand All @@ -196,7 +196,10 @@ Ambiorix <- R6::R6Class(
if(is.null(host))
host <- private$.host

super$reorder_routes()
super$prepare()
private$.routes <- super$get_routes()
private$.receivers <- super$get_receivers()
private$.middleware <- super$get_middleware()

private$.server <- httpuv::startServer(
host = host,
Expand Down Expand Up @@ -259,30 +262,30 @@ Ambiorix <- R6::R6Class(

invisible(self)
},
#' @details Define Serialiser
#' @param handler Function to use to serialise.
#' This function should accept two arguments: the object to serialise and `...`.
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$serialiser(function(data, ...){
#' jsonlite::toJSON(x, ..., pretty = TRUE)
#' })
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start()
#' @details Define Serialiser
#' @param handler Function to use to serialise.
#' This function should accept two arguments: the object to serialise and `...`.
#'
#' @examples
#' app <- Ambiorix$new()
#'
#' app$serialiser(function(data, ...){
#' jsonlite::toJSON(x, ..., pretty = TRUE)
#' })
#'
#' app$get("/", function(req, res){
#' res$send("Using {ambiorix}!")
#' })
#'
#' if(interactive())
#' app$start()
serialiser = function(handler){
assert_that(is_function(handler))
options(AMBIORIX_SERIALISER = handler)
invisible(self)
},
#' @details Stop
#' Stop the webserver.
#' @details Stop
#' Stop the webserver.
stop = function(){

if(!private$.is_running){
Expand All @@ -301,7 +304,7 @@ Ambiorix <- R6::R6Class(

invisible(self)
},
#' @details Print
#' @details Print
print = function(){
cli::cli_rule("Ambiorix", right = "web server")
cli::cli_li("routes: {.val {private$n_routes()}}")
Expand Down
4 changes: 2 additions & 2 deletions R/log.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ new_log <- function(
file = file,
sep = sep
)$
date()$
time()
date()$
time()
}

#' Customise logs
Expand Down
39 changes: 4 additions & 35 deletions R/request.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#' @field HTTP_SEC_FETCH_USER Only sent for requests initiated by user activation, and its value will always be `?1`.
#' @field HTTP_UPGRADE_INSECURE_REQUESTS Signals that server supports upgrade.
#' @field HTTP_USER_AGENT User agent.
#' @field SERVER_NAME Name of the server.
#' @field httpuv.version Version of httpuv.
#' @field PATH_INFO Path of the request.
#' @field QUERY_STRING Query string of the request.
Expand All @@ -26,8 +27,7 @@
#' @field rook.input Rook inputs.
#' @field rook.url_scheme Rook url scheme.
#' @field rook.version Rook version.
#' @field SCRIPT_NAME The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location".
#' @field SERVER_NAME Server name.
#' @field SCRIPT_NAME The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". #' @field SERVER_NAME Server name.
#' @field SERVER_PORT Server port
#' @field CONTENT_LENGTH Size of the message body.
#' @field CONTENT_TYPE Type of content of the request.
Expand Down Expand Up @@ -160,37 +160,6 @@ Request <- R6::R6Class(

cli::cli_end()
},
#' @details Set Data
#' @param name Name of the variable.
#' @param value Value of the variable.
#' @return Invisible returns self.
set = function(name, value){
assert_that(not_missing(name))
assert_that(not_missing(value))
.Deprecated(
"",
package = "ambiorix",
"Deprecated. The environment is no longer locked, you may simply `res$name <- value`"
)

name <- as_label(name)
self[[name]] <- value

invisible(self)
},
#' @details Get data
#' @param name Name of the variable to get.
get = function(name){
assert_that(not_missing(name))
.Deprecated(
"",
package = "ambiorix",
"Deprecated. The environment is no longer locked, you may simply `res$value"
)

name <- as_label(name)
self[[name]]
},
#' @details Get Header
#' @param name Name of the header
get_header = function(name){
Expand Down Expand Up @@ -256,7 +225,7 @@ set_params <- function(path, route = NULL){

nms <- c()
pms <- list()
for(i in 1:length(path_split)){
for(i in seq_along(path_split)){
if(route$components[[i]]$dynamic){
nms <- c(nms, route$components[[i]]$name)
pms <- append(pms, utils::URLdecode(path_split[i]))
Expand Down Expand Up @@ -332,4 +301,4 @@ mockRequest <- function(
)

Request$new(req)
}
}
Loading
Loading