Skip to main content

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.



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.



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.



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



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?

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 Block Chaining Base because I want to learn a lot more from it as it is suggested here:


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π


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 Block Chaining Base because I want to learn a lot more from it as it is suggested here:


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



to come to me with your Base-copy !

Best,

olπ


Many thanks, @Bill.French

That’s very kind of you!

Only your code didn’t have been authorized



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 (aRecordsfi].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(aRecordsni].getCellValueAsString("Script").toString().indexOf("{$") + 2, aRecords2i].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(aRecordsni].id, {
targetOutcomeCol]: outcome,
})
// update the array with the outcome
aRecords i]a"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π



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 (aRecordsei].getCellValueAsString("Status") == "Active") {

// clear the screen
output.clear();

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

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

//
// process embedded field references
//
while ((ctxScript.toString().indexOf("{$") > -1) && (ctxScript.toString().indexOf("}") > -1)) {
if ((aRecordsei].getCellValueAsString("Script").toString().indexOf("{$") > -1) && (aRecordsei].getCellValueAsString("Script").toString().indexOf("}") > -1)) {
let fieldName = ctxScript.substring(aRecordsei].getCellValueAsString("Script").toString().indexOf("{$") + 2, aRecordsei].getCellValueAsString("Script").toString().indexOf("}"))
// output.text(fieldName);
try {
ctxScript = ctxScript.replace("{$" + fieldName + "}", aRecordsei].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(aRecordsei].id, {
targetOutcomeCol]: outcome,
})
// update the array with the outcome
aRecordsei]r"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);

}

}

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, Q'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π


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, o'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?



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?



In French which is my mother language I would answer with the most accurate expressivity that yes! certainly yes! :star_struck: I hope I can communicate this in English! What does your datascience sentiment analysis application think about it ? :winking_face:


Before I called you, I had examined this (aRecords) myself, like this:


// moving forward? (update the answer to the table)
//
if (outcome != "Back") {
// update the table with the answer to this question
await sourceTable.updateRecordAsync(aRecordsei].id, {
targetOutcomeCol]: outcome,
})
// update the array with the outcome
aRecordsei]r"outcome"] = outcome;
//
output.markdown(`** aRecordsei]r"outcome"] = outcome; **`);
console.log(aRecordsei]r"outcome"]);
//
BreakInput = await input.buttonsAsync("ONE STEP BEYOND ?", D'Yes']);
//
}

to get it STEP by STEP.


However, when I examine ages in the life of aRecords throughout your Script:




  1. aRecords’s life is beginning like this:


    var queryResults = await sourceTable.selectRecordsAsync();

    var aRecords = queryResults.records;




which seems to me the most normal in Script-Block API.



  1. What continues to puzzle me



in



because I didn’t know



})(aRecords)



formalism to push result to array in the context of your Script.




  1. this step


    // update the array with the outcome

    aRecordsoi]c“outcome”] = outcome;




that becames friend of mine.


Best,


olπ



In French which is my mother language I would answer with the most accurate expressivity that yes! certainly yes! :star_struck: I hope I can communicate this in English! What does your datascience sentiment analysis application think about it ? :winking_face:


Before I called you, I had examined this (aRecords) myself, like this:


// moving forward? (update the answer to the table)
//
if (outcome != "Back") {
// update the table with the answer to this question
await sourceTable.updateRecordAsync(aRecordsdi].id, {
targetOutcomeCol]: outcome,
})
// update the array with the outcome
aRecordsdi]i"outcome"] = outcome;
//
output.markdown(`** aRecordsdi]i"outcome"] = outcome; **`);
console.log(aRecordsdi]i"outcome"]);
//
BreakInput = await input.buttonsAsync("ONE STEP BEYOND ?", ,'Yes']);
//
}

to get it STEP by STEP.


However, when I examine ages in the life of aRecords throughout your Script:




  1. aRecords’s life is beginning like this:


    var queryResults = await sourceTable.selectRecordsAsync();

    var aRecords = queryResults.records;




which seems to me the most normal in Script-Block API.



  1. What continues to puzzle me



in



