1.0.0-Beta6
July 19, 2024
BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.
New Features
BL-157 Implement nested transactions
We are excited to bring the completion of nested transactions into BoxLang, something we have wanted in an open-source engine for years.
Here, the INSERT
in the child transaction is rolled back, but the parent transaction's INSERT
statement persists.
General behavior:
A rollback on the parent transaction will roll back the child transaction.
A rollback on the child will NOT roll back the parent.
Child transaction savepoints use a prefix so they don't collide with the parent transaction.
Logging has been added, so you can easily see the transaction lifecycle as a transaction is created, savepoints are created, transactions rollback, etc.
You can't change any transaction properties on a nested transaction. i.e., once the parent transaction begins, you can't begin a child transaction and change the isolation level.
Transaction events will come in the next beta.
BL-348 queryReverse() finalized
This BIF is now complete so you can easily reverse query data.
BL-349 Allow servlet to expand paths so CommandBox web aliases or ModCFML web dirs can be used and new OnMissingMapping
event.
OnMissingMapping
event.Adobe CFML Engines do this today via their “magic” connector. Lucee has never supported it. The expanded path should work like so now:
if it doesn’t start with a
/
(and isn’t absolute), then make it relative to the base pathlook for a BoxLang mapping (not including “/” )
Check if it’s an absolute path already, like C:/foo/bar
Then announce an
ON_MISSING_MAPPING
interception pointif no interceptor provided a value, then resolve via the root
/
mapping
The servlet runtime will have a listener for ON_MISSING_MAPPING
that attempts to use servetContext.getRealPath()
to try and resolve the path. This will allow any aliases in the resource manager to kick in.
If the servlet has an alias called /foo
and you expand the path /foo/existingFile.txt
, it will see it, but if you expand the path /foo/notExistingFile.txt
then it will not “see” the mapping. We can solve that by taking off segments to see if any part of the path is found, but there is a performance hit to that, and I’m not sure if it’s necessary or not.
BL-350 Allow static functional access to BIFs
We have introduced static access to headless BIFS in BoxLang so you can use them as method references anywhere a BIF can be received, EVEN in Java classes that implement similar BIFS.
This expression represents a first-class BoxLang function that can be invoked directly.
You can also store the BIF static access into variables.
You can also leverage it as a high-order function combination from methods or classes that expect function references.
BL-351 Allow Functional binding to member functions
In order to explain this new functionality in BoxLang, here is a Java snippet:
This syntax is a shortcut for writing out the following Java lambda:
(Other JVM languages like Clojure have similar syntax) The String::toUpperCase
binds to the instance method toUpperCase
, but in a way that it will be called on each string instance in the stream. So for each iteration, it will call the toUpperCase
method on that instance. Nothing like this ever existed in CFML engines. However, in BoxLang, it does now. BoxLang allows you to do the following:
BoxLang-type member methods
Class member methods
Java member methods
Any object member/field
Since member methods in BoxLang aren’t necessarily bound to a class and we’re a dynamic language, we have simplified the syntax to this:
This is a function expression that accepts a single argument and calls the method name specified on the incoming instance, returning the result of the member call. it is a shortcut for
So our Java example becomes like this in BoxLang
When not using (), check if there is a field of that name that is not a function, and if so, return that field value. So, the following example would fetch the name key in the struct.
If the syntax .foo()
is used or there is no field of that name, then it will be assumed we’re invoking a method. It can be directly invoked:
It can be placed into a variable and invoked later:
And it can be passed to higher-order functions. (This is the main use case)
Additionally, we have added support for multiple-arguments as well:
Example:
The arguments will not be evaluated until the function is invoked, and the argument expressions will be re-evaluated for every invocation.
The argument evaluation will have lexical binding to the declaring context.
or
Bound member methods can also use named params (unless they are Java methods, of course)
This brings a whole new dimension of dynamic goodness for not only BoxLang functions but also for Java functions. Welcome to a new era!
BL-352 arrayRange() BIF to create arrays with a specific range of values
This is one of our very first steps in supporting range types. You know have the capability to generate arrays with a specific boxed range using arrayRange( from, to )
. You can use a from
and to
numeric index, and it will build the array with that many elements with the range as its value. However, you can also use range notation: {from}..{to}
BL-353 threadTerminate() finalized
We have now finalized a threadTerminate( name )
BIF.
BL-354 threadJoin() BIF Finalized
We have now finalized a threadJoin( [name], timeout )
BIF. We have expanded this BIF and if you don't pass a thread name or a list of names, it will join all active threads.
BL-355 queryInsertAt() BIF finalized
This is now finalized.
BL-356 QueryRowSwap() bif finalized
This is now finalized.
BL-358 runAsync() completed but based on the powerful new BoxFuture -> CompletableFuture
This is a big ticket. Welcome to the future with BoxFuture.
We have completed the ability to do asynchronous pipelines and parallel computations in BoxLang. We have introduced a new type called BoxFuture,
which extends a Java Completable Future. The return of a runAsync()
is a BoxFuture, and you can create pipelines to process the computations. You can access all the methods in a Completable future and many more. This will be documented in our Async Programming guide.
Futures
You have a new BIF: futureNew( [value], [executor] )
that can create new box futures. By default it will create incomplete and empty futures. However, you can pass different values to the future. Check out the API Docs: https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-beta6/ortus/boxlang/runtime/async/BoxFuture.html
value
: If passed, the value to set on the BoxFuture object as completed, or it can be a lambda/closure that will provide the value, and it will be executed asynchronously, or it can be a native Java CompletableFutureexecutor
: If passed, it will use this executor to execute the future. Otherwise, it defaults to the fork/join pool in Java.
runAsync( callback, [executor ] )
You can pass a closure or lambda to runAsync()
to execute it asynchronously. The seconds parameter is an executor
which runs the threads. It runs in the fork/join pool by default, but you can also pass your own executors. The return is a BoxFuture, which you can then do asynchronous pipelines.
BL-359 BIF Collection for managing and interacting with async service executors: list, get, has, new, shutdown
You now have the full capability to register, create, and manage custom executors in BoxLang. The AsyncService
manages all executors in BoxLang.
Here are the new functions dealing with executors:
executorGet( name )
: Get a registered executorexecutorHas( name ):
Checks if an executor has been registered or notexecutorList()
: Lists all registered executorsexecutorNew( name, type, [maxThreads:20] ):
Creates and registers an executor by type and max threads if applicable.executorShutdown( name )
: Shuts down an executorexecutorStatus( [name] )
: Get a struct of executor metadata and stats by name or all.
The available executor types are:
cached
- Creates a cached thread pool executor.fixed
- Creates a fixed thread pool executor.fork_join
- Creates a fork-join pool executor.scheduled
- Creates a scheduled thread pool executor.single
- Creates a single thread executor.virtual
- Creates a virtual thread executor.work_stealing
- Creates a work-stealing thread pool executor.
BL-360 Configuration now supports executor registration for global usage
You can now register your executors globally in BoxLang in the boxlang.json
BL-124 Implement primary/foreign key columns in dbInfo
DBInfo now returns primary and foreign key columns when you need them.
BL-255 Implement QueryMeta class for $.bx.meta or $.bx.getMeta() debugging support
Metaprogramming on queries is now available with much more information like caching, execution status, records, etc.
Improvements
BL-357 "Fix" return types of BIFs that return "true" for no real reason
One of the odd things about CFML is the following:
In BoxLang, the following BIFS returns the instance it was called with instead of a boolean. However, if you are in compat mode in a CFML file, you will get the old behavior.
arrayAppend()
arrayClear()
arrayDeleteAt()
arrayInsertAt()
arrayResize()
arraySet()
arraySwap()
StructClear()
StructKeyTranslate()
StructInsert()
structAppend()
QuerySetRow()
QueryDeleteRow()
QuerySort()
ArrayPrepend()
The following BIFs return something other than the original data structure, but they have a good reason for doing so, so they remain as is:
queryAddColumn() -- returns index of the column removed
queryAddRow() -- returns the row number added
The following BIF returns the query in Adobe, which we’ll match. Lucee returns the array of removed data, but we’re ignoring that since we agree with Adobe’s behavior.
QueryDeleteColumn()
BL-366 Provide Java FI typing on all higher-order BIFs
BL-371 make name un-required in the application component
BL-346 DynamicObject equals() hashCode() to allow for dynamic checking
Bugs
BL-364 Overridden getter in parent class not being used
BL-370 List and Delmiter Args are not correct in ListReduce
callback
BL-365 The throwable Dump table is not styled
Last updated