Help

Save the date! Join us on October 16 for our Product Ops launch event. Register here.

Re: BlockChaining - Is it Possible?

8288 0
cancel
Showing results for 
Search instead for 
Did you mean: 

Um, yeah - it is. But first, a little of the backstory.

As is often the case just when you’ve thought you’ve seen it all, a client asks -

Is it possible to chain blocks and run them sequentially - as one completes, the next one starts?

No, right?

But then she tossed in another requirement -

The computational results from the first block must be available to the next block and sometimes to all subsequent blocks.

Oy! Nothing like a mind-bender for a Monday morning.

This business requirement is daunting – indeed, impossible given the nature of Script Blocks. I’m sure the Airtable folks have pondered this, but as it stands, it’s off the table for now, right? Indeed, Script Blocks are simply not capable … until and unless … you factor in the amazing power of that which Airtable is known and most famous for - simple data/content management with an intimate relationship with integrated script.

Often, it is the alchemy of data and code that spark the most remote possibilities into a new reality.

Simple Workflow Example - a Happiness Survey

Imagine a three-step process asking basic questions about your day. It’s a survey per-se and I’ve embodied the essence of it into a simple table shown below. Each step has a question and a column for answers. Lastly, there’s a final step - Happiness Assessment - that computes your relative happiness based on your answers

The following screenshot shows this three-step process but also displays a very important field showing Block Script in a long text field.

As I mentioned, each step is a question, and each question is presented abstractly in the script using the value in the first column (i.e., Process Step). The final stage in the process is based upon the answers at each step in the survey process.

image

BlockChaining

To do this elegantly and well, chaining together different script blocks is ideal.

Each script in each process is loaded and executed sequentially. A single “runner” Block orchestrates the execution of each data-based block and it manages the in-memory scope of the results from start to finish.

image

The final step includes the block shown below which displays the resulting happiness assessment. Note the reference to aRecords - this is the variable that contains all of the answers gathered from each of the chained blocks.

image

And the outcome in the block runner reveals this display when all four steps have completed.

image

Summary

Block chaining is possible. In and of itself, this is an important realization. While my example is super simple and somewhat lame, imagine the incredible solutions that could be built by chaining block scripts together in easily-managed table data.

  • The execution order of block segments could be altered by simply dragging table rows.
  • Imagine using this to administer dynamic tests where specific (named) users must be tracked.
  • Learning systems could be provided through Block Chaining.
  • Searching block script for key elements and debugging is made simple by Airtable search.
  • Building complex expert systems are more possible because blending data is virtually unlimited.
  • Block Chaining is somewhat ideal for creating code reuse.
  • Building code in Long Text fields is terrible (just sayin’… build them in a real block and test, then deploy as data objects).

As I mentioned, it is also possible to manage, share, and aggregate computational results that occur across multiple block segments.

Tell me - is Block Chaining a thing?

43 Replies 43

Do I understand correctly that you are executing JavaScript which is stored in a field of a table by querying that record and retrieving the JavaScript out of the record’s field?

Yes. The block runner retrieves and executes script blocks from the table itself. I also have tested block script served from Apps Script services.

Hi @Bill.French
Still busy with Script-Block and not yet in Custom-Block (not until my Summer Holidays that will come since 10 july), I’m asking you where (on the WEB) and how much do you sell or rent your a307b5161b2de9ac29b3aa061cf857638b6dfb70.pngBlock Chaining Base because I want to learn a lot more from it as it is suggested here:
Screen Shot 2020-06-20 at 19.50.45

Be sure that this is not a veiled way to ask for this for free: on the opposite, I would find it quite normal to finally compensate you a little after so many free lessons on the Script-Block Forum.

Best,

oLπ

No worries - it’s a cool demonstration that abstracts script code as data and executes each record as if it were a script block. This base should allow you to make a copy and play.

Many thanks, @Bill.French
That’s very kind of you!
Only your code didn’t have been authorized
Screen Shot 2020-06-21 at 02.52.17
to come to me with your Base-copy !
Best,
olπ

Here’s the code…

/*

   ***********************************************************
   ScriptBloqs - ScriptChain
   Copyright (c) 2020 by Global Technologies Corporation
   ALL RIGHTS RESERVED
   ***********************************************************
   
*/

