Marten 0.6: Localized routes, annotations, array schema field, and more!

Posted by Morgan Aubert on Aug 3, 2025

Morgan Aubert's picture

We are pleased to announce the release of Marten 0.6!

Marten 0.6 brings significant enhancements, including localized routes, annotations, array schema fields, and image fields for models and schemas. These features make building apps with Marten smoother and more flexible. For a detailed overview of all new features and changes, check out the full changelog.

New features and highlights

Localized routes

Marten now provides the ability to define localized routes through the use of two mechanisms: automatically adding locale prefixes to routes and activating the appropriate locale based on the prefix, and translating the routes themselves. These mechanisms can be used independently or in combination.

For example, the following routes map defines routes that will be prefixed by the currently activated locale and whose paths will be translated using the considered project's translations:

ARTICLE_ROUTES = Marten::Routing::Map.draw do
  path t("routes.articles.list"), ArticlesHandler, name: "list"
  path t("routes.articles.create"), ArticleCreateHandler, name: "create"
  path t("routes.articles.detail"), ArticleDetailHandler, name: "detail"
  path t("routes.articles.update"), ArticleUpdateHandler, name: "update"
  path t("routes.articles.delete"), ArticleDeleteHandler, name: "delete"
end

Marten.routes.draw do
  localized do
    path t("routes.landing"), LandingPageHandler, name: "landing"
    path t("routes.articles.prefix"), ARTICLE_ROUTES, name: "articles"
  end
end

As highlighted above, the use of routes prefixed with locales can be activated by wrapping route paths by a call to the #localized method. Route path translations can be defined using the #t method, which assigns a translation key to each route (this key is then dynamically used to generate the route's path based on the active locale).

With the routes map defined above, generated routes are fully localized and vary based on the currently activated locale:


I18n.activate("en")
Marten.routes.reverse("landing")         # => "/en/landing"
Marten.routes.reverse("articles:create") # => "/en/articles/create"

I18n.activate("fr")
Marten.routes.reverse("landing")         # => "/fr/accueil"
Marten.routes.reverse("articles:create") # => "/fr/articles/creer"

Please refer to the Localized routes section of the documentation to learn more about this new capability.

Annotations

Marten now lets you annotate query sets with aggregated data. This is useful when you need to "retain" aggregated values for each record in a query set (possibly for further filtering or for making use of the aggregated values when dealing with individual records). This can be achieved by using the #annotate method.

For example:

# Annotate the query set with the number of articles for each author.
Author.all.annotate { count(:articles) }

# Get all the authors that have more than 10 articles.
Author.all.annotate { count(:articles) }.where(articles_count__gt: 10)

# Order all the authors by the number of articles they have.
Author.all.annotate { count(:articles) }.order(:articles_count)

# Access the annotated values for each author.
authors = Author.all.annotate { count(:articles) }
authors.each do |author|
  puts author.name
  puts author.annotations["articles_count"]
end

Please refer to the Aggregations section of the documentation to learn more about this new capability.

Array schema field

Marten now lets you define array schema fields that allow validating lists of values, with each value subject to the validation rules of an array member field (such as string, int, or any other existing schema field type).

For example, the following schema allows validating lists of string values whose sizes must not be greater than 10:

class TestSchema < Marten::Schema
  field :values, of: :string, max_size: 10
end

As highlighted by the above example, the type of the underlying array member field must be specified through the use of an of option, which should reference an existing schema field type (such as string, enum, etc).

Please refer to the schema field reference to learn more about array fields.

Image fields for models and schemas

It is now possible to define image fields in models and schemas, which allow you to store or validate files that are indeed images. This capability requires the use of the crystal-vips shard.

For example:

class ImageAttachment < Marten::Model
  field :id, :big_int, primary_key: true, auto: true
  field :uploaded_file, :image, blank: false, null: false
end

attachment = ImageAttachment.first!
attachment.uploaded_file            # => #<Marten::DB::Field::File::File:0x102dd0ac0 ...>
attachment.uploaded_file.attached?  # => true
attachment.uploaded_file.name       # => "test.png"
attachment.uploaded_file.size       # => 5796929
attachment.uploaded_file.url        # => "/media/test.png"

Other changes

Marten 0.6 also includes lots of additional changes and quality-of-life improvements. Head over to the official Marten 0.6 release notes for a full overview of everything in this release.