Plumber: Doubts in creating routes in a programmable way

Created on 31 Dec 2018  Â·  8Comments  Â·  Source: rstudio/plumber

I'm trying to create an MVC (Angular) site development library in R using the plumber route system, but I'm stagnant on a part of the system that I still can not solve.

Using the Plumber class, I created a method in my R6 class to generate the routes, where I pass parameters to it but when I run the plumber it generates error in creating the route because it can not identify the callback (function (req, res)) of the handle function .

make_route = function (route_method, route_name, route_path, is_html) {
    private$plumber$handle(route_method, route_path, function (req, res) {
        view_name <- tolower(gsub(".", "/", gsub("_", "/", route_name, fixed = T), fixed = T))
        route_name <- gsub(" ", "", str_to_title(gsub(".", " ", gsub("_", " ", route_name, fixed = T), fixed = T)), fixed = T)

        controller_name <- paste0(route_name, "Controller")
        model_name <- route_name

        model <- NULL

        if (model_name %in% ls()) {
            model <- eval(parse(
                text = paste0(model_name, "$new()")
            ))
        }

        model <- NULL

        controller <- eval(parse(
            text = paste0(controller_name, "$new(model, req, res)")
        ))

        response <- controller$boot()

        if (is_html) {
            res$setHeader("Content-Type", "text/html; charset=utf-8")

            if (file.exists(paste0("./resources/view/", view_name, ".html"))) {
                view <- read_file(paste0("./resources/view/", view_name, ".html"))
            }

            html <- self$processor$process(view, response)

            return (self$processor$output(read_file("./public/index.html"), html))
        } else {
            res$setHeader("Content-Type", "application/json; charset=utf-8")

            return (response)
        }
    })
}

(full code in: Github)

I still don't know how to create this function and generate all the routes in a friendly and dynamic way (I also think of storing the routes in a JSON to facilitate the development and performance of the application as a whole).

Does anyone have any light? Or should I give up?

need info question

All 8 comments

I believe the approach is fine. (Function encapsulate when adding a new handle)

Can you provide me with the whole traceback() of the error?

@schloerke Hi!

I tried again, but still the same.

traceback:

14: execCallbacks(timeoutSecs, all, loop$id)
13: run_now(check_time, all = FALSE)
12: service(0)
11: httpuv::runServer(host, port, self)
10: private$plumber$run(api_host, api_port) at Router.R#35
9: Router$run(App$environments$APP_HOST, App$environments$APP_PORT) at bootstrap.R#4
8: eval(ei, envir)
7: eval(ei, envir)
6: withVisible(eval(ei, envir))
5: source("resources/bootstrap.R") at lambR.R#6
4: eval(ei, envir)
3: eval(ei, envir)
2: withVisible(eval(ei, envir))
1: source("./lambR.R")

@aleDsz I've downloaded your repo and I'm able to run it. Don't know how to help much.

lambR-master: R
> install.packages("plumber")
trying URL 'https://cloud.r-project.org/bin/macosx/contrib/4.0/plumber_0.4.6.tgz'
(Content type 'application/x-gzip' length 357004 bytes (348 KB)
==================================================
downloaded 348 KB

The downloaded binary packages are in
    /var/folders/0k/bxg5lhr92sq74mb1d446ql540000gp/T//RtmpZTn9T1/downloaded_packages

> source("lambR.R")

Attaching package: ‘dplyr’

The following objects are masked from ‘package:plyr’:

    arrange, count, desc, failwith, id, mutate, rename, summarise, summarize

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Starting server to listen on port 8080
Running the swagger UI at http://127.0.0.1:8080/__swagger__/

@schloerke But if u try to access http://127.0.0.1:8080 you will see the error he throws because he doesn't recognize how to process the line:

private$plumber$handle(route_method, route_path, function (req, res) {
# [...]

Your problem is that self$processor$process(view, response) does not exist. Nothing to do with plumber.

Browse[1]> 
debug at ./app/Router.R#75: html <- self$processor$process(view, response)
Browse[2]> self$processor
NULL
Browse[2]> self$processor$process(view, response)
Error: attempt to apply non-function

It's processing it. The function has multiple errors.

  • There is no self$processor defined.
        initialize = function (plumber, App) {
            self$processor <- WebProcessor$new()
  • Return res in your route to not have the HTML be sent through the default JSON parser. Also change processor$process to processor$process_html
                    html <- self$processor$process_html(view, response)
                    res$body <- self$processor$output(read_file("./public/index.html"), html)
                    return (res)
  • When checking the view folder, change folder /view/ to /views/ to match repo structure
                    if (file.exists(paste0("./resources/views/", view_name, ".html"))) {
                        view <- read_file(paste0("./resources/views/", view_name, ".html"))
                    }
  • Definition of WebProcessor response argument is missing an s
        process_html = function (html, response) {

Final product:

Screen Shot 2020-07-31 at 10 19 55 AM

Hope this helps!

(Closing as issue is not plumber related)

If you have to troubleshoot R code, check out browser() and debugonce(). They are really useful and you can find out what is going on within your code and other libraries code. Hopefully, things will go smoothly from here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jeffkeller87 picture jeffkeller87  Â·  6Comments

dpmccabe picture dpmccabe  Â·  6Comments

rensa picture rensa  Â·  4Comments

utkarshsaraf19 picture utkarshsaraf19  Â·  3Comments

Jube-Dev picture Jube-Dev  Â·  3Comments