Skip to content

Rad Blocks

This section discusses another unique RSL concept, including rad blocks. These allow you to easily and concisely define queries to JSON APIs and what data you want to extract from the response, so that it can be displayed as a nice, readable table.

Basic Example

This concept will become clearer with an example.

Below is a simple script capable of querying a given GitHub repo (leveraging GitHub's public API) for information about its latest commits.

File: commits
args:
    repo string    # The repo to query. Format: user/project
    limit int = 20 # The max commits to return.

url = "https://api.github.com/repos/{repo}/commits?per_page={limit}"

Time = json[].commit.author.date
Author = json[].commit.author.name
SHA = json[].sha

rad url:
    fields Time, Author, SHA

Here's an example of running it, and the command line output:

rad commits amterp/rad 5
Querying url: https://api.github.com/repos/amterp/rad/commits?per_page=5
Time                  Author          SHA
2025-01-11T04:15:06Z  Alexander Terp  306f3a4ddb3b09747d61a5eab264c3d72fbbc36e
2025-01-11T03:07:56Z  Alexander Terp  304a914644dfd73a59b85a191481f9c429b4d25e
2025-01-11T01:00:42Z  Alexander Terp  7171ec92ae729f9d04e224e16272c9b888dffe41
2025-01-10T12:11:08Z  Alexander Terp  4b64f585d08d9a5ee40549b6b9624530ac713eb1
2025-01-09T11:34:51Z  Alexander Terp  abfcf2d940a18b819f7ae9e9040550a9644e5120

This is a simple example, but it demonstrates the ability to query GitHub's API (which returns JSON), extracting fields we're interested in (commits' time, author, and hash string).

And that's with just a few lines of code! Let's break it down piece by piece.

Arg Block

args:
    repo string    # The repo to query. Format: user/project
    limit int = 20 # The max commits to return.

url = "https://api.github.com/repos/{repo}/commits?per_page={limit}"

Here we have an args block where we declare a repo string and a limit int with a default of 20. After the args block, we create a url string using string interpolation to fill in the repo name, and the limit for how many commits we want GitHub to give us.

Json Field Definitions

Time = json[].commit.author.date
Author = json[].commit.author.name
SHA = json[].sha

This is where things get more interesting. The above definitions correspond to expected paths in the JSON response i.e. a series of keys to look up.

In RSL nomenclature, we refer to these as json field definitions.

If you query GitHub's API and take a look at the JSON response, you can see how these fields correspond to the data. Example URL if you want to see for yourself:

https://api.github.com/repos/amterp/rad/commits?per_page=2

Here's a simplified example response with two commits, showing the structure we're interested in:

[
  {
    "sha": "306f3a4ddb3b09747d61a5eab264c3d72fbbc36e",
    "commit": {
      "author": {
        "name": "Alexander Terp",
        "date": "2025-01-11T04:15:06Z"
      }
    }
  },
  {
    "sha": "2b642c482b32e4d87924839b2f5a9592670dee69",
    "commit": {
      "author": {
        "name": "Alexander Terp",
        "date": "2025-01-10T12:21:03Z"
      }
    }
  }
]

You can see how the path for Author = json[].commit.author.name corresponds to a final value of "Alexander Terp".

Json field definitions leverage a special syntax that RSL offers. They always start with json. json can be thought of as representing the JSON blob as a whole, or its root, if you want to think of the JSON data as a tree.

The [] after json tells RSL that you expect the json blob to be a list, and to "unwrap" it and dig into individual items in that list for the remainder of the path. commit then tells it to look up a key of that name (literally commit), and to then look up author next, and then finally name.

The json field definition syntax can do a lot of things that we won't cover here. For details, see its reference page.

When the json field assignment is executed, its variable (Author for example) is initialized as an empty list. It's a "special" list though, as it has a json field definition tied to it, which can be used in a rad block, let's look at that one next.

Rad Block

rad url:
    fields Time, Author, SHA

rad is a keyword. As with the name of the interpreter and CLI tool which runs it, it stands for "Request And Display", to give you an idea for what it does.

Following the rad keyword, we provide the url variable which will get used to execute an HTTP GET request. Then, inside the rad block itself, using the keyword fields, we specify the earlier json fields that we defined. Listing them here will tell the rad block to extract data from the resulting JSON blob according to the json field paths that you defined. The ordering in which the fields are listed also controls the ordering of columns in the ensuing table that gets printed.

When run, this will print the table we saw earlier; here it is again for reference (with a limit of 3).

Time                  Author          SHA
2025-01-11T04:15:06Z  Alexander Terp  306f3a4ddb3b09747d61a5eab264c3d72fbbc36e
2025-01-11T03:07:56Z  Alexander Terp  304a914644dfd73a59b85a191481f9c429b4d25e
2025-01-11T01:00:42Z  Alexander Terp  7171ec92ae729f9d04e224e16272c9b888dffe41

Note that the variable names we choose for the json field definitions become the header names in the table.

Additional Rad Block Options

Rad blocks offer additional syntax for controlling what your final table looks like.

Sorting

By default, the rows in your table are sorted by their original order in the JSON blob. However, you can control this.

Let's say we have the following table by default:

City         Country  Population
Los Angeles  USA      3800000
London       England  8800000
Houston      USA      2300000
Copenhagen   Denmark  640000

The simplest sorting option is alphabetically, across the whole row.

rad url:
    fields City, Country, Population
    sort
City         Country  Population
Copenhagen   Denmark  640000
Houston      USA      2300000
London       England  8800000
Los Angeles  USA      3800000

What if we wanted to sort by Country, though? And then break ties with City? We can do that:

rad url:
    fields City, Country, Population
    sort Country, City
City         Country  Population
Copenhagen   Denmark  640000
London       England  8800000
Houston      USA      2300000
Los Angeles  USA      3800000

If we wanted to sort by descending population, you can add desc after the name of the column:

rad url:
    fields City, Country, Population
    sort Population desc
City         Country  Population
London       England  8800000
Los Angeles  USA      3800000
Houston      USA      2300000
Copenhagen   Denmark  640000

'asc' is the default

sort City and sort City asc are both valid and identical in functionality - you can include it if you want to be explicit.

Mapping

You can also transform a column's values before it gets printed.

For example, let's say you wanted the 'Population' column in the above example to be in millions, and to display one decimal place, you can do that with a map column modifier:

rad url:
    fields City, Country, Population
    Population:
        map p -> "{p/1000000:.1}"

If we run this, you'll see the change:

City         Country  Population
Los Angeles  USA      3.8
London       England  8.8
Houston      USA      2.3
Copenhagen   Denmark  0.6

This example is actually using string interpolation formatting, which we haven't covered yet, so is a bit of a preview. Let's break down this syntax though. After declaring the fields:

Population:
    map p -> "{p/1000000:.1}"

Population: begins a column modifier block. The identifier prior to the colon is expected to be one of the fields. Inside one of these blocks, you can apply modifiers on that column, such as map or color.

map is considered a keyword in the context of rad blocks. After map, a lambda expression is expected, which you can think of as a mini-function.

In this case, we've written p -> "{p/1000000:.1}". Left of the arrow is an identifier p (name could be any valid identifier). This will represent an individual value in the Population list. Right of the arrow is an expression for what that entry in the list should become. It is the output of your lambda mini-function. In this example, we turn it into a string, created using string interpolation.

Inside the string interpolation expression, we first divide it by one million (p/1000000), and then use formatting syntax (right of the colon) to specify that we want the resulting float to be stringified with one decimal place (.1).

Note that sorting operations are done on the pre-mapping column values.

Color

Another column modifier uses the keyword color. You can tell rad to color a cell's value depending on its contents by using a regex.

For example:

rad url:
    fields City, Country, Population
    Country:
        color "pink" ".*"
        color "red" "Denmark"
        color "blue" "USA"

The syntax is color <color> <regex>. You can apply multiple rules, and later rules override earlier ones. For example, here we start off by coloring everything pink. Then, we add three more rules: any sequence "Denmark" should be colored red, and "USA" should be colored blue.

color

This screenshot from a terminal demonstrates the colors. England is colored pink because the initial .* rule is the only regex that matched it.

Note that the specific colors that appear varies between terminals and user settings.

See the reference for a list of valid colors.

If Statements

Rad blocks can contain if statements, so if you want slightly different behavior for your rad block based on some condition, you don't need to copy and paste the whole block into two separate if blocks.

Let's say your script had a flag for sorting by population or not. Here's an example of how that could look:

args:
    sort_by_population "sort-by-population" p bool  # Enable to sort by population.

// ... some script stuff here

rad url:
    fields City, Country, Population
    if sort_by_population:
        sort Population desc
    else:
        sort

If the flag is enabled, we sort by descending population, otherwise we sort rows alphabetically.

You can put any rad block statements into these if blocks, including fields, column modifiers, etc.

Request Blocks

There are two other types of rad blocks: "request" blocks and "display" blocks. Let's first discuss the former.

A request block is similar to a rad block, however it does not print out any table after executing its query and field extraction steps. You can use them to just query and extract fields from a returned json blob and stop there - allowing you to separately process the populated json fields/lists further if need be, or do whatever else you'd like.

Generally the syntax is very similar between request and rad blocks.

request url:
    fields City, Country, Population

// from here the above fields' lists are populated with extracted values

Naturally, block statements like sorting or coloring have no effect, and may be considered invalid and error.

Wait, so is a request block different from a rad block, or is it a type of rad block?

We generally consider "display" and "request" blocks to be types of rad blocks, just with slightly different syntax and behavior. We may still compare them to the rad block (i.e. the one using rad as its keyword) when discussing differences however - it depends on the context in which they're discussed.

Display Blocks

Display blocks are the opposite side of the coin from request blocks. Where the request blocks will perform a request, populate your lists, and not print, the display block will simply take already-populated lists and display them as a table.

To demonstrate, you can even manually populate your lists (columns) and use them as fields for the table:

Nums = [1, 2]
Words = ["hi", "hello"]
display:
    fields Nums, Words
Nums  Words
1     hi
2     hello

Notice display does not take any url or equivalent thereof, as it doesn't perform any requests, and gets all it needs from the specified fields.

Combining request and display blocks

Sometimes you want to display more information in your table than the JSON blob itself provides. For example, you may wish to derive an additional column off two others. To accomplish this, you can write a request block, derive whatever additional columns you want, and then write a display block to print your final range of columns.

For example:

request url:
    fields width, height

area = [width[i] * height[i] for i in range(width)]

display:
    fields width, height, area

Summary

  • RSL provides first-class support for scripts whose primary purpose is to query a JSON API and present parts of its response in a useful, readable table.
  • This is offered by the rad blocks, and its siblings display and request.
  • RSL has syntax for specifying json field definitions or json field paths, that rad blocks can then use to extract fields from a JSON blob response.
    • e.g. Author = json[].commit.author.name
  • Rad blocks offer customization options, such as coloring, transforming cell values, and sorting.
  • You can also use if statements inside them.

Next

RSL offers a range of useful built-in functions to help you write your scripts. We'll take a look at those in the next section: Functions.