Introducing Marten 0.3: Streaming responses, Caching, JSON fields, and more!

Posted by Morgan Aubert on Jun 19, 2023

Morgan Aubert's picture

We are pleased to announce the release of Marten 0.3!

New features and highlights

Support for streaming responses

It is now possible to generate streaming responses from iterators of strings easily by leveraging the Marten::HTTP::Response::Streaming class or the #respond helper method. This can be beneficial if you intend to generate lengthy responses or responses that consume excessive memory (a classic example of this is the generation of large CSV files).

Please refer to Streaming responses to learn more about this new capability.

Caching

Marten now lets you interact with a global cache store that allows interacting with an underlying cache system and performing basic operations such as fetching cached entries, writing new entries, etc. By using caching, you can save the result of expensive operations so that you don't have to perform them for every request.

The global cache can be accessed by leveraging the Marten#cache method. Here are a few examples on how to perform some basic caching operations:

# Fetching an entry from the cache.
Marten.cache.fetch("mykey", expires_in: 4.hours) do
  "myvalue"
end

# Reading from the cache.
Marten.cache.read("unknown") # => nil
Marten.cache.read("mykey") # => "myvalue"
Marten.cache.exists?("mykey") => true

# Writing to the cache.
Marten.cache.write("foo", "bar", expires_in: 10.minutes) => true

Marten's caching leverages a cache store mechanism. By default, Marten uses an in-memory cache (instance of Marten::Cache::Store::Memory) and other third-party stores can be installed depending on your caching requirements (eg. Memcached, Redis).

Marten's new caching capabilities are not only limited to its standard cache functionality. They can also be effectively utilized via the newly introduced template fragment caching feature, made possible by the cache template tag. With this feature, specific parts of your templates can now be cached with ease.

Please refer to the Caching to learn more about these new capabilities.

JSON field for models and schemas

Marten now provides the ability to define json fields in models and schemas. These fields allow you to easily persist and interact with valid JSON structures that are exposed as JSON::Any objects by default.

For example:

class MyModel < Marten::Model
  # Other fields...
  field :metadata, :json
end

MyModel.last!.metadata # => JSON::Any object

Additionally, it is also possible to specify that JSON values must be deserialized using a class that makes use of JSON::Serializable. This can be done by leveraging the serializable option in both model fields and schema fields.

For example:

class MySerializable
  include JSON::Serializable

  property a : Int32 | Nil
  property b : String | Nil
end

class MyModel < Marten::Model
  # Other fields...
  field :metadata, :json, serializable: MySerializable
end

MyModel.last!.metadata # => MySerializable object

Duration field for models and schemas

It is now possible to define duration fields in models and schemas. These allow you to easily persist valid durations (that map to Time::Span objects in Crystal) in your models but also to expect valid durations in data validated through the use of schemas.

For example:

class Recipe < Marten::Model
  field :id, :big_int, primary_key: true, auto: true
  # Other fields...
  field :fridge_time, :duration, blank: true, null: true
end

Other changes

Please head over to the official Marten 0.3 release notes for an overview of all the changes that are part of this release.