Perinci::CmdLine, as it name reflects, centers around the concept of Rinci. Functions, as well as packages (and variables and other types of code entities) are added with rich metadata so tools interacting with them can have a better idea about the details of the entities and do useful things with them.
In Perl module/script, Rinci metadata is placed in a package variable called %SPEC, with the name of the function or variable (along with its sigil) serves as the key (for package, the key is :package).
But, for flexibility Rinci metadata should be able to be put elsewhere, even remotely.
Thus Riap, as the other side of the coin, is born. It is a client-server, request-response protocol to exchange Rinci metadata or do things with code entities. Request is represented with a hash, with the minimum keys of: v (protocol version, default to 1.1), uri (location to code entity), action.
The Riap response is an enveloped result, which has been discussed previously (pericmd 013). Riap adds several keys in the result metadata (fourth element), mainly: riap.v (server Riap protocol version), and some optional others.
The server side is viewed as a tree of code entities, with packages having the ability to contain other subentities.
The URL can be schemeless path like /WWW/PAUSE/Simple/ (notice the ending slash) which in Perl application maps to Perl package (WWW::PAUSE::Simple) or /WWW/PAUSE/Simple/list_files (notice the lack of ending slash) which maps to a Perl function (or variable, or other non-package entity).
meta
The meta action is one of the most important actions. This is a request for Rinci metadata itself. So a Riap request like {action=>"meta", uri=>"/WWW/PAUSE/Simple/"} will return a response of something like below (this is providing you have installed WWW::PAUSE::Simple from CPAN):
[
200,
"OK (meta action)",
{
"entity_date" : "2015-02-26",
"entity_v" : "0.07",
"summary" : "An API for PAUSE",
"v" : 1.1
},
{}
]
How about request for a function metadata: {action=>"meta", uri=>"/WWW/PAUSE/Simple/list_files"}? This might return something like:
[
200,
"OK (meta action)",
{
"args" : {
"del" : {
"schema" : [
"bool",
{},
{}
],
"summary" : "Only list files which are scheduled for deletion",
"summary.alt.bool.not" : "Only list files which are not scheduled for deletion",
"tags" : [
"category:filtering"
]
},
"detail" : {
"schema" : [
"bool",
{},
{}
],
"summary" : "Whether to return detailed records"
},
"file" : {
"greedy" : 1,
"pos" : 0,
"schema" : [
"array",
{
"of" : "str*",
"req" : 1
},
{}
],
"summary" : "File name/wildcard pattern"
},
"password" : {
"is_password" : 1,
"req" : 1,
"schema" : [
"str",
{
"req" : 1
},
{}
],
"summary" : "PAUSE password",
"tags" : [
"common"
]
},
"username" : {
"req" : 1,
"schema" : [
"str",
{
"match" : "\\A\\w{2,9}\\z",
"max_len" : 9,
"req" : 1
},
{}
],
"summary" : "PAUSE ID",
"tags" : [
"common"
]
}
},
"args_as" : "hash",
"entity_date" : "2015-02-26",
"entity_v" : "0.07",
"result_naked" : 0,
"summary" : "List files on your PAUSE account",
"v" : "1.1",
"x.perinci.sub.wrapper.logs" : [
{
"normalize_schema" : 1,
"validate_args" : 1,
"validate_result" : 1
}
]
},
{}
]
You can use a convenient command-line utility called riap to launch Riap requests and browse around a Riap server as if it were a filesystem (install the utility via cpanm -n App::riap), e.g.:
% riap
riap / > meta /WWW/PAUSE/Simple/
┌────────────────────────────────┒
│ key value ┃
│ ┃
│ entity_date 2015-02-26 ┃
│ entity_v 0.07 ┃
│ summary An API for PAUSE ┃
│ v 1.1 ┃
┕━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
riap / > meta /WWW/PAUSE/Simple/ --json
[
200,
"OK (meta action)",
{
"entity_date": "2015-02-26",
"entity_v": "0.07",
"summary": "An API for PAUSE",
"v": 1.1
}
]
call
Aside from meta, call is another important action. It is used to call a function and return its result. Another request key args (hash) can be added to specify function arguments. A Riap request like {action=>"call", uri=>"/Perinci/Examples/gen_array", args=>{len=>5}} might return (assuming you have installed Perinci::Examples from CPAN):
[200,"OK",[4,2,1,3,5]]
Obviously, only functions can be called. If you try to call a non-function entity, an error will be returned:
% riap
riap / > cd /Perinci
riap /Perinci > call Examples/
ERROR 501: Action 'call' not implemented for 'package' entity (at ... line ...)
Note that the riap utility regards Riap packages as directories and Riap functions as executable files, so instead of:
% riap
riap / > call /Perinci/Examples/gen_array --args '{"len":5}'
┌────────────────────────┒
│ 3 1 2 3 5 ┃
┕━━━━━━━━━━━━━━━━━━━━━━━━┛
you can also “run” a function like it is an executable program:
riap / > /Perinci/Examples/gen_array --len 5 --json
[
200,
"OK",
[
"2",
"1",
"4",
"3",
"2"
],
{}
]