//
// get the table name and records
//
output.markdown('# BlockChaining');

let sourceTable     = await input.tableAsync("Pick a Process:");
let sourceTableName = sourceTable.name;
var queryResults    = await sourceTable.selectRecordsAsync();
var aRecords        = queryResults.records;

//
// set the static column names (warning - these are hard-wired into this demo app)
//
let processStepCol   = "Process Step";
let targetOutcomeCol = "Answers";

//
// delete existing answers
//
for (let record of queryResults.records) {
    await sourceTable.updateRecordAsync(record.id, {
        [targetOutcomeCol]: outcome,
    })
}

//
// loop through the script items
//
// for (let record of queryResult.records) {
for (var i = 0; i < aRecords.length; i++)
{

    if (aRecords[i].getCellValueAsString("Status") == "Active") {

        // clear the screen
        output.clear();

        //
        // display the process title and step
        //
        output.markdown('# ' + sourceTableName);
        output.markdown("## " + aRecords[i].getCellValueAsString(processStepCol));

        //
        // get the script block to execute for this process step
        //
        let ctxScript = aRecords[i].getCellValueAsString("Script");

        //
        // process embedded field references
        //
        while ((ctxScript.toString().indexOf("{$") > -1) && (ctxScript.toString().indexOf("}") > -1)) {
            if ((aRecords[i].getCellValueAsString("Script").toString().indexOf("{$") > -1) && (aRecords[i].getCellValueAsString("Script").toString().indexOf("}") > -1)) {
                let fieldName = ctxScript.substring(aRecords[i].getCellValueAsString("Script").toString().indexOf("{$") + 2, aRecords[i].getCellValueAsString("Script").toString().indexOf("}"))
                // output.text(fieldName);
                try {
                    ctxScript = ctxScript.replace("{$" + fieldName + "}", aRecords[i].getCellValueAsString(fieldName));
                } catch(e) {
                    // do nothing
                }
            }
        }

        //
        // build the eval function block
        //
        var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";

        //
        // process the block
        //
        var outcome = await eval(thisCode);

        // 
        // moving forward? (update the answer to the table)
        //
        if (outcome != "Back") {
            // update the table with the answer to this question
            await sourceTable.updateRecordAsync(aRecords[i].id, {
                [targetOutcomeCol]: outcome,
            })
            // update the array with the outcome
            aRecords[i]["outcome"] = outcome;
        }

        //
        // decrement the process
        //
        if ((outcome == "Back") && (i <= 1)) {
            i = -1;
        } else if ((outcome == "Back") && (i >= 2)) {
            i = i - 2;
        } else if (outcome == "Back") {
            i = i - 1;
        }

        // output.inspect(aRecords);

    }    
    
}

Thanks a lot @Bill.French,
At my level of experience in js and airtable, it’s one of the essential gifts I’ve received since I arrived here to progress, like some scripts + explanations received from other Experts in this Script-Block Section.
Without these amazing contributions, I would probably have continued by myself out of Airtable’s environment that has its wonderful facilities and its few restrictions set by its design and by its Administrators, but almost free of constraints, life in js and py is not easy anyway! I’ve been there so I know it.
oLπ

Hi @Bill.French,

After having searched and searched well, reviewing some more javascript, I can’t understand what’s going on here:

var outcome = await eval(thisCode);

that runs actually:

var outcome = await eval("(async() => {let scriptQuestion = "Are you happy?"; let stepOutcome = await input.buttonsAsync(scriptQuestion, ['Yes', 'No', 'Back']);; return(stepOutcome);})(aRecords)");

is OK for me, except (aRecords):
What the h*ll is (aRecords) at the end of the expression being evaluated?!?

Be sure that I have examined (aRecords) throughout your Script but at this point, both its position and its role escapes me!

Thank you very much in advance for considering my question,

oLπ

It’s an array of responses, one for each question. It is a global variable the is accessible even in script steps defined in the table records.

It provides the ability to evaluate the answers provided from all the chained script blocks once the entire collect of questions has been completed.

Is this like the coolest, craziest app you’ve ever seen?