Skip to content

Commit

Permalink
Merge pull request #73 from ambiorix-web/feat/nested-routers
Browse files Browse the repository at this point in the history
feat: nested routers initial pass
  • Loading branch information
JohnCoene authored Oct 11, 2024
2 parents e4fdfd2 + fe1edb5 commit 6e856d9
Show file tree
Hide file tree
Showing 22 changed files with 528 additions and 655 deletions.
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

0 comments on commit 6e856d9

Please sign in to comment.