Skip to main content
Version: Next

Applications

Marten projects can be organized into logical and reusable components called "applications". These applications can contribute specific behaviors and abstractions to a project, including models, handlers, schemas, emails, and templates. They can be packaged and reused across various projects as well.

Overview

A Marten application is a set of abstractions (defined under a dedicated and unique folder) that provides some set of features. These abstractions can correspond to models, handlers, templates, schemas, emails, etc.

Marten projects always use one or many applications. Indeed, each Marten project comes with a default main application that corresponds to the standard src folder: models, migrations, or other classes defined in this folder are associated with the main application by default (unless they are part of another explicitly defined application). As projects grow in size and scope, it is generally encouraged to start thinking in terms of applications and how to split models, handlers, or features across multiple apps depending on their intended responsibilities.

Another benefit of applications is that they can be packaged and reused across multiple projects. This allows third-party libraries and shards to easily contribute models, migrations, handlers, or templates to other projects.

Using applications

The use of applications must be manually enabled within projects: this is done through the use of the installed_apps setting.

This setting corresponds to an array of installed app classes. Indeed, each Marten application must define a subclass of Marten::App to specify a few things such as the application label (see Creating applications for more information about this). When those subclasses are specified in the installed_apps setting, the applications' models, migrations, assets, and templates will be made available to the considered project.

For example:

Marten.configure do |config|
config.installed_apps = [
FooApp,
BarApp,
]
end

Adding an application class inside this array will have the following impact on the considered project:

  • the models of this application and the associated migrations will be used
  • the templates of the application will be made available to the templates engine
  • the assets of the application will be made available to the assets engine
  • the management commands defined by the application will be made available to the Marten CLI

The main application

The "main" application is a default application that is always implicitly used by Marten projects (which means that it does not appear in the installed_apps setting). This application is associated with the standard src folder: this means that models, migrations, assets, or templates defined in this folder will be associated with the main application by default. For example, models defined under a src/models folder would be associated with the main application.

info

The main application is associated with the main label. This means that models of the main application that do not define an explicit table name will have table names starting with main_.

It should be noted that it is possible to create explicitly defined applications whose structures live under the src folder as well: the abstractions (eg. models, migrations, etc) of these applications will be associated with them and not with the main application. This is because abstractions are always associated with the closest application in the files/folders structure.

In the end, the main application provides a convenient way for starting projects and prototyping without requiring to spec out how projects will be organized in terms of applications upfront. That being said, as projects grow in size and scope, it is really encouraged to start thinking in terms of applications and how to split abstractions and features across multiple apps depending on their intended responsibilities.

Order of installed applications

You should note that the order in which installed applications are defined in the installed_apps setting can actually matter.

For example, a "foo" app might define a test.html template, and a similar template with the exact same name might be defined by a "bar" app. If the "foo" app appears before the "bar" app in the array of installed apps, then requesting and rendering the test.html template will actually involve the "foo" app's template only. This is because template loaders associated with app directories iterate over applications in the order in which they are defined in the installed apps array.

This is why it is always important to namespace abstractions, assets, templates, and locales when creating applications. Failing to do so exposes apps to conflicts with other applications' code. As such, in the previous example, the "foo" app should've defined a foo/test.html template while the "bar" app should've defined a bar/test.html template to avoid possible conflicts.

Creating applications

Creating applications can be done very easily through the use of the app generator. For example:

marten gen app blog

Running such a command will add a new blog application to the current project with the following structure:

src/blog
├── emails
├── handlers
├── migrations
├── models
├── schemas
├── templates
├── app.cr
└── cli.cr

These files and folders are described below:

PathDescription
emails/Empty directory where the emails of the application will be defined.
handlers/Empty directory where the request handlers of the application will be defined.
migrations/Empty directory that will store the migrations that will be generated for the models of the application.
models/Empty directory where the models of the application will be defined.
schemas/Empty directory where the schemas of the application will be defined.
templates/Empty directory where the templates of the application will be defined.
app.crDefinition of the application configuration abstraction; this is also where application-specific file requirements should be made.
cli.crRequirements of CLI-related files, such as migrations for example.
routes.crModule containing the routes of the application.
tip

The app generator automatically ensures that:

  • The newly created application is added to the installed_apps setting.
  • Requirements for the application itself are added to the src/project.cr and src/cli.cr files.
  • The application's routes are included in the main routes map (which lives in the config/routes.cr file).

The most important file of an application is the app.cr one. This file usually includes all the app requirements and defines the application configuration class itself, which must be a subclass of the Marten::App abstract class. This class allows mainly to define the "label" identifier of the application (through the use of the #label class method): this identifier must be unique across all the installed applications of a project and is used to generate things like model table names or migration classes.

Here is an example app.cr file content for a hypothetic "blog" app:

require "./emails/**"
require "./handlers/**"
require "./models/**"
require "./routes"
require "./schemas/**"

module Blog
class App < Marten::App
label "blog"
end
end
info

Where the app.cr file is located is important: the directory where this file is defined is also the directory where key folders like models, migrations, templates, etc, must be present. This is necessary to ensure that these files and abstractions are associated with the considered app.

Another very important file is the cli.cr one: this file is there to define all the CLI-related requirements and will usually be required directly by your project's manage.cr file. A minima the cli.cr file should require model migrations, but it could also require the management commands provided by the application. For example:

require "./cli/**"
require "./migrations/**"

Defining settings for applications

Applications that you create as part of your projects or third-party libraries can have their own associated settings, configurable through the use of regular settings files.

In order to define settings for your applications, the simplest way is to create a settings.cr file containing a subclass of Marten::Conf::Settings in your application's folder. This subclass must make use of the #namespace macro in order to define the setting "namespace" under which your application's settings will be accessible.

For example:

module Blog
class Settings < Marten::Conf::Settings
namespace :blog

@my_setting : String = "foo"

getter my_setting
setter my_setting
end
end

With the above example, it will be possible to configure the blog.my_setting setting as follows in a project's settings file:

Marten.configure do |config|
config.blog.my_setting = "bar"
end

As you can see, the application's settings are configurable like any other built-in settings, but they are namespaced to the namespace value that was defined in the Blog::Settings class through the use of the #namespace macro.

It's important to note that Marten::Conf::Settings subclasses have the flexibility to define any necessary methods to facilitate user configuration for the considered application. While basic settings typically necessitate only getters and setters for configuration, more intricate scenarios may demand additional methods, the utilization of blocks, or other complexities.