Customize your Runtime Creating your first BoxLang Module.pdf

ortussolutions 9 views 29 slides May 14, 2025
Slide 1
Slide 1 of 29
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29

About This Presentation

Led by Brad Wood

Your runtime. Your way. Endless possibilities! In this session we’ll teach you how to customize your runtime, contribute built-in-functions and tags/components to your BoxLang runtime. Intimidated by Java? Don’t be! We will show you how to create your very first BoxLang runtime...


Slide Content

WELCOME
Into the Box 2025: The Future is Dynamic!

www.intothebox.org
GET STARTED
Creating your first BoxLang Module

Customize your Runtime

Brad Wood
•Lead developer of CommandBox CLI
•BoxLang Framework Architect
•I’ve made 9,722 commits on GitHub since 2011
Senior Software Architect
[email protected] @bdw429s

●BoxLang’s entire runtime is modular and extensible
●This was the case from day one of this project
●We have a ModuleService which is very similar to
ColdBox/CommandBox/ContentBox, but at the runtime level
●The BoxLang core allows anything to be dynamically registered
●Modules can be written entirely in Java, in BoxLang, or in a mix of both. You choose!
●Ortus has written dozens of “official” modules
●You can write your own private or public modules
●Publish your modules directly to ForgeBox for the world to use
BoxLang Modules

●BIFs (Built In Functions)
●Components (formerly known as tags)
●Classes
●Interceptors (listening to core BL runtime event announcements)
●Custom Components (AKA Custom Tags)
●Jars
●Configuration
●Caches
●JDBC Drivers
●Custom Contexts/scopes
BoxLang Modules

●Default locations
○boxlang_modules/ folder in the working directory
○modules/ folder inside the BoxLang home folder
●You can configure additional folders in the boxlang.json file
●You can configure additional folders via env variable
Module Locations

●A folder
●A ModuleConfig.bx class
○configure()
■settings
■interceptors
○onLoad()
○onUnload()
●bifs/ folder
●components/ folder
●libs/ folder
Module Structure
The only requirement is the
ModuleConfig.bx
Everything else is optional!

●A folder
●A ModuleConfig.bx class
○configure()
■settings
■interceptors
○onLoad()
○onUnload()
●bifs/ folder
●components/ folder
●libs/ folder
Module Structure

●configure() method
○Runs when module is registered, but before it’s activated. Responsible for
creating settings structure which defines default settings for the module.
The user can override these in their BoxLang config!
●onLoad() method
○Runs when the module activates and can register additional items with
the runtime, or setup anything the module needs
●onUnload() method
○Runs when the module is unloaded (or the runtime shuts down). Clean up
and unregister any custom items.
ModuleConfig.bx

function configure() {
variables.settings = {
mySetting= "value",
anotherSetting= "another value"
}
}
Module Settings
ModuleConfig .bx

{
"modules": {
"myModule": {
"settings": {
"mySetting": "override value"
}
}
}
}
Module Settings Override
boxlang.json

function onLoad() {
boxRuntime.getConfiguration().registerMapping( "/foobar", "C:/foo/bar");
println( "This module is LOADED!" )
}
Module onLoad
ModuleConfig.bx

●Every module gets an automatic class mapping created that points to the
root of the module
●/bxModules/moduleName/
●Customize the mapping name in your ModuleConfig.bx
this.mapping = "waffle"
●You can access files inside the module
fileRead( "/bxModules/myModule/resources/settings.json" )
●You can create classes from inside the module
new bxModules.myModule.models.FooService()

Automatic Mapping

●Each module has a dedicated class loader, pointed at the lib/ folder.
●This is for 3rd party jars, or even custom Java classes you write
●Each module is an “island” so they won’t cloud the system
classloader with potentially different versions of the same library
●There is a namespace registered for Java class paths

new com.foo.JavaClass@myModule()

Module Classloader

There is not a specific convention for custom tags, but since there is a
mapping created that points to the root of the module and custom tags
are searched for in any mapping, you can just place a .bxm or .bxs
folder in the root of your module and it will be usable as a custom
component/tag.
Module Custom Components

●BIFs -> Built In Functions
●First-class headless global functions like now() or fileRead()
●You can OVERWRITE core BIFs. Be careful! With great power comes
great responsibility.
●Add bx class to bifs/ folder
●Name of class is name of BIF
●Needs @BoxBIF annotation
●invoke() method runs it
●BIF arguments are passed along to the invoke() method
Register BIFs

import ortus.boxlang.runtime.types.DateTime;

@BoxBIF
class {

function invoke(){
return new DateTime();
}

}
BIF Example
bifs/Now.bx

●Create one alias for the BIF with
@BoxBIF( "nowButCooler" )
●Create many aliases with
@BoxBIF( [
"nowButCooler",
"now2point0",
"nowBrad"
] )
BIF Aliases

●Register BIF as member method on a type
@BoxMember( "array" )
●If the BIF name starts with the type name, then the
member method removes that text from the name
arrayAppend() becomes just myArr.append()
○Get fancy
@BoxMember( { "string": { "name": "append", "objectArgument": "string" } })
BIF Member Methods

@BoxBIF
@BoxMember( "array" )
class {

function invoke( Array arr, string str ){
arr.append( str.ucase() );
return arr;
}

}
BIF Member Example
bifs/ArrayAppendUcase.bx

myArr = [];

arrayAppendUcase( myArr, "brad" )

myArr.appendUcase( "brad" )
BIF Member Example

●Components -> used to be called tags
●Components have both a template and a script sytnax
●First-class global like bx:http or bx:mail
●You can OVERWRITE core components. Be careful!
●Add bx class to components/ folder
●Name of class is name of component
●Needs @BoxComponent annotation
●invoke() method runs it
Register Components (tags)

Component Annotations
@BoxComponent
@BoxComponent( "MyAlias" )
@BoxComponent( "MyAlias", "AnotherAlias" )

@AllowsBody( false )

@RequiresBody( true )

●invoke() arguments
○context - The BoxLang Context
○Struct attributes - The attributes passed by the user
○body - A function that represents the body of the component
○Struct executionState - Data about execution
Component invoke() method

@BoxComponent
@AllowsBody( false )
class {

function invoke( context, attributes, body, executionState ){
echo( "hello " & attriutes.name ?: "world" );
}

}
Component Example
components/SayHello.bx

<!--- Templating --->
<bx:sayHello name="Brad">

// Scripting
bx:sayHello name="Brad";
Component Example

@BoxComponent
@RequiresBody( true )
class {
function invoke( context, attributes, body, executionState ){
var buffer = newBuffer();
var bodyResult = processBody( context, body, buffer );
// handle return, break, or continue
if ( bodyResult.isEarlyExit() ) {
return bodyResult;
}
echo( buffer.toString().Ucase() );
}
}
Component Example 2
components/Scream.bx

<!--- Templating --->
<bx:scream>
I like spam!
</bx:scream>

// Scripting
bx:sayHello {
echo( "I like spam!" );
}
Component Example 2

Thank You!
The Future of Modern Development Starts Here, with you! ??????