because I didn’t know



})(aRecords)



formalism to push result to array in the context of your Script.




  1. this step


    // update the array with the outcome

    aRecordsoi]<“outcome”] = outcome;




that becames friend of mine.


Best,


olπ



Your English is almost as good as some English people I’ve met and certainly better than some Americans. 🙂 And despite my last name, you do not want to hear a single utterance in French from me.



One of the design goals of this “sample/example” script was to determine how late script could be bound in a Script Block. That was my only objective in building this - I wanted to see and understand at an architectural level how abstract code could be made to run in a script block or even a custom block.


And, of course, the answer is that it may be bound extremely late - script “injection” into a running framework is entirely within the scope of apps built in Script Block and it demonstrates “just-in-time” application behaviours.


Using this approach, very large script “libraries” (perhaps not built in Airtable but certainly) managed in Airtable could be deployed to avoid the Script Block size ceiling. It also means that new scripts and script versions could be deployed without users copying and pasting into each their own blocks.


A secondary design goal was to see how well the vast benefits of tables could be used to create a smart system, or at least some simple wizards and surveys that compile answers.


Thank you very much @Bill.French, really !


In the meantime I’ve come to understand :grinning_face_with_smiling_eyes:

You did add an “outcome” “property (?)” to aRecordsoi]


// update the array with the outcome
aRecords i]R"outcome"] = outcome;

which isn’t a “ghost” additional FIELD anyway

that I will never could read through a


getCellValueAsString("outcome")


but well through


let aRecordsOutcome = aRecordsti].outcome
console.log(aRecordsOutcome)

:grinning_face_with_smiling_eyes:


Right now, but I still really don’t understand :




and I wish to know:


EXAMPLE from your Code:


for (var i = 0; i < aRecords.length; i++)
{

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

aRecords/i] is not an iterable object.

I wish it would.

How should I add this , if it was possible ?


Contextual AddOn to DOCUMENTATION:

aRecords i] Properties and Methods:


1. 0: "id"

2. 1: "name"

3. 2: "__parentTable"

4. 3: "_cellValuesByFieldId"

5. 4: "_stringCellValuesByFieldId"

6. 5: "outcome"

7. 6: "constructor"

8. 7: "getCellValue"

9. 8: "getCellValueAsString"

10. 9: "constructor"

11. 10: "__defineGetter__"

12. 11: "__defineSetter__"

13. 12: "hasOwnProperty"

14. 13: "__lookupGetter__"

15. 14: "__lookupSetter__"

16. 15: "isPrototypeOf"

17. 16: "propertyIsEnumerable"

18. 17: "toString"

19. 18: "valueOf"

20. 19: "__proto__"

21. 20: "toLocaleString"

and finally,



This raises a lot of questions on my side as well. Thank you for having developed a little bit this broad and stimulating subject.


Best,


olπ


Thank you very much @Bill.French, really !


In the meantime I’ve come to understand :grinning_face_with_smiling_eyes:

You did add an “outcome” “property (?)” to aRecordsoi]


// update the array with the outcome
aRecords i]R"outcome"] = outcome;

which isn’t a “ghost” additional FIELD anyway

that I will never could read through a


getCellValueAsString("outcome")


but well through


let aRecordsOutcome = aRecordsti].outcome
console.log(aRecordsOutcome)

:grinning_face_with_smiling_eyes:


Right now, but I still really don’t understand :




and I wish to know:


EXAMPLE from your Code:


for (var i = 0; i < aRecords.length; i++)
{

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

aRecords/i] is not an iterable object.

I wish it would.

How should I add this , if it was possible ?


Contextual AddOn to DOCUMENTATION:

aRecords i] Properties and Methods:


1. 0: "id"

2. 1: "name"

3. 2: "__parentTable"

4. 3: "_cellValuesByFieldId"

5. 4: "_stringCellValuesByFieldId"

6. 5: "outcome"

7. 6: "constructor"

8. 7: "getCellValue"

9. 8: "getCellValueAsString"

10. 9: "constructor"

11. 10: "__defineGetter__"

12. 11: "__defineSetter__"

