Including scripts
Modularizing your applications
UltraGen provide the keyword include
that allows you to embed other scripts. There are two ways to declare the script whose will be included. The first is passing the path of file.
include 'path/to/file.ultra'
# it can use a reference
somePath = 'path/to/otherPath.ultra'
include somePath
The script is imported at same scope of the caller. Their live stream is also added to live stream of its caller. The same applies if it's a plain text script. The plain text is sent to caller's live stream.
Warning
Be aware that because of this behavior include can cause name masking because of the names being imported at same scope.
Note
The script included is actually executed not only his names are imported. That is, if you write something in global scope, it will be executed. In case of a template, it will rendered at include time.
Note
UltraGen handles the use of slash as separator in Windows. No need to convert it replace.
Including many files
When declaring the file path and the path is not a .ultra
or .ultra.xxx
file, UltraGen try to include the path following the described sequence:
Assuming you want to include the path MyClass
UltraGen searches for MyClass.ultra file and includes it. If not found, UltraGen searches for MyClass/_init.ultra file and includes it. This is a great way to group all files from that class and use a single file to include all of them. If not found, UltraGen searches for a folder in path MyClass and include all files from root level of folder. The files are included in alphabetical order. This is not much recommended however can have its specific use cases. If none of these were found the interpreter will raise an error.
Isolating scopes
The best way to isolate scopes is to think your external script as a library and write it as a class. Different from Java or other languages the script file names have nothing to do with class names. Neither there is a helper which relate names in a script with its names.
Even templates?
Yes. It's a good practice to declare your templates as functions to enclose them and have control about when they are rendered and about passed arguments.
# template at root scope
# temp_root.ultra.html
<h1>{{ title }}</h1>
# template at function scope
# temp_func.ultra.html
@function h1(title)
<h1>{{ title }}</h1>
@end
title = 'some title'
include 'temp_root.ultra.html'
# will render the template at include
include 'temp_func.ultra.html'
# will not render
h1('some title')
# now it's rendered
If you have a group of related templates in different files you can have a structure like this
- templates
|-- UsersTemplates
|---- _init.ultra
|-- index.ultra.html
|-- show.ultra.html
And your files can be like this:
# _init.ultra
class UsersTemplates
include 'templates/UsersTemplates/index.ultra.html'
include 'templates/UsersTemplates/show.ultra.html'
# index.ultra.html
@function index() : UsersTemplates
Index of users
@end
# show.ultra.html
@function show() : UsersTemplates
Show a user
@end
And finally, to include all templates from Users.
# index.ultra
include 'templates/UsersTemplates'
Modules
UltraGen provides a convenient way to include files to you script. The modules. Including modules uses an alternative notation and depends on some configuration. There are some paths which UltraGen interpreter treats internally as modules path. Supposing you have scripts in '/myLibs/modules'. When including scripts in these paths you can write the following:
include @Path.To.File
This will be resolved to '/myLibs/modules/Path/To/File.ultra' at first. If this file doesn't exist it will search for '/myLibs/modules/Path/To/File/_init.ultra'. It's similar to the Python __init__.py
or JS index.js
. This way is good when you want to include many files of your module but not all. You can expose many files to the including script but some will be seen only internally. If these both don't exist it will search for '/myLibs/modules/Path/To/File' folder. If found, all of the scripts at root level will be included. If none of these were found the interpreter will raise an error.
As you must have noticed, the dots are replaced by bars internally. So, it's clear how to structure and reference your files.
For declaring your location as a searchable module path use the addModulePath
function.
addModulePath('/myLibs/modules')
include @Path.To.File
You can add many paths as you want but be aware that the search direction is from last to first added. This can be a problem if you have files with same relative path.
This function is available thinking of libraries or customized environments where you can have a set of modules in your applications project (something like the node_modules) and can refer to a modules repository just changing the module path add. It's suggested you don't use it for your own application paths.
The load keyword
Additionally there's a load
keyword used to add functionalities to you script. However, this is for internal types loading and has no function to any part written by developer. Conventionally, every internal type is loaded by a script and available to developer through include
. Taken @Core.Markdown
for example, this is what we have.
load Markdown
So, in your script you write include @Core.Markdown
and the internal type load action is abstracted.
While it's syntactically allowed, you should not use load
directly in your script. The reason is because the types often have more methods declared in the including file as we can see in the next example in @Core.Request
.
load Request
function postJson(url, data, headers={}) : Request
headers['Content-Type'] = 'application/json'
req = Request.post(url, JSON.create(data), headers)
return req
end
function getJson() : Response self
return JSON.parse(self.text)
end
If you write a load
directly in your script these methods wouldn't be available.
Setting up default loaded types
As said, every time you run a UltraGen script the module @Core
is included. This module is the script at build/modules/Core/_init.ultra
. It has a bunch of include statements for including other modules. If you want to make a module available to every script you execute or even drop some of the defaults just change this file. However, it's suggested you don't remove a include since as any application is supposed to rely on default release configuration of this file.