Showing posts with label CFML. Show all posts
Showing posts with label CFML. Show all posts

cfmlFiddle - Compare ColdFusion, Lucee, and BoxLang Side-by-Side

Mike's Notes

Thank you, James. This will help with code testing.

Resources

References

  • Reference

Repository

  • Home > Ajabbi Research > Library >
  • Home > Handbook > 

Last Updated

23/04/2026

cfmlFiddle - Compare ColdFusion, Lucee, and BoxLang Side-by-Side

By: James Moberg
myCFML: 16/04/2026

Mike is the inventor and architect of Pipi and the founder of Ajabbi.

I built a local CFML playground that runs 10 engines at once

I've been writing CFML for a long time. Long enough to remember when testing something meant editing a file, refreshing the browser, and hoping the server hadn't crashed. We have better tools now, but I kept running into the same problem: I'd want to check how something behaves on CF2021 vs CF2025, or whether Lucee handles a date function differently than Adobe, and there wasn't a good way to do that without maintaining a bunch of separate installs.

The online tools help. CFFiddle.org, TryCF.com, and Try BoxLang are all useful when you need a quick test. But I kept hitting limitations. CFFiddle requires a social login to use some features. TryCF doesn't indicate which patch version it's running against. Neither lets you compare engines side-by-side. And both go offline sometimes, usually right when I need them. :(

So I built cfmlFiddle.

What it is

cfmlFiddle is a self-hosted CFML playground. It runs on your machine through CommandBox. You write code in an Ace Editor, pick an engine (or all of them), click Run, and see the output. If you picked multiple engines, you see all the results stacked, side by side, or tabbed.

cfmlFiddle screenshot

The default config ships with 10 server definitions:

  • Adobe ColdFusion 2016, 2021, 2023, 2025
  • Lucee 5, 6, 7
  • BoxLang (native, Adobe compat, Lucee compat)

You start whichever ones you need. Most of the time I run two or three.

The part I actually wanted

My main gripe with the online tools was never being able to pin a version. If I'm debugging something a client reported on CF2021.0.14, I need to know what CF2021.0.14 does, not whatever patch the hosted service happens to be running this week.

With cfmlFiddle, each engine version is a CommandBox server.json file. You control which version is installed, down to the patch. You can keep CF2021.0.14 around for months if you need it, or install CF2025 the day it drops.

The other thing: running code that the hosted services block. File operations, HTTP calls, Java objects, custom tags. cfmlFiddle doesn't restrict anything. It's your machine.

How the comparison works

Click "Run All Online" and cfmlFiddle sends your code to every running engine simultaneously via cfhttp. Each engine executes the same temp file from a shared webroot. The results come back with timing info, the engine name, and the actual patch version, so you can see exactly what ran where.

There's also an "Append" mode. Check the box and each run stacks on top of the previous results, so you can tweak your code and compare iterations without losing the earlier output.

Interactive mode

I wrote a test script with a <form> and immediately realized the form couldn't post back to itself. The result was static HTML in a div. The form action had nowhere to go.

cfmlFiddle now auto-detects forms in your output. When it finds one (or you check the Interactive box), it renders the result in a sandboxed iframe pointing directly at the payload file on the target engine. The form posts back to itself, processes the data, and returns the result. Multi-step scripts just work.

Under the hood

The status bar uses Server-Sent Events instead of polling. The heartbeat checks all engines with raw TCP socket connections (~50ms total for 10 servers) and streams updates to the browser in real time. It falls back to polling if SSE doesn't work on a particular engine.

Config lives in a JSON file above the webroot. You can change settings without editing CFML code.

All the frontend libraries (Ace, jQuery, SweetAlert2, jQuery contextMenu) ship locally in an assets/vendor/ directory. No CDN dependency by default, though you can flip a config switch if you prefer CDN.

Server management is built into the UI. Click the status bar to start, stop, or inspect engines. Left-click any server for a context menu with direct links to its admin panel, homepage, and documentation.

Session management

Every time you run code, cfmlFiddle saves the payload file with a timestamp. Click the Session button in the toolbar to see a list of everything you've run. Click any entry to reload it into the editor. When you're done, Archive All zips everything up and clears the working directory.

I kept losing track of what I'd tested ten minutes ago. Now I just open the session list and pick it.

The smaller stuff

There's a light/dark theme toggle. It picks up your OS preference by default, and the Ace editor switches to match. I bounce between light and dark depending on the time of day, so this was mostly for me.

You can import code from a GitHub Gist URL. Paste the link, it pulls the first file and drops it in the editor. Useful when someone shares a snippet and you want to see what it does on three engines before replying.

Snippets work the other direction too. Save whatever's in the editor as a named file, reload it later from the dropdown.

Each result card has a refresh button that re-executes and updates the timing, plus a dismiss button to toss results you don't need. Small thing, but it adds up when you're iterating.

We also put some work into keyboard accessibility: skip link, visible focus indicators, arrow keys on the splitter, ARIA roles on the toolbar and status bar.

Getting it

cfmlFiddle is open source under the MIT license.

Website: cfmlFiddle.com Source: GitHub

You need CommandBox installed. Clone the repo, edit config.json with your box.exe path, run box task run launchCFMLFiddle, and pick an engine. It opens in your browser.

cfmlFiddle is a myCFML.com project, sponsored by SunStar Media.

Add a data source using Coldfusion Administrator API

Mike's Notes

Google Search - AI Mode (Gemini) was used to find the sample code for adding a data source using the ColdFusion Administrator API.

Speed is king

My strength is architecture and problem-solving, not coding (a slow writer and too many typos), so 99.9% of the coding is now supplied via Google Search - AI Mode (Gemini), then I test everything. It's about 100X faster. 😎😎😎😎😎😎😎

Data sources

data/

  • couchbase/
  • db2/
  • derby/
  • h2/
  • hsqldb/
  • mariadb/
  • msaccess/
    • 32/ (32-bit)
    • 64/ (64-bit)
  • mssql/
  • oracle/
  • pg/
    • 18/ (version 18)
  • sqllite/
  • sybase/
  • virtuoso/

Other issues to explore later

  • The advanced arguments (such as connection pool limits or timeout settings) for these specific drivers.

Future testing

  • Install loki-01 on 9cc/
  • Run the Data Engine (dat) to create all data sources
  • Test data sources.

Here is a detailed example copied from the ColdFusion Cookbook. It is written by Jeremy Petersen and was last updated in 2007.


How do I programmatically create a new datasource?

The short answer is to use the ColdFusion Administrator API.


The following is taken directly from the ColdFusion documentation:
You can perform most ColdFusion MX Administrator tasks programmatically using the Administrator API. The Administrator API consists of a set of ColdFusion components (CFCs) that contain methods you call to perform Administrator tasks. For example, you use the setMSQL method of datasource.cfc to add a SQL Server data source.

<cfscript>
   // Login is always required. This example uses a single line of code.
   createObject("component","cfide.adminapi.administrator").login("admin");
   // Instantiate the data source object.
   myObj = createObject("component","cfide.adminapi.datasource");
   // Required arguments for a data source.
   stDSN = structNew();
   stDSN.driver = "MSSQLServer";
   stDSN.name="northwind_MSSQL";
   stDSN.host = "10.1.147.73";
   stDSN.port = "1433";
   stDSN.database = "northwind";
   stDSN.username = "sa";
   // Optional and advanced arguments.
   stDSN.login_timeout = "29";
   stDSN.timeout = "23";
   stDSN.interval = 6;
   stDSN.buffer = "64000";
   stDSN.blob_buffer = "64000";
   stDSN.setStringParameterAsUnicode = "false";
   stDSN.description = "Northwind SQL Server";
   stDSN.pooling = true;
   stDSN.maxpooledstatements = 999;
   stDSN.enableMaxConnections = "true";
   stDSN.maxConnections = "299";
   stDSN.enable_clob = true;
   stDSN.enable_blob = true;
   stDSN.disable = false;
   stDSN.storedProc = true;
   stDSN.alter = false;
   stDSN.grant = true;
   stDSN.select = true;
   stDSN.update = true;
   stDSN.create = true;
   stDSN.delete = true;
   stDSN.drop = false;
   stDSN.revoke = false;
   //Create a DSN.
   myObj.setMSSQL(argumentCollection=stDSN);
</cfscript>

Resources

References

  • Reference

Repository

  • Home > Ajabbi Research > Library >
  • Home > Handbook > 

Last Updated

18/04/2026

Add a data source using Coldfusion Administrator API

By: Mike Peters and Gemini
On a Sandy Beach: 18/04/2026

Mike is the inventor and architect of Pipi and the founder of Ajabbi.

Google Gemini is cool.

The ColdFusion server stores data source configurations in specific XML files located within its installation directory. The exact location depends on your version and installation type (Server vs. J2EE configuration). [1, 2, 3, 4, 5] 

Configuration File Locations

Most modern Adobe ColdFusion versions (including ColdFusion 2021 and 2025) store these settings in the following files:

  • Main Datasource File: neo-datasource.xml.
  • Legacy/Alternative File: neo-query.xml (often used in older versions like ColdFusion 7). [1, 2] 

Default File Paths

The absolute path varies based on how ColdFusion was installed: [6] 

  • Server Configuration (Standard): {cf_root}/cfusion/lib/ (e.g., C:\ColdFusion2021\cfusion\lib\neo-datasource.xml on Windows).
  • JEE / Multiserver Configuration: {cf_webapp_root}/WEB-INF/cfusion/lib/.
  • Central Config Server (CCS): If using a [Central Config Server](https://helpx.adobe.com/coldfusion/using/central-config-server-coldfusion.html), settings are stored in <CF_Instance_Home>/bin/ccsstore/ and replicated across nodes. [1, 2, 3, 7] 

Important Notes

  • Encryption: While you can see the DSN names and server addresses in these XML files, passwords are encrypted and cannot be read in plain text.
  • Editing: It is highly recommended to manage datasources through the ColdFusion Administrator (found under Data & Services > Data Sources) rather than editing the XML files directly to avoid corruption.
  • API Access: You can also manage these settings programmatically using the [ColdFusion Administrator API](https://helpx.adobe.com/coldfusion/kb/database-connections-handled-coldfusion.html). [1, 8, 9, 10, 11, 12] 

References

To use the ColdFusion Administrator API to add a datasource, you use a set of ColdFusion components (CFCs) located in the /CFIDE/adminapi/ directory. This allows you to manage server settings programmatically without using the web-based Administrator interface. [1, 2, 3, 4] 

Prerequisites

  • Enable RDS: Some versions or configurations may require enabling the RDS service in Security > RDS within the ColdFusion Administrator.
  • Administrator Password: You must have the server's administrator password to authenticate. [1, 5, 6, 7] 

Basic Steps and Example Code

The following cfscript example demonstrates how to log in and create a Microsoft SQL Server datasource using the datasource.cfc (or Data Sources.cfc in some versions). [5, 8] 

<cfscript>
    // 1. Authenticate with the Administrator API
    adminObj = createObject("component", "cfide.adminapi.administrator");
    adminObj.login("yourAdminPassword"); // Replace with your actual admin password
    // 2. Instantiate the Data Source object
    // Note: Use "datasource" or "Data Sources" depending on your CF version
    dsObj = createObject("component", "cfide.adminapi.datasource");
    // 3. Define the datasource details
    // For SQL Server, you use the setMSSQL method
    dsObj.setMSSQL(
        name = "MyNewDSN",
        host = "127.0.0.1",
        port = "1433",
        database = "MyDatabaseName",
        username = "dbUser",
        password = "dbPassword",
        description = "Datasource created via Admin API"
    );
    // 4. (Optional) Verify the connection
    isVerified = dsObj.verifyDsn("MyNewDSN");
    
    if (isVerified) {
        writeOutput("Datasource successfully created and verified!");
    } else {
        writeOutput("Datasource created but failed verification.");
    }
</cfscript>

Common Methods for Different Drivers

The datasource component provides specific methods for different database types: [4, 9, 10] 

  • SQL Server: setMSSQL()
  • MySQL: setMySQL5() or setMySQL()
  • [Oracle](https://www.oracle.com/): setOracle()
  • Derby: setDerbyEmbedded()
  • Other JDBC: setOther() (used for custom or thin drivers) [4, 5, 6, 8, 11, 12, 13] 

Alternative: Per-Application Datasources [14] 

In ColdFusion 2021 and later, you can also define datasources directly in your Application.cfc without using the Admin API. This is often preferred for modern application deployment as it keeps configurations within the application code. [14, 15, 16, 17, 18]

// In Application.cfc
component {
    this.name = "MyApplication";
    this.datasources["myDSN"] = {
        driver: "MSSQLServer",
        host: "localhost",
        database: "mydb",
        username: "user",
        password: "pass"
    };
    this.datasource = "myDSN";
}

References

To use the ColdFusion Administrator API with tag-based syntax, you must first authenticate using administrator.cfc, then call the specific driver methods in datasource.cfc.

1. Authentication (Required First)

Before adding any datasource, you must log in to the API session. [1] 

<cfset adminObj = createObject("component", "cfide.adminapi.administrator")>
<cfset adminObj.login("yourAdminPassword")>
<!--- Instantiate the datasource object for subsequent calls --->
<cfset dsObj = createObject("component", "cfide.adminapi.datasource")>

------------------------------

2. Database-Specific Tag Examples

Microsoft SQL Server

Uses the setMSSQL method.

<cfset dsObj.setMSSQL(
    name = "MyMSSQL_DSN",
    host = "127.0.0.1",
    database = "Northwind",
    username = "db_user",
    password = "db_password"
)>

PostgreSQL

Uses the setPostgreSQL method.

<cfset dsObj.setPostgreSQL(
    name = "MyPostgres_DSN",
    host = "localhost",
    database = "sales_db",
    username = "postgres",
    password = "password"
)>

MySQL

Uses setMySQL5 (for MySQL 5.x and later, including MariaDB).

<cfset dsObj.setMySQL5(
    name = "MyMySQL_DSN",
    host = "://example.com",
    database = "inventory",
    username = "admin",
    password = "secretpassword"
)>

Oracle

Uses setOracle. Note that Standard edition users often use the "Other" method with an Oracle thin driver.

<cfset dsObj.setOracle(
    name = "MyOracle_DSN",
    host = "oracle_server",
    service_name = "ORCL",
    username = "system",
    password = "password"
)>

MS Access (32-bit vs. 64-bit)

Both use the setMSAccess method, but the behaviour depends on the server's ODBC configuration and the ColdFusion bitness.

  • 32-bit: Requires the 32-bit Microsoft Access Database Engine.
  • 64-bit: Requires the 64-bit Microsoft Access Database Engine.

<cfset dsObj.setMSAccess(
    name = "MyAccessDSN",
    database = "C:\data\mydb.mdb"
)>

Sybase

Uses the setSybase method.

<cfset dsObj.setSybase(
    name = "MySybase_DSN",
    host = "sybase_host",
    database = "production",
    username = "sa",
    password = "password"
)>

DB2

Uses the setDB2 method.

<cfset dsObj.setDB2(
    name = "MyDB2_DSN",
    host = "db2_server",
    database = "SAMPLE",
    username = "db2admin",
    password = "password"
)>

------------------------------

3. Verification (Recommended)

After adding a datasource, it is best practice to verify the connection immediately.

<cfset isVerified = dsObj.verifyDsn("MyMSSQL_DSN")>
<cfif isVerified>
    <p>Connection Successful!</p>
<cfelse>
    <p>Connection Failed. Check credentials or server availability.</p>
</cfif>

References

Pipi Nestspace update - what I learned this week

Mike's Notes

The experiments continue, with progress slow but steady, and with a lot of new stuff learned.

Ongoing test results at the end. This one seems to be working without error.

😎😎😎😎

Update 19/04/2026

  • Nestspace /9cc/ passed all critical tests
  • The next job is to add the System Engine (sys) to nestspace /9cc/ and test it.
  • Then add a Nestspace Engine (nst) inside the System Engine (sys) to enable Pipi to build, configure, and repair nestspaces on demand, so that Pipi can then deploy itself to production and also create archives of itself.
Update 18/05/2025
  • Create a NEST mapping
  • Add Application.cfc to /9cc/
  • Add nest_boxlang.cfm, nest_cfml_engine.cfm, nest_java.cfm, nest_os.cfm to /9cc/

Update 27/05/2026

Nest Engine (nst) renamed as Nestspace Engine (nst)

Resources

References

  • Reference

Repository

  • Home > Ajabbi Research > Library >
  • Home > Handbook > 

Last Updated

2705/2026

Pipi Nestspace update - what I learned this week

By: Mike Peters
On a Sandy Beach: 15/04/2026

Mike is the inventor and architect of Pipi and the founder of Ajabbi.

Problem

Last week, I created Pipi Nestspace, a container that uses the standard root directory convention to host Pipi.

  1. The problem I discovered was that a standard deployment was not working across multiple CFML Engine environments on various operating systems.
    • Adobe ColdFusion Server 11+
    • Lucee
    • BoxLang
  2. Pipi (robot version) is required to manipulate enterprise applications.
  3. Mission Control requires a website
  4. Some of the configuration files were outside the webroot.
    • Application.cfc (1), (2)

Solution

  • Install side by side in <pipi nest>/pipi/
    • <user account>/ (none, one, or many)
    • <pipi instance-n>/ (one or many)
  • <nestspace>/pipi/<pipi instance>/www/
    • mission-control/
  • Create CFML server mappings where a Pipi Instance is the only user.
    • data/
    • pipi/
    • work/

Database support

Adobe ColdFusion, Lucee, and BoxLang are all built on the Java Virtual Machine (JVM). This means they can technically connect to any relational database that provides a JDBC (Java Database Connectivity) driver

Pipi Core example (Windows)

/9cc/ (Nestspace)

  • Application.cfc
  • nest_boxlang.cfm
  • nest_cfml_engine.cfm
  • nest_java.cfm
  • nest_os.cfm
  • nest_probe.cfm
  • data/
    • pipi_data.cfm
    • couchbase/
    • db2/
    • derby/
    • h2/
    • hsqldb/
    • informix/
    • mariadb/
    • msaccess/
      • 32/
      • 64/
    • mssql/
    • mysql/
    • oracle/
    • pg/
      • 18/
    • sqllite/
    • sybase/
    • virtuoso/
  • pipi/
      • Application.cfc (1) - Nest settings
      • pipi_nest.cfm
      • loki-01/ (instance)
        • ...
      • loki-02/ (instance)
        • Application.cfc (2) - Account settings
        • pipi_account.cfm
        • pip/
          • Application.cfc (3) - Version settings
          • pipi_version.cfm
          • i18n/
          • log/
          • pipi_<pipi version>.txt
          • sys/
            • Application.cfc (4) - System settings
            • pipi_system.cfm
          • temp/
          • template/
            • _include/
            • _layout/
              • _log/
        • www/
          • mission-control/
            • Application.cfc (4) - Website settings
    • work/
      • pipi_work.cfm
      • backup/
      • install/
      • project/

    Pipi Enterprise example (Windows)

    /9ae/ (Nestspace)

    • Application.cfc
    • nest_boxlang.cfm
    • nest_cfml_engine.cfm
    • nest_java.cfm
    • nest_os.cfm
    • nest_probe.cfm
    • data/
      • pipi_data.cfm
      • couchbase/
      • db2/
      • derby/
      • h2/
      • hsqldb/
      • informix/
      • mariadb/
      • msaccess/
        • 32/
        • 64/
      • mssql/
      • mysql/
      • oracle/
      • pg/
        • 18/
      • sqllite/
      • sybase/
      • virtuoso/
    • pipi/
      • Application.cfc (1) - Nest settings
      • pipi_nest.cfm
      • ajabbi/ (Account Name)
        • Application.cfc (2) - Account settings
        • pipi_account.cfm
        • com/
        • dat/
        • lib/
        • log/
        • plu/
        • tmp/
        • www/
          • learn.ajabbi.com/
            • Application.cfc (4) - Website settings
          • wiki.ajabbi.com/
            • Application.cfc (4) - Website settings
      • dis-01/ (Instance)
      • ...
      • dis-04/ (Instance)
        • Application.cfc (2) - Account settings
        • pipi_account.cfm
        • pip/
          • Application.cfc (3) - Version settings
          • pipi_version.,cfm
          • i18n/
          • log/
          • pipi_<pipi version>.txt
          • sys/
            • Application.cfc (4) - System settings
            • pipi_system.,cfm
          • temp/
          • template/
            • _include/
            • _layout/
              • _log/
        • www/
          • mission-control/
            • Application.cfc (4) - Website settings
    • work/
      • pipi_work.cfm
      • backup/
        • pipi_9ae_ajabbi_pip_www_learn.ajabbi.com_20260411.zip
      • install/
      • project/

    Automated installs

    • Adobe ColdFusion Server 11+: Many XML files store the server configuration. Any pipi instance doing Pipi Nest installs could rewrite these XML files.
    • Lucee: Yet to discover
    • BoxLang: Yet to discover

    Production obfuscation

    Pipi will obfuscate these names using UUIDs and other dastardly methods on public-facing production websites. But the names are very useful for UI labels and visualisation.


    DevOps log (edit)

    A record of work done.

    NZ DateTime Action Object Status
    2026-04-15 14:15 Edit server.xml Mappings Success
    2026-04-15 14:20 Create Mapping Docs Complete
    2026-04-15 14:26 Rename Named instances Success
    2026-04-15 14:48 Create Mission Control Success
    2026-04-15 17:24 Test Application.cfc (1) - Pipi Nest settings Success
    2026-04-15 17:40 Test pipi_nest.cfm Success
    2026-04-16 13:22 Test pipi_account.cfm Success
    2026-04-16 13:54 Test Application.cfc (2) - Pipi Account settings
    Not inheriting Application.cfc (1)
    Success
    2026-04-16 15:09 Test Application.cfc (3) - Version settings Success
    2026-04-16 15:40 Test pipi_version.cfm Success
    2026-04-16 16:02 Test Application.cfc (4) - Pipi Website settings Success
    2026-04-16 16:03 Test pipi_website.cfm Success
    2026-04-16 19:09 Create Named instance website Success
    2026-04-16 19:44 Test pipi_nest_probe.cfm Success
    2026-04-16 20:28 Test java.lang.System class - get properties Success
    2026-04-17 11:23 Test Add a 32-bit datasource using the CFML Engine CFIDE Success
    2026-04-17 11:56 Test Add a 64-bit datasource using the CFML Engine CFIDE Success
    2026-04-17 13:33 Test Add a datasource using Coldfusion Administrator API
    • couchbase/
    • db2/
    • derby/
    • h2/
    • hsqldb/
    • informix/
    • mariadb/
    • msaccess/
    • mssql/
    • mysql/
    • oracle/
    • pg/
    • sqllite/
    • sybase/
    • virtuoso/
    Success
    2026-04-19 18:00 Demo 9cc/ live demo to Open Research Group Success

    Changes to the Pipi System Engine (sys) data model

    Mike's Notes

    The next job is to write the 6 configuration files for each Nestspace.

    A big thanks is owed to Ben Nadel for the sample code he shared on his CFML blog, which explained some ways to do this.

    Resources

    References

    • Reference

    Repository

    • Home > Ajabbi Research > Library >
    • Home > Handbook > 

    Last Updated

    27/05/2026

    Changes to the Pipi System Engine (sys) data model

    By: Mike Peters
    On a Sandy Beach: 12/04/2026

    Mike is the inventor and architect of Pipi and the founder of Ajabbi.

    Changes this week to how Pipi is organised in the data centre continue.

    This job is to write the 6 base configuration files for each Nestspace. Because the recent code tests were successful, most of the time will be spent documenting. This should only need to be done once. The 6 files can be done sequentially, starting from the root.

    Pipi Nest

    The fundamental organising principle is to use a uniquely named "Nestspace" directory to host Pipi.

    Every nestspace has these properties;

    • One Pipi major version
    • One Pipi edition
    • One account type
    • Can host
      • One or more accounts
      • AND
      • One or more Pipi instances.

    Account

    Every customer or user has an account, which is opened when they sign up.

    A customer account has these properties;

    • One account type.
    • Contains one or more deployments.

    Pipi Instance

    Each Pipi instance has these properties;
    • One account name, e.g., "loki-01".
    • Shares a codebase, e.g., "loki".

    Deployment

    A deployment has these properties;

    • One deployment tenancy type.
    • One language.
    • Contains one or more deployment objects.
    • Can contain other deployments to create global settings for an account (Enterprise or DevOps).

    Revised Configuration Hierarchy

    Child files inherit properties from parent files and can also override them.

    • Nestspace > Account > Deployment > Deployment Object > Publication > Website > Workspace.
    • Pipi Nest > Codebase > Pipi Instance.

    Configuration files

    Pipi uses a hierarchy of CFML configuration files to set system properties.

    <nestspace>/
    • nest_probe.cfm
    • pipi/
      • Application.cfc [1]
      • pipi_nest.cfm
      • <name>/
        • Application.cfc [2]
        • pipi_account.cfm
        • pip/
          • Application.cfc [3]
          • pipi_system.cfm

    Configuration notes

    The 3 different Application.cfc files are common across all Pipi Nests and don't ever change.

    Application.cfc [1] defines Nestspace variables

    • OS
    • Java environment
    • Platform-appropriate absolute physical path
    • Nest datasources

    Application.cfc [2] defines Name variables

    • Account name
    • Deployments
    • Pipi Instances

    Application.cfc [3] defines System variables

    • Version
    • Edition
    • State

    The .cfm files contain specific local configuration variables that can be directly edited by Pipi.

    • nest_probe.cfm
    • pipi_nest.cfm
    • pipi_account.cfm
    • pipi_system.cfm

      Example

      This is the list of 7 configuration files for the Nestspace 9cc/

      • 9cc/pipi_nest_probe.cfm
      • 9cc/pipi/Application.cfc
      • 9cc/pipi/pipi_nest.cfm
      • 9cc/pipi/loki/Application.cfc
      • 9cc/pipi/loki/pipi_account.cfm
      • 9cc/pipi/loki/pip/Application.cfc
      • 9cc/pipi/loki/pip/pipi_system.cfm

      Pipi autonomous Read-Eval-Print-Loop (REPL)

      Mike's Notes

      Deployment roadmap

      Later in the year, Pipi will be able to deploy infrastructure on any platform using Infrastructure as Code (IaC).

      Options on how to do this include;

      • CL (Command Line)
      • REPL (Read-Eval-Print-Loop)
      • API

      Implementing a REPL (Read-Eval-Print-Loop) programmatically in CFML

      I asked Google Search AI Mode (powered by Google Gemini) how to use CFML to write and read REPL.

      The Google output has been reformatted but not edited, and the code needs to be tested. The questions were turned into headings. There is a lot of duplication.

      I find that carefully using Gemini to write CFML code and then checking everything is making me more productive.

      There is now enough here to set up REPL.

      Once this automation is complete, productivity will increase by at least x10.

      Resources

      References

      • Reference

      Repository

      • Home > Ajabbi Research > Library >
      • Home > Handbook > 

      Last Updated

      04/04/2026

      Pipi autonomous Read-Eval-Print-Loop (REPL)

      By: Mike Peters and Gemini
      On a Sandy Beach: 04/04/2026

      Mike is the inventor and architect of Pipi and the founder of Ajabbi.

      Gemini is still cool.

      A Read-Eval-Print-Loop (REPL) is an interactive computer programming environment that accepts single-user input, executes it, and returns the result immediately. It is also known as an interactive toplevel or language shell. [1, 2, 3] 

      The Four Steps of the Loop

      The name describes the precise cycle the environment follows: [4, 5] 

      • Read: The system accepts a line of input (usually a single expression or command) from the user.
      • Eval: It parses and evaluates the input code to determine its value or effect.
      • Print: The resulting value or output is displayed back to the user on the screen.
      • Loop: The process repeats, returning to the prompt to wait for the next input. [1, 5, 6, 7, 8, 9, 10] 

      Why Developers Use REPLs

      REPLs are highly valued for their tight feedback loop, which allows for: [6, 7, 11] 

      • Interactive Prototyping: Quickly testing small snippets of code without creating and saving files.
      • Debugging: Investigating the state of a program and testing potential fixes in real-time.
      • Learning: Experimenting with new language features or libraries with instant results.
      • Calculations: Using the environment as a high-powered calculator for mathematical expressions. [1, 12, 13, 14, 15, 16, 17] 

      Common Examples

      Most modern programming languages provide a built-in REPL or have popular third-party versions: [1, 18] 

      References

      Implementing REPL programmatically in CFML

      Implementing a REPL (Read-Eval-Print-Loop) programmatically in CFML involves creating a loop that captures user input, evaluates it as live code, and returns the result. [1, 2, 3] 

      While Adobe ColdFusion (2018+) and CommandBox include built-in REPLs for terminal use, you can build your own programmatic version using the following core functions: [2, 4]

      1. The Evaluation Engine

      The most critical part of a REPL is the Eval step. CFML provides two primary ways to execute strings as code:

      • Evaluate(): Best for executing single expressions and returning a value.
      • Render(): (Lucee specific) Similar to Evaluate, but specifically designed to execute CFML tag or script code and return the generated output as a string. [5, 6] 

      2. Building a Basic Web-Based REPL

      A programmatic REPL usually consists of a simple loop (often via a form submission) that maintains state.

      // 1. READ: Get input from a form or API
      inputCode = form.myScript ?: "";
      if (len(trim(inputCode))) {
          try {
              // 2. EVAL: Execute the code
              // Note: Evaluate() is powerful but dangerous with untrusted input
              result = evaluate(inputCode);
              
              // 3. PRINT: Output the result
              writeOutput("Result: " & result);
          } catch (any e) {
              writeOutput("Error: " & e.message);
          }
      }
      // 4. LOOP: Handled by the user submitting the form again

      3. Advanced Implementation Details

      To create a more robust "shell-like" experience:

      • Persistent State: To keep variables alive across multiple inputs, store a "context" structure in a persistent scope like Session or Application.
      • Capturing Console Output: If the code uses writeOutput(), you may need to use cfsavecontent to capture that output before it hits the main buffer.
      • Safety: Never expose a REPL to public users. It allows for arbitrary code execution, which can delete files or access sensitive server data.
      • Multi-line Support: For more complex scripts, you may need to detect if a block (like an if statement or function) is complete before evaluating, similar to how [CommandBox's REPLParser](https://community.ortussolutions.com/t/replparser-multiline-support/9559) works. [6, 7, 8] 

      Summary of Tools

      Feature [2, 3] Function/Tool Platform
      Expression Eval Evaluate(string) Adobe & Lucee
      Full Code Execution Render(string) Lucee Only
      Terminal REPL cf.bat / cf.sh Adobe (2018+)
      CLI REPL box repl CommandBox

      References

      Using workflows written in CFML to write and read REPL

      To build a workflow in CFML that programmatically reads from and writes to a REPL, you need to treat the REPL as an external process. This is most effectively done using CommandBox, which provides a robust CLI and REPL environment for CFML. [1, 2] 

      1. Using CommandBox via cfexecute

      The standard way to interact with a REPL from a running CFML workflow (like a background task or a web request) is to use <cfexecute> or the execute() function to call the [CommandBox binary](https://modern-cfml.ortusbooks.com/intro/instructions-and-interpreters).

      • Writing to REPL: You can pass CFML code directly to the CommandBox repl command using the --code flag or by piping input.
      • Reading from REPL: The output of the command is captured in a variable for your workflow to process. [3, 4, 5] 

      Example: Running a Snippet and Capturing the Result

      // Define the CFML code you want the REPL to evaluate
      replInput = "x = 10; y = 20; writeOutput(x + y);";
      // Execute CommandBox REPL with the code
      execute(
          name = "box", 
          arguments = "repl --code=""#replInput#""", 
          variable = "replOutput", 
          timeout = 10
      );
      // Read the result back into your workflow
      writeOutput("The REPL returned: " & replOutput);

      2. Interactive Workflows with Piping

      If your workflow needs to chain multiple operations, you can use the cfml command in CommandBox. This allows you to [pipe data](https://commandbox.ortusbooks.com/usage/execution/cfml-functions) between CFML functions as if they were terminal commands.

      • Workflow logic: #listGetAt www.foo.com 2 . | #ucase | #reverse
      • Result: The REPL processes each step and passes the "Read" result of one to the "Eval" of the next. [6, 7] 

      3. Asynchronous "Daemon" REPL

      For a long-running workflow that needs to keep a REPL "warm" (to avoid startup overhead), consider using an Event Gateway or a persistent background thread. [8] 

      1. Start a process: Use java.lang.ProcessBuilder via CFML to open a persistent connection to the CommandBox shell.
      2. Write to Stream: Send commands to the process's OutputStream.
      3. Read from Stream: Use a separate CFML thread to constantly poll the InputStream for the REPL's response. [9, 10] 

      4. Safety and Best Practices

      • Sanitisation: If the input for your REPL comes from an external source, treat it as unsafe. Programmatic REPLs can execute fileDelete() or access system environment variables.
      • Timeouts: Always set a timeout in your execute calls to prevent a "loop" in the REPL from hanging your entire server.
      • Context: Remember that code executed in a separate REPL process does not share the same variable scope (like Session or Request) as your main workflow unless you explicitly pass those values in. [11] 

      References

      Pass complex data (like a Struct) between your workflow and a CommandBox REPL

      To pass complex data, such as a Struct or Array, between a CFML workflow and a REPL, the most reliable method is to use JSON serialisation. Since the REPL and your main application run in separate memory spaces, JSON acts as a universal bridge for data exchange.

      1. Passing a Struct into the REPL (Write)

      When calling the REPL, convert your CFML Struct into a JSON string and inject it into the command as a literal. CommandBox’s REPL is smart enough to interpret a JSON string as a native Struct if it's the first input to certain functions or assigned directly. [1, 2] 

      // Your complex workflow data
      userData = {
          "id": 123,
          "roles": ["admin", "editor"],
          "settings": { "theme": "dark" }
      };
      // Serialize to JSON (use single quotes for the shell command)
      jsonInput = serializeJSON(userData);
      // Build the REPL command
      // Note: We assign the JSON string directly to a variable inside the REPL
      replCommand = "data = #jsonInput#; writeOutput(data.roles[1]);";
      execute(
          name = "box",
          arguments = "repl --code=""#replCommand#""",
          variable = "replResponse"
      );
      writeOutput("REPL processed role: " & replResponse); // "admin"

      2. Getting a Struct back from the REPL (Read)

      To read complex data back into your workflow, have the REPL print the result as JSON. You then use deserializeJSON() in your main workflow to turn that string back into a live CFML Struct. [3] 

      // REPL code that performs logic and returns a new struct
      replCode = "result = { 'status': 'success', 'timestamp': now() }; writeOutput(serializeJSON(result));";
      execute(
          name = "box",
          arguments = "repl --code=""#replCode#""",
          variable = "rawJsonResponse"
      );
      // Read the complex data back into your workflow
      if (isJSON(rawJsonResponse)) {
          finalData = deserializeJSON(rawJsonResponse);
          writeOutput("Status received: " & finalData.status);
      }

      Key Considerations for Data Integrity

      • Key Casing: Adobe ColdFusion typically converts struct keys to UPPERCASE during serialisation unless you quote the keys (e.g., {"name": "value"}).
      • Shell Escaping: Be careful with double quotes inside your JSON. When building the arguments string for execute(), ensure you escape quotes correctly so the shell doesn't break the command.
      • Performance: If you are passing very large datasets (multi-MB structs), the overhead of serialisation and shell execution will increase. For high-frequency workflows, consider writing the data to a temporary JSON file and having the REPL read that file instead of passing it as a command-line argument. [4, 5, 6, 7] 

      Using temporary files to handle larger datasets between your workflow and the REPL

      To handle larger datasets between your CFML workflow and a REPL, using temporary files is the most reliable method. This avoids shell character limits and prevents memory issues. [1] 

      1. The Workflow: Writing to a Temp File

      First, your main application serialises the data into a JSON file. Use getTempFile() to ensure a unique filename that won't collide with other processes. [2, 3] 

      // 1. Prepare your large dataset
      complexData = {
          "users": queryToArray( myLargeUserQuery ), // Assume 10k rows
          "metadata": { "source": "workflow_alpha", "processed": now() }
      };
      // 2. Create a unique temp file
      tempFilePath = getTempFile( getTempDirectory(), "repl_data" );
      // 3. WRITE: Save the JSON to the file
      fileWrite( tempFilePath, serializeJSON( complexData ) );

      2. The REPL: Reading the File

      When you execute the REPL, pass the file path as a variable. The REPL code will then read the file from disk and deserialise it back into a native CFML object. [4, 5] 

      // 4. Build the REPL script to READ the file
      replCode = "
          data = deserializeJSON( fileRead('#tempFilePath#') );
          // Perform complex logic on the data
          resultCount = data.users.len();
          writeOutput( resultCount );
      ";
      // 5. EXECUTE: Call CommandBox REPL
      execute(
          name = "box",
          arguments = "repl --code=""#replCode#""",
          variable = "replOutput"
      );

      3. Cleaning Up

      Because these files are stored in the system's temporary directory, they will eventually be cleared, but it is best practice to delete them manually once your workflow is finished to save disk space. [1, 6] 

      // 6. Cleanup after reading the result
      if ( fileExists( tempFilePath ) ) {
          fileDelete( tempFilePath );
      }
      writeOutput( "REPL processed #replOutput# records." );

      Why use files instead of variables?

      • Memory Efficiency: Large strings in shell commands can lead to "Out of Memory" (OOM) errors or exceed operating system command-line length limits.
      • Data Integrity: Files eliminate the need for complex shell quoting and escaping of special characters in your JSON.
      • Persistent Context: If the REPL crashes, the data remains safely on disk for debugging or a retry attempt. [1, 7] 

      References

      CommandBox Task Runners to make these multi-step REPL workflows even cleaner

      While a REPL is great for ad-hoc snippets, CommandBox Task Runners provide a more structured way to handle multi-step workflows. A Task Runner is a CFML component (.cfc) that runs directly from the command line, allowing you to pass complex arguments naturally and use built-in helpers for file and shell interaction. [1, 2, 3, 4]

      1. Create the Task Runner (MyTask.cfc) [5]

      Instead of cramming logic into a string for the REPL, you write a standard CFML function. You can create one by running task create --open in [CommandBox](https://commandbox.ortusbooks.com/task-runners). [2, 3, 6] 

      /**
       * MyTask.cfc
       * Automates a workflow with complex data
       */
      component {
          
          function run( string inputPath ) {
              // 1. READ: Load the large dataset from the file
              var data = deserializeJSON( fileRead( arguments.inputPath ) );
              
              print.greenLine( "Processing #data.users.len()# users..." );

              // 2. EVAL: Perform your workflow logic
              var result = {
                  "processedCount": data.users.len(),
                  "timestamp": now(),
                  "status": "complete"
              };

              // 3. PRINT: Return the result as JSON for the calling workflow
              print.text( serializeJSON( result ) );
          }
      }

      2. Execute the Task from your Workflow [5]

      In your main application, you call the task just like you did with the REPL, but it’s much cleaner because the logic is encapsulated in its own file. [7, 8] 


      // Path to your Task Runner CFC
      taskPath = expandPath( "./MyTask.cfc" );
      tempDataFile = getTempFile( getTempDirectory(), "data" );

      // Prepare and write data
      fileWrite( tempDataFile, serializeJSON( myLargeStruct ) );

      // Execute the task via CommandBox
      execute(
          name = "box",
          arguments = "task run taskFile=#taskPath# inputPath=#tempDataFile#",
          variable = "rawResult"
      );

      // Process the structured return
      workflowResult = deserializeJSON( rawResult );
      writeOutput( "Workflow finished at: #workflowResult.timestamp#" );

      Why Task Runners are better for workflows:

      • Portability: Tasks are self-contained in a CFC and can be dropped into any folder without being a full module.
      • Built-in Helpers: You get access to the print helper for formatted console output and filesystemUtil for easy path resolution.
      • Named Parameters: Unlike the REPL, tasks support [named parameters](https://commandbox.ortusbooks.com/task-runners/running-other-tasks), making it easy to pass file paths, IDs, or configuration flags.
      • Readability: Your code is written in a standard IDE with syntax highlighting and error checking rather than as a string inside an execute() call. [1, 3, 9, 10, 11] 

      References

      Scheduling these tasks to run automatically at specific intervals

      To schedule CommandBox Task Runners at specific intervals, you have three primary approaches depending on whether you want to use the operating system, a background process (daemon), or the ColdFusion engine itself. [1] 

      1. The Daemon Approach (Self-Scheduling)

      For workflows that need to run continuously in the background without external dependencies, you can write your task as a daemon. This involves a while(true) loop and a sleep() command within the CFC. [2, 3] 

      /**
       * ScheduledTask.cfc
       * Run: box task run taskFile=ScheduledTask.cfc
       */
      component {
          function run() {
              try {
                  while( true ) {
                      print.blueLine( "Running workflow at #now()#..." );
                      
                      // INSERT YOUR WORKFLOW LOGIC HERE
                      
                      // Sleep for 5 minutes (300,000 milliseconds)
                      sleep( 300000 );
                  }
              } catch( any e ) {
                  print.redLine( "Task interrupted: #e.message#" );
              }
          }
      }

      2. OS-Level Scheduling (Cron / Task Scheduler)

      This is the most "production-ready" method for strictly timed intervals (e.g., every day at 2 AM). You simply tell your OS to execute the CommandBox binary and your specific task file. [3, 4, 5, 6] 

      • Linux (Crontab): Add a line to your crontab using crontab -e:
        0 2 * * * /usr/local/bin/box task run /path/to/MyTask.cfc
      • Windows (Task Scheduler): Create a new task that runs the box.exe program with the arguments task run taskFile=C:\path\to\MyTask.cfc. [7, 8, 9] 

      3. CFML Engine Scheduling (cfschedule) [10] 

      If you want to manage the schedule from within your web application's admin interface or code, you can use the native <cfschedule> tag or cfschedule() function to call the CommandBox CLI. [10, 11] 

      cfschedule(
          action    = "update",
          task      = "HourlyWorkflow",
          operation = "HTTPRequest",
          url       = "http://localhost/run_my_task.cfm", // A script that runs the task
          interval  = "3600" // Every hour
      );

      Note: Your run_my_task.cfm file would then contain the execute() call to trigger the CommandBox task.

      4. Advanced: ColdBox Scheduled Tasks

      If you are using the [ColdBox Framework](https://coldbox.ortusbooks.com/digging-deeper/scheduled-tasks), you can use the built-in Async Scheduler to define tasks with a fluent, human-readable API. [1, 9, 12] 

      // inside your Scheduler.cfc
      task( "CleanTempFiles" )
          .call( function(){
              // CommandBox execution logic here
          } )
          .every( 1, "hours" )
          .delay( 5, "minutes" );

      References