We're thrilled to announce the release of BoxLang 1.0.0-Beta2! This beta introduces a plethora of exciting new features and bug fixes, including encryption functionality, improved Java interoperability, and streamlined event handling. We are also excited to announce that these betas will be incrementing weekly until we reach our stable version in the coming months. So let's dive in!
What is BoxLang?
BoxLang is a modern dynamic JVM language that can be deployed on multiple runtimes: operating system (Windows/Mac/*nix/Embedded), web server, lambda, iOS, android, web assembly, and more. BoxLang combines many features from different programming languages, including Java, ColdFusion, Python, Ruby, Go, and PHP, to provide developers with a modern and expressive syntax.
How to get started?
Visit our docs at https://boxlang.ortusbooks.com and get coding today. If you want to try it out on the web then go to our online REPL at https://try.boxlang.io
Release Notes
Here are the beta 2 release notes
Bugs
BL-140 Writedump
expanded collapsed support
BL-141 Writedump
top
support
BL-193 listdeleteAt
returns a list with multiple delimiters as a list with whole delimiters
BL-231 StructNew
with localeSensitive
flag throws error
BL-235 structKeyTranslate
returns void
BL-241 StructGet
does not create struct when missing
BL-245 StructFindValue
returning null owner
BL-246 no named applications not auto creating name
BL-247 application listener requests interception points not registered
BL-248 ambiguous if statements when not using curly braces
BL-249 this.javasettings
not expanding / to correct pathing
BL-250 this.javasettings
ignores paths to actual jars and classes
BL-257 cfdirectory
fails on centos, converting datetime
BL-263 dateAdd
() modifies its argument!
BL-265 `toString
` not formatting doubles correctly
BL-266 Attempt to cast instead of expecting strings inside `isValid
`
BL-270 Regression on JSON serialization of box classes with JSON exclude annotations
New Features
BL-128 Encryption module
We have created the bx-password-encrypt
module so you can use it for password encryption. Find out much more here: https://forgebox.io/view/bx-password-encrypt
This will collaborate several new BIFs and components:
ArgonHash
: Returns a secure input hash of the given string using the Argon2 hashing algorithm. ( Alias:GenerateArgon2Hash
)ArgonVerify
: Performs a Argon2 verification on the given string against the hashed value. ( Alias:Argon2CheckHash
)BCryptHash
: Returns a secure input hash of the given string using the BCrypt hashing algorithm.( Alias:GenerateBCryptHash
)BCryptVerify
: Performs a BCrypt verification on the given string against the hashed value. ( Alias:BCryptCheckHash
)SCryptHash
: Returns a secure input hash of the given string using the SCrypt hashing algorithm.( Alias:GenerateSCryptHash
)SCryptVerify
: Performs a SCrypt verification on the given string against the hashed value. ( Alias:SCryptCheckHash
)GeneratePBKDFKey
: Generates a PDFK key from the given password and salt.
BL-251 New event: ON_REQUEST_FLUSH_BUFFER
You can now listen to when the engine flushes the output buffer and intercepts it. This will allow you to collaborate content to the buffer before it's sent to the output destination. The data received is:
context
- The execution contextoutput
- The string output to send to the buffer. This can be text or HTML
BL-258 Ability to coerce BoxLang functions, lambdas, and UDFs, to well-known functional interfaces for Java interop
This is one of our biggest tickets for this release to continue to close the Java interop cycle in BoxLang. This will allow BoxLang lambdas/closures/udfs to be coerced to Java Functional Interfaces at runtime. This means that ANY Java library that offers functional interfaces (lambdas) can be used natively in BoxLang. This means that using streams, completable futures, and Java lambdas are now native to BoxLang.
fruits = [ "apple", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn" ];
result = fruits
.parallelStream()
.filter( fruit -> fruit.startsWith( "a" ) )
.toList();
// Call a Java class that accepts a Runnable lambda with a BoxLang lambda
myJavaclass.runAsync( () -> "Hello from BoxLang" )
// Call a Java class that accepts a Runnable lambda with a BoxLang closure
myJavaclass.runAsync( () => processMyClosureData() )
This can also be used to tap into high concurrency constructs in Java. You can combine the usage of BoxLang pure functions (lambdas) or context-aware closures.
import java.util.concurrent.CompletableFuture
// Build out an async BoxLang task using native Java Interop
function main( args = [] ) {
// Define closures to fetch data from APIs
// (can be replaced with actual API calls)
data1Future = CompletableFuture.supplyAsync( () => simulateApiCall("API 1") )
data2Future = CompletableFuture.supplyAsync( () => simulateApiCall("API 2") )
// Combine futures (waits for both to complete)
// With a BoxLang lambda
data1Future.thenAcceptBoth( data2Future, (data1, data2) -> {
println("Data from API 1: " + data1);
println("Data from API 2: " + data2);
println("Combined data: " + data1 + " " + data2);
});
// Wait for all futures to complete
CompletableFuture.allOf( data1Future, data2Future ).get();
}
private static simulateApiCall( apiName ) {
try {
sleep(1000); // Simulate API call delay
println("Fetching data from " + apiName);
return "Data from #apiName#"
} catch (InterruptedException e) {
println( e )
}
}
BL-260 Add parallel streams from BoxLang arrays
All BoxLang arrays have native stream support, and you get native parallel support as well.
result = fruits
.parallelStream()
.filter( fruit -> fruit.startsWith( "a" ) )
.toList();
BL-264 Truthy / Falsey completion for boolean caster
Our truthy/false evaluations for arrays, structs, queries, Java lists, and collections are complete.
BL-268 New Fluent Attempt bif and class
This is inspired by Java Optionals for BoxLang. We have introduced a new class called Attempt()
, which allows you to create fluent and functional code to work with values or delay attempts at code execution. Then, you can interact with the result of your attempt using our functional methods.
Another important aspect of attempts is that they evaluate that the seeded value is not null
but also truthy. This means you can use it to evaluate that the value is truthy or false, not only null. You can also pass in a closure/lambda to be the value and once you request to evaluate the result, it will do it asynchronously and delayed.
attempt( userService.get( rc.id ).isLoaded() )
.ifPresent( user -> populate( user ).save() )
.orThrow( "UserNotFoundException" )
// A delayed attempt
userDataAttempt = attempt( () -> userData.getData() )
.toMatchRegex( "^myRegex" )
....
return userDataAttempt
.orElse( "" )
Here are the current functions available to you in this beta. There are more coming to make it more fluent.
{% embed url="https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-beta2/ortus/boxlang/runtime/dynamic/Attempt.html" %}
get():any
- Get the value or throw an exception if null or falseyempty():Attempt
- Produce a new empty attemptof( value ):Attempt
- Produce a new attempt with the passed valueofFunction( context, function/closure/lambda ):Attempt
- Produce a new attempt with a closure or lambda.isEmpty():boolean
- Is the value falsey or nullisPresent():boolean
- Is the value presentifPresent( consumer ):Attempt
- Call the consumer lambda/closure if the value is presentifPresentOrElse( consumer, action ):Attempt
- Call the consumer lambda/closure if present or the action lambda/closure if not.ifEmpty( consumer ):Attempt
- Call the consumer if the value is not presentor( supplier ):Attempt
- If the value is present it returns itself, if not, it calls the supplier closure/lambda to produce a new attempt.orElse( other ):any
- Get the value if present, or otherwise return the other value passedorElseGet( supplier ):any
- Get the value if present, otherwise call the supplier closure/lambda to produce the value you want to return.map( mapper ): Attempt
- Maps the value of the attempt if it exists, else returns the same attempt with no value.filter( predicate )
- If the value is present it will call your predicate closure/lambda so you can run tests on the value. If the return is true it will return the same attempt, else an empty attempt.orThrow():any
- Returns the value if it exists or throws an exceptionorThrow( message ):any
- Returns the value if it exists or throws an exception with your custom messageorThrow( exception ):any
- Returns the value if it exists or throws your custom exceptionstream():Stream
- If the value exists returns a stream with the value else an empty streamtoString():String
- Gives you a string representation of the valueisValid():Boolean
- If the value is present it will try to validate it with the registered validation schemas, if any.toBeValid( closure or lambda ):Attempt
- This allows you to register a lambda/closure to validate the data if any. When callingisValid()
it will call this function if registered.toBeBetween( min, max ):Attempt
- This allows you to register a min and max numerical values to test the value. It must be in range to be valid.toMatchRegex( regex ):Attempt
- This allows you to register a regular expression to match against the value.
BL-269 Add the ability to add member methods to BoxLang classes
This was something we always wanted to do. This allows module and BoxLang developers to contribute member methods to ANY BoxLang class. So now, you can serialize any Class to JSON natively using our BoxLang Class to JSON native serialization. Let's build a Person
class:
/**
* My Person
*/
@jsonExclude "anotherprop, anotherProp2"
class Person{
property String name;
property String surname;
property numeric age;
property Date createdDate;
property Date modifiedDate;
property boolean isActive;
property Array tags;
@jsonExclude
property any javaSystem;
property anotherProp;
property anotherProp2;
function init(){
variables.name = "John";
variables.surname = "Doe";
variables.age = 30;
variables.createdDate = now();
variables.modifiedDate = now();
variables.isActive = true;
variables.tags = ["tag1", "tag2"];
variables.test = CreateObject( "java", "java.lang.System" );
variables.anotherProp = "hello";
variables.anotherProp2 = "hello";
return this;
}
function sayHello(){
return "Hello " & variables.name;
}
}
As you can see, we have introduced a few annotations for JSON serialization based on our experience with the ColdBox mementifier project. You can tag properties to be excluded from serialization using the jsonExclude
annotation. You can exclude a list of properties from the class annotation as well. Now, let's get some JSON data out using the toJSON()
member function, which delegates it to the jsonSerialize()
bif.
function main( args={} ){
return new Person()
.setName( "Luis" )
.setSurname( "Majano" )
.toJSON()
}
This will allow framework developers to collaborate with first-class methods in any BoxLang class.
BL-271 new static helper on Array class: `fromString( list, delimiter )` to create quick BoxLang arrays from strings
This is an internal convenience method for creating BoxLang arrays from strings.
BL-272 new BIFS for registered interceptors into the request pool and the global pool: BoxRegisterREquestInterceptor, BoxRegisterInterceptor
BoxLang is an event-driven language. It announces tons of events during the software life cycle. You can now listen to any global event via the new BoxRegisterInterceptor()
bif. This will allow you to register classes, lambdas, or closures.
boxRegisterInterceptor( ()=> listenToRequestStarts(), "onServerScopeCreation" )
However, BoxLang also offers interceptors at the request level via the internal application listener. This means that you can listen to a specific request life-cycle by using the boxRegisterRequestInterceptor()
bif.
boxRegisterRequestInterceptor( ()=> listenToRequestStarts(), "onRequestStart" )
BL-274 writedump abort support
More work towards compatibility is completed.
BL-275 writeoutput on complex BoxLang types should call the `toString()` on it
This has been a heached in current CFML engines. We now detect what you send in to the writeOutput() or echo()
commands and we will convert them to string if they are complex objects or classes.
fruits = [ "apple", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn" ];BL-277 implements BIFs GenerateSecretKey, Encrypt, Decrypt
writeoutput( fruits )
writeOutput( server )
Native Encrypt, Decrypt and GenerateSecretKey()
We now support all encryption and decryption algorithms natively in BoxLang without ANY third-party library. Secure by default and with 3 BIFS created for this. Supported algorithms:
AES
ARCFOUR
Blowfish
ChaCha20
DES
DESede
HmacMD5
HmacSHA1
HmacSHA224
HmacSHA256
HmacSHA384
HmacSHA512
HmacSHA3-224
HmacSHA3-256
HmacSHA3-384
HmacSHA3-512
Add Your Comment