albertosantini / node-rio

Integration with Rserve, a TCP/IP server for R framework
https://github.com/albertosantini/node-conpa
MIT License
176 stars 35 forks source link

Passing relative routes as params #38

Closed davepadrino closed 7 years ago

davepadrino commented 7 years ago

Hi Alberto,

I'm using RIO to develop a Meteor-SparkR application.

The things i need is to pass the current route of a .R file as a parameter, but the problem is when i connect Rserve and print the current route with getwd() from .JS code, it prints "/tmp/Rserv/conn9584" and that is not the real location of my .R file.

The function works with an absolute route but i'd like to make it relative but with "__dirname" as the first parameter isn't working.

In conclusion, i need to pass JS variable to R through a call like this: Meteor.methods({ test2: async function(route){ var r = {route: route}; rio.e({command: "r"}); return r } })

I've tried to pass this "route" variable as an object, as an array, as a string, but no results and just the error:

"Eval failed with error code 127"

Any ideas?

THANKS!!

albertosantini commented 7 years ago

Thanks for the feedback.

Well, rio.e({command: "r"}); is wrong. It is like you are executing on R console:

> r
Error: object 'r' not found

README contains the documentation about the APIs.

For instance, you may give a look at the example 11, in examples folder, or the other examples involving an R script: it shows how to execute a script passing any data you want to the script and the script may returns any data you want.

The data object is the payload.

On js side:

rio.e({
    filename: path.join(__dirname, "ex11.R"),
    entrypoint: "run",
    data: {
        hello: escape("Hello 'world'!")
    },
    callback: displayResponse
});

On R side:

run <- function(jsonObj) {
    o = fromJSON(jsonObj)

    toJSON(o)
}
davepadrino commented 7 years ago

Hello again, thanks for the quick answer,

What i'm trying to do with rio.e({command: "r"}); is just print the value of the 'r' variable received through the js function, in R you can print any variable just calling it.

I already tried the ex11 but the __dirname is the route of the Rserve direction as i mentioned in my previous comment. All i'm trying to do is to pass an absolute local route like "/usr/local" where is supposed to be my .R file.

Something like

rio.e({
    filename: path.join("/home/<user>/Desktop/example/ex11.R"),
    entrypoint: "run",
    data: {
        hello: escape("Hello 'world'!")
    },
    callback: displayResponse
});

but instead of passing the absolute route (it works for me like that), i like to receive it as a parameter in the js function that calls the rio.e code

albertosantini commented 7 years ago

If you need to pass any data to Rserve instance, use data payload: inside the R script you need to deserialize that string with a JSON parser (for instance, with RJSONIO).

If I understood correctly the issue, I think there is a misunderstanding with RJSONIO. Indeed if you try in R console:

> require(RJSONIO)
> 
> foo <- function(jsonObj) {
+     o = fromJSON(jsonObj)
+ 
+     print(o$route)
+ 
+     res <- list()
+     res$route <- "ok"
+ 
+     toJSON(res)
+ }
> 
> foo('{"route":"/this/is/myroute"}')
Error in o$route : $ operator is invalid for atomic vectors
> 

And on js side the result, if that script would be called, would the usual error string: Eval failed with error code 127 Rserve call failed

But if we add the square brackets foo('{"route":["/this/is/myroute"]}'):

> require(RJSONIO)
> 
> foo <- function(jsonObj) {
+     o = fromJSON(jsonObj)
+ 
+     print(o$route)
+ 
+     res <- list()
+     res$answer <- "ok"
+ 
+     toJSON(res)
+ }
> 
> foo('{"route":["/this/is/myroute"]}')
[1] "/this/is/myroute"
[1] "{\n \"answer\": \"ok\" \n}"
> 

It works ok: now inside the R script you can use that param accordingly to your use case. I am sorry for the glitch, but it is RJSONIO behaviour.

This the complete example:

ex12.js

"use strict";

var path = require("path"),
    rio = require("../lib/rio");

function displayResponse(err, res) {
    if (!err) {
        res = JSON.parse(res);
        console.log(res);
    } else {
        console.log(err, "Rserve call failed");
    }
}

rio.e({
    filename: path.join(__dirname, "ex12.R"),
    entrypoint: "foo",
    data: {
        route: ["/this/is/myroute"]
    },
    callback: displayResponse
});

ex12.R

require(RJSONIO)

foo <- function(jsonObj) {
    o = fromJSON(jsonObj)

    print(o$route)

    res <- list()
    res$answer <- "ok"

    toJSON(res)
}

# foo('{"route":["/this/is/myroute"]}')

Put both scripts in examples folder and from the root of project you can execute: node examples/ex12.js It displays { answer: 'ok' }.

Don't forget the R script is read on the machine you are executing the js script, serialized and sent to Rserve instance. Rserve has an api like ctrlSource(file) to evaluate a source file where the Rserve instance is running, but there would not be a feasible way to pass params to the script.

Hope this helps.

davepadrino commented 7 years ago

Hi, thanks again.

I've found a way to do this:

const promisedResult = rio.$e({ filename: route + "example2.R", entrypoint: "getOptimalPortfolio", data: args })...

I think __dirname is not properly working on meteor.js and all i need is to pass in the route variable any route i previoulsy defined in my js.

I'm using this for my thesys in Computing degree and this is the only way i've found to work with Meteor.js + R + Spark + Hadoop.

Really appreciate your time to answer and to do this node-rio!