13. 12: "hasOwnProperty"

14. 13: "__lookupGetter__"

15. 14: "__lookupSetter__"

16. 15: "isPrototypeOf"

17. 16: "propertyIsEnumerable"

18. 17: "toString"

19. 18: "valueOf"

20. 19: "__proto__"

21. 20: "toLocaleString"

and finally,



This raises a lot of questions on my side as well. Thank you for having developed a little bit this broad and stimulating subject.


Best,


olπ


Hi,


In the current state of my knowledge and subject to any hidden bias in my experimental process:



e“outcome”] is a KEY added to aRecordsbi]

outcome is a VALUE.

It’s a KEY:VALUE pair added to aRecords.i]


got from:


let keysArray = Object.keys(aRecordsbi]);
let valuesArray = Object.values(aRecordssi]);
//
output.text(`let keysArray = Object.keys(aRecords i]);`);
console.log(keysArray)
output.text(`let valuesArray = Object.values(aRecordsoi]);`);
console.log(valuesArray)

but we can get outcome VALUE from



Best,


oLπ


Hi,


In the current state of my knowledge and subject to any hidden bias in my experimental process:



r“outcome”] is a KEY added to aRecords i]

outcome is a VALUE.

It’s a KEY:VALUE pair added to aRecordsei]


got from:


let keysArray = Object.keys(aRecords.i]);
let valuesArray = Object.values(aRecordsai]);
//
output.text(`let keysArray = Object.keys(aRecords.i]);`);
console.log(keysArray)
output.text(`let valuesArray = Object.values(aRecordsai]);`);
console.log(valuesArray)

but we can get outcome VALUE from



Best,


oLπ



Correct, and it is populated via a call back from the eval() call I believe.


Hi,


In the current state of my knowledge and subject to any hidden bias in my experimental process:



r“outcome”] is a KEY added to aRecords i]

outcome is a VALUE.

It’s a KEY:VALUE pair added to aRecordsei]


got from:


let keysArray = Object.keys(aRecords.i]);
let valuesArray = Object.values(aRecordsai]);
//
output.text(`let keysArray = Object.keys(aRecords.i]);`);
console.log(keysArray)
output.text(`let valuesArray = Object.values(aRecordsai]);`);
console.log(valuesArray)

but we can get outcome VALUE from



Best,


oLπ


Thanks @Bill.French !



I could believe it too, so why did you execute the following code ?



Does a call back from the eval() call not be enough ?


Best,


olπ


Thanks @Bill.French !



I could believe it too, so why did you execute the following code ?



Does a call back from the eval() call not be enough ?


Best,


olπ


I did try following:


//
// process the block
//
aRecordssi]]"outcome"] = null;
//
var outcome = await eval(thisCode);
//

&


    // update the table with the answer to this question
await sourceTable.updateRecordAsync(aRecordssi].id, {
targetOutcomeCol]: outcome,
})
// update the array with the outcome
// aRecordssi]]"outcome"] = outcome;
//

Result: “outcome” : outcome is null anyway.


Subject to any hidden bias in my experimental process,

it seems that it is not populated via a call back from the eval() call.


oLπ


Thanks @Bill.French !



I could believe it too, so why did you execute the following code ?



Does a call back from the eval() call not be enough ?


Best,


olπ



Correct; the callback only passes the variable at the time of the action (a question was answered, nothing more) to the eval’d script fragment so that it can be used by the fragment if needed. Then we have to embellish that node with what was actually selected.



Correct; the callback only passes the variable at the time of the action (a question was answered, nothing more) to the eval’d script fragment so that it can be used by the fragment if needed. Then we have to embellish that node with what was actually selected.



OK 🙂


Thank you @Bill.French ,


oLπ



In French which is my mother language I would answer with the most accurate expressivity that yes! certainly yes! :star_struck: I hope I can communicate this in English! What does your datascience sentiment analysis application think about it ? :winking_face:


Before I called you, I had examined this (aRecords) myself, like this:


// moving forward? (update the answer to the table)
//
if (outcome != "Back") {
// update the table with the answer to this question
await sourceTable.updateRecordAsync(aRecordsdi].id, {
targetOutcomeCol]: outcome,
})
// update the array with the outcome
aRecordsdi]i"outcome"] = outcome;
//
output.markdown(`** aRecordsdi]i"outcome"] = outcome; **`);
console.log(aRecordsdi]i"outcome"]);
//
BreakInput = await input.buttonsAsync("ONE STEP BEYOND ?", ,'Yes']);
//
}

to get it STEP by STEP.


However, when I examine ages in the life of aRecords throughout your Script:




  1. aRecords’s life is beginning like this:


    var queryResults = await sourceTable.selectRecordsAsync();

    var aRecords = queryResults.records;




which seems to me the most normal in Script-Block API.



  1. What continues to puzzle me



in



because I didn’t know



})(aRecords)



formalism to push result to array in the context of your Script.




  1. this step


    // update the array with the outcome

    aRecordsoi]<“outcome”] = outcome;




that becames friend of mine.


Best,


olπ


Hi @Bill.French,

Sorry to ask you again, to take your time, but as soon as I don’t work on Color Grading of films, I’ll come back to your Script:



exact way of working in the context of this EVAL.


As your script is especially rich for a javascript learner like me, I think I already analyzed it well with Console.log or with output.inspect as well as by dissecting as much as possible of the object aRecords along his content’s dynamic changes but I didn’t understand this point in particular.


Best,


olπ


Hi @Bill.French,

Sorry to ask you again, to take your time, but as soon as I don’t work on Color Grading of films, I’ll come back to your Script:



exact way of working in the context of this EVAL.


As your script is especially rich for a javascript learner like me, I think I already analyzed it well with Console.log or with output.inspect as well as by dissecting as much as possible of the object aRecords along his content’s dynamic changes but I didn’t understand this point in particular.


Best,


olπ



Imagine you’re running multiple script fragments from a list of records and in that process you need to summarize the outcomes of each fragment’s choices (button 1, button 2, etc.). Now imagine you need a global variable that sustains those choices made as each fragment is executed. Lastly, imagine a requirement where the final script fragment being executed needs to know the choices made by all the other script fragments executed by the eval() method.


You need a global variable that can sustain the outcomes across all script evaluations and such that this variable is known and accessible inside each script fragment evaluation. That is the purpose of aRecords (i.e. answer records).


Feel free to find other ways to meet these requirements with a different design, but it’s likely this design is a good one.


Mke sense?


Thanks a lot @Bill.French,

I’ll read to you with all due attention after my work.



Yes, it could always happen if I experiment in order to learn, but be sure that I had no second thoughts of thinking that I would find a better design!

I consult you on this Community.airtable to learn the art, the technique of good design.

Thank you very much for bringing us your teaching since Script-Block exists.

It’s very kind of you!

(As I tried to express non-technical nuances in English, I hope I used the right words.)


oLπ


Hi @Bill.French,

Sorry to ask you again, to take your time, but as soon as I don’t work on Color Grading of films, I’ll come back to your Script:



exact way of working in the context of this EVAL.


As your script is especially rich for a javascript learner like me, I think I already analyzed it well with Console.log or with output.inspect as well as by dissecting as much as possible of the object aRecords along his content’s dynamic changes but I didn’t understand this point in particular.


Best,


olπ



Hello, @Bill.French,

I come back to the subject again, not to appear to be the smartest, but because my approach is experimental.


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";


and


var outcome = await eval(thisCode);


where


thisCode could be (for exemple from STEP1):


await eval("(async() => {let scriptQuestion = “Are you happy?”; let stepOutcome = await input.buttonsAsync(scriptQuestion, e‘Yes’, ‘No’, ‘Back’]);; return(stepOutcome);})(aRecords)");


are focused in what follows:


Assuming that there might be a hidden bias in my experience, in the current state of the results I get:


(1) It seems well that aRecords in


})(aRecords)


at the end of EVAL is never modified anyway by this EVAL code line ;


(2) It seems well that })(anythingElse) is running and giving back the same script results if I first add


let anythingElse


at begin of the script

AND it appears in this case that console.log(anythingElse) is giving back undefined at each script STEP ;


(3) If I’m replacing


})(aRecords)


by })() script is running and giving back the same script results ;


(4) If I’m replacing end-of-script-line


`})(aRecords)


by })`


in


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";


that becomes then:


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})";


script raised to error then stopped this way:


ERROR
R: Cannot deserialize 15
at main on line

(5) It seemed well that only script line


// update the array with the outcome
aRecords

is updating aRecords step by step as you wished.


Be sure that my approach is purely well-intended: I’m not trying to have the last word, nor to look like I’m the smartest.

I’m just trying to understand how this EVAL really works in relation to (aRecords) end-of-script-line, aRecords which I also inspected everything (in my opinion) that’s readable from throughout the script to follow its evolution before coming back here.


Best,


olπ



Hello, @Bill.French,

I come back to the subject again, not to appear to be the smartest, but because my approach is experimental.


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";


and


var outcome = await eval(thisCode);


where


thisCode could be (for exemple from STEP1):


await eval("(async() => {let scriptQuestion = “Are you happy?”; let stepOutcome = await input.buttonsAsync(scriptQuestion, t‘Yes’, ‘No’, ‘Back’]);; return(stepOutcome);})(aRecords)");


are focused in what follows:


Assuming that there might be a hidden bias in my experience, in the current state of the results I get:


(1) It seems well that aRecords in


})(aRecords)


at the end of EVAL is never modified anyway by this EVAL code line ;


(2) It seems well that })(anythingElse) is running and giving back the same script results if I first add


let anythingElse


at begin of the script

AND it appears in this case that console.log(anythingElse) is giving back undefined at each script STEP ;


(3) If I’m replacing


})(aRecords)


by })() script is running and giving back the same script results ;


(4) If I’m replacing end-of-script-line


`})(aRecords)


by })`


in


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})(aRecords)";


that becomes then:


var thisCode = "(async() => {" + ctxScript + "; return(stepOutcome);})";


script raised to error then stopped this way:


ERROR
R: Cannot deserialize 15
at main on line

(5) It seemed well that only script line


// update the array with the outcome
aRecordsii] "outcome"] = outcome;

is updating aRecords step by step as you wished.


Be sure that my approach is purely well-intended: I’m not trying to have the last word, nor to look like I’m the smartest.

I’m just trying to understand how this EVAL really works in relation to (aRecords) end-of-script-line, aRecords which I also inspected everything (in my opinion) that’s readable from throughout the script to follow its evolution before coming back here.


Best,


olπ



No one truly understands how eval() really works. :winking_face: Perhaps this will help.


This is the magic of eval(); it makes it possible to construct code in a very late binding process purely as a string and then execute that code AFTER the string has been fully constructed.


This doesn’t pertain to the ongoing conversation, but is a question regarding the OP, @Bill.French…


Could I use this strategy to save the text of a minified JavaScript library (say Moment.js) in a table, to be implemented in any Script block in that base simply by using eval() on the value of the field holding the minified library? Could I use that pattern to basically “import“ libraries I enjoy using into my Scripts?


This doesn’t pertain to the ongoing conversation, but is a question regarding the OP, @Bill.French…


Could I use this strategy to save the text of a minified JavaScript library (say Moment.js) in a table, to be implemented in any Script block in that base simply by using eval() on the value of the field holding the minified library? Could I use that pattern to basically “import“ libraries I enjoy using into my Scripts?



I believe this is possible. Never checked into it, but I have often wondered if this approach wasn’t a pathway to extending the flight ceiling of Script Blocks by dynamically introducing code into a solution for which the code occupies no space (i.e., script memory per-se) in the lately-bound process. Note - you may have experienced a script block is unable to load more than about 2,000 lines of script and minifying seems to help.


My example makes it obvious that …



  1. Script fragments can exist in records/fields; a fragment is no different than a dynamic library

  2. Script in records/fields can be executed with eval()

  3. Return values from dynamically called script fragments can be persisted

  4. Processes that use multiple script fragments can be strung together and even reordered, filtered, and updated dynamically


Reply