Releases: martenframework/marten
0.4.0
Marten 0.4.0 brings substantial enhancements to the framework, including the addition of generators, multi-table inheritance, schema handler callbacks, and more!
Requirements and compatibility
Crystal 1.9, 1.10, and 1.11.
New features
Generators
Marten now provides a generator mechanism that makes it easy to create various abstractions, files, and structures within an existing project. This feature is available through the use of the gen
management command and facilitates the generation of key components such as models, schemas, emails, or applications. The authentication application can now also be added easily to existing projects through the use of generators. By leveraging generators, developers can improve their workflow and speed up the development of their Marten projects while following best practices.
Below are highlighted some examples illustrating the use of the gen
management command:
# Generate a model in the admin app:
marten gen model User name:string email:string --app admin
# Generate a new TestEmail email in the blog application:
marten gen email TestEmail --app blog
# Add a new 'blogging' application to the current project:
marten gen app blogging
# Add the authentication application to the current project:
margen gen auth
You can read more about the generator mechanism in the dedicated documentation. All the available generators are also listed in the generators reference.
Multi table inheritance
It is now possible to define models that inherit from other concrete models (ie. non-abstract models). In this situation, each model can be used/queried individually and has its own associated database table. The framework automatically defines a set of "links" between each model that uses multi table inheritance and its parent models in order to ensure that the relational structure and inheritance hierarchy are maintained.
For example:
class Person < Marten::Model
field :id, :big_int, primary_key: true, auto: true
field :first_name, :string, max_size: 100
field :last_name, :string, max_size: 100
end
class Employee < Person
field :company_name, :string, max_size: 100
end
employee = Employee.filter(first_name: "John").first!
employee.first_name # => "John"
All the fields defined in the Person
model can be accessed when interacting with records of the Employee
model (despite the fact that the data itself is stored in distinct tables).
You can read more about this new kind of model inheritance in Multi table inheritance.
Schema handler callbacks
Handlers that inherit from the base schema handler - Marten::Handlers::Schema
- or one of its subclasses (such as Marten::Handlers::RecordCreate
or Marten::Handlers::RecordUpdate
) can now define new kinds of callbacks that allow to easily manipulate the considered schema instance and to define logic to execute before the schema is validated or after (eg. when the schema validation is successful or failed):
before_schema_validation
after_schema_validation
after_successful_schema_validation
after_failed_schema_validation
For example, the after_successful_schema_validation
callback can be used to create a flash message after a schema has been successfully validated:
class ArticleCreateHandler < Marten::Handlers::Schema
success_route_name "home"
template_name "articles/create.html"
schema ArticleSchema
after_successful_schema_validation :generate_success_flash_message
private def generate_success_flash_message : Nil
flash[:notice] = "Article successfully created!"
end
end
Please head over to Schema handler callbacks to learn more about these new types of callbacks.
URL field for models and schemas
It is now possible to define url
fields in models and schemas. These allow you to easily persist valid URLs in your models but also to expect valid URL values in data validated through the use of schemas.
For example:
class User < Marten::Model
field :id, :big_int, primary_key: true, auto: true
field :website_url, :url, blank: true, null: true
end
Slug field for models and schemas
It is now possible to define slug
fields in models and schemas. These allow you to easily persist valid slug values (ie. strings that can only include characters, numbers, dashes, and underscores) in your models but also to expect such values in data validated through the use of schemas.
For example:
class User < Marten::Model
field :id, :big_int, primary_key: true, auto: true
field :username, :slug
end
Minor features
Models and databases
- Support for removing records from many-to-many fields was added and many-to-many field query sets now provide a
#remove
helper method allowing to easily remove specific records from a specific relation. You can learn more about this capability in Many-to-many relationships. - Support for clearing all the references to records targeted by many-to-many fields was added. Indeed, many-to-many field query sets now provide a
#clear
method allowing to easily clear a specific relation. You can learn more about this capability in Many-to-many relationships. - It is now possible to specify arrays of records to add or remove from a many-to-many relationship query set, through the use of the
#add
and#remove
methods. See the related documentation to learn more about interacting with records targeted by many-to-many relationships. - Records targeted by reverse relations that are contributed to models by
one_to_one
(ie. when using therelated
option) are now memoized when the corresponding methods are called on related model instances. - Relation fields that contribute methods that return query sets to models (such as
many_to_one
ormany_to_many
fields) now make sure that those query set objects are memoized at the record level. The corresponding instance variables are also reset when the considered records are reloaded. This allows to limit the number of queries involved when iterating multiple times over the records targeted by amany_to_many
field for example. - The
#order
query set method can now be called directly on model classes to allow retrieving all the records of the considered model in a specific order. - A
#pk?
model method can now be leveraged to determine if a primary key value is set on a given model record. - The
#join
query set method now makes it possible to pre-select one-to-one reverse relations. This essentially ...
0.3.4
0.3.3
Marten 0.3.3 fixes a couple of bugs.
Bug fixes
- Fix unexpected
Marten::Template::Errors::InvalidSyntax
exceptions raised when adding spaces between a template filter name and its argument. - Make sure that the
#add
method of many-to-many query sets honors the targeted DB alias. - Make sure that the
#delete
and#update
query set methods reset cached records if applicable. - Make sure that the
#add
method of many-to-many query sets resets cached records if applicable.
0.3.2
Marten 0.3.2 fixes a couple of bugs.
Bug fixes
- Fix possible inconsistencies in results returned by query sets based on the order of calls to
#filter
and#exclude
. - Fix invalid through model generation for recursive many-to-many relationships.
- Ensure that
#<field>?
model methods return false for empty field values. - Add missing
#<field>?
method forfile
model fields.
0.3.1
Marten 0.3.1 fixes a couple of bugs.
Bug fixes
- Ensure that context objects provided by generic handlers are initialized using
Marten::Template::Context
. - Fix a possible compilation error happening around template variables initialization.
- Ensure that
#<field>?
schema methods returnfalse
for empty field values.
0.3.0
Marten 0.3.0 brings substantial enhancements to the framework, including the addition of streaming capabilities, caching, JSON fields, duration fields, and more.
Requirements and compatibility
Crystal 1.6, 1.7, and 1.8.
New features
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
Minor features
Models and databases
- New
#get_or_create
/#get_or_create!
methods were added to query sets in order to allow easily retrieving a model record matching a given set of filters or creating a new one if no record is found. string
fields now support amin_size
option allowing to validate the minimum size of persisted string field values.- A new
#includes?
method was added to query sets in order easily perform membership checks without loading the entire list of records targeted by a given query set. - Alternative
#exists?
methods were added to query sets in order to allow specifying additional filters to use as part of existence checks. - An
#any?
method was added to query sets in order to short-circuit the default implementation ofEnumerable#any?
and to avoid loading the full list of records in memory (when called without arguments). This overridden method is technically an alias of#exists?
. - Marten migrations are now optimized to prevent possible issues with circular dependencies within added or deleted tables
- It is now possible to define arbitrary database options by using the new
db.options
database setting. - It is now possible to define
many_to_one
andone_to_one
fields that target models with non-integer primary key fields (such as UUID fields for example).
Handlers and HTTP
- The
Marten::Handlers::RecordList
generic record now provides the ability to specify a custom query set instead of a model class. This can be achieved through the use of the#queryset
macro. - A new
Marten::Middleware::AssetServing
middleware was introduced to make it easy to serve collected assets in situations where it is not possible to easily configure a web server (such as Nginx) or a third-party service (like Amazon's S3 or GCS) to serve assets directly. - A new
Marten::Middleware::SSLRedirect
middleware was introduced to allow redirecting non-HTTPS requests to HTTPS easily. - A new
Marten::Middleware::ContentSecurityPolicy
middleware was introduced to ensure the presence of the Content-Security-Policy header in the response's headers. Please refer to Content Security Policy to learn more about the Content-Security-Policy header and how to configure it. - The
Marten::Middleware::I18n
middleware can now automatically determine the current locale based on the value of a cookie whose name can be configured with thei18n.locale_cookie_name
setting. - The
Marten::Middleware::I18n
middleware now automatically sets the Content-Language header based on the activated locale.
Templates
- A
join
template filter was introduced to allow converting enumerable template values...
0.2.4
Marten 0.2.4 fixes a couple of bugs.
Bug fixes
- Fix possible
KeyError
exception raised when the use_x_forwarded_proto setting is set totrue
. - Fix overly strict route name rules.
0.2.3
Marten 0.2.3 fixes a couple of bugs.
Bug fixes
- Fix possible incorrect migration dependency generation when two separate apps have migrations generated at the same time
- Fix possible incorrect circular dependency error raised when verifying the acyclic property of the migrations graph
0.2.2
Marten 0.2.2 fixes a couple of bugs.
Bug fixes
- Fix possible compilation errors happening when inheriting from a schema containing fields with options
- Fix an issue where model field validations were inconsistent with validation callbacks (eg. model field validations were called before
before_validation
callbacks) - Ensure that exception messages and snippet lines are properly escaped in the server error debug page
0.2.1
Marten 0.2.1 fixes a couple of bugs.
Bug fixes
- Fix possible empty field names in messages associated with exceptions raised when targetting non-existing fields with query sets
- Fix broken methods inherited from the
Enumerable
mixin for request parameter abstractions - Fix a possible typing issue related to the use of
Marten::Emailing::Backend::Development#delivered_emails
in specs generated for projects with authentication