This new Lotus release is an important step towards 1.0!

In the past months we talked with people at conferences, meetups, events, you all are eagerly looking for a production ready version. This makes us proud of Lotus and we are determined to get there.

That's why we have decided to postpone a few announced features like WebSocket and experimental code reloading.


Let's have a look at what is shipped with this version.


Inês & Rosa (aka DEIGirls), are the Rails Girls Summer of Code students who worked on mailers. During these three months, mentored by Trung Lê, they learned about Ruby, Lotus and they shipped their first gem: lotus-mailer. This is a huge achievement for all of us!

We have introduced a generator, which creates a mailer, the test code and two associated templates for multipart delivery.

% bundle exec lotus generate mailer forgot_password
      create  spec/bookshelf/mailers/forgot_password_spec.rb
      create  lib/bookshelf/mailers/forgot_password.rb
      create  lib/bookshelf/mailers/templates/forgot_password.txt.erb
      create  lib/bookshelf/mailers/templates/forgot_password.html.erb

For simplicity, each mailer can handle only one use case (feature). If in our application we need to send emails for several features like: "confirm your email address" or "forgot password", we will have Mailers::ConfirmEmailAddress and Mailers::ForgotPassword instead of a generic UserMailer that manages all these use cases.

# lib/bookshelf/mailers/forgot_password.rb
class Mailers::ForgotPassword
  include Lotus::Mailer

  from    ''
  to      ''
  subject 'Hello'

# Usage

Lotus::Mailer is built on top of the rock solid mail gem by Mikel Lindsaar.

Read the guides

Custom Data Mapper Coercions

Lotus data mapper supports the most common Ruby data type such as String, Integer, or DateTime. Sometimes, this simple approach is not enough to solve the database impedance mismatch on types.

Imagine we have a Book#tags, a collection of strings that we want to store as a Postgres array. If we use Array builtin type, our tags aren't properly translated into a format that is compatible with our column type.

The solution to this problem is to define a custom coercer.

# lib/ext/pg_array.rb
require 'lotus/model/coercer'
require 'sequel/extensions/pg_array'

class PGArray < Lotus::Model::Coercer
  def self.dump(value)
    ::Sequel.pg_array(value, :varchar)

  def self.load(value)
    ::Kernel.Array(value) unless value.nil?
# lib/bookshelf.rb
require_relative './ext/pg_array'
# ...

Lotus::Model.configure do
  # ...
  mapping do
    # ...
    collection :articles do
      attribute :id,   Integer
      attribute :tags, PGArray

Read the guides

Command / Query Separation

When the powerful repositories API doesn't fit our needs, we can send raw command and queries to the database. Until now there was a generic .execute to use. Both the signature and the semantic of this method, became too complex, so we decided to add .fetch.

It returns a raw dataset from the database.

# lib/bookshelf/repositories/book_repository.rb
class BookRepository
  include Lotus::Repository

  def self.raw_all
    fetch("SELECT * FROM books")

  def self.find_all_titles
    fetch("SELECT title FROM books").map do |book|

We changed .execute send a raw command and return nil.

# lib/bookshelf/repositories/book_repository.rb
class BookRepository
  include Lotus::Repository

  def self.reset_download_count
    execute("UPDATE books SET download_count = 0")

Read the guides

Minor Changes

Thanks to Theo Felippe for the MIME type detection work and to Wellington Santos for a better exception handling. To Pascal Betz, José Mota, Alex Wochna and Khải Lê for their form helper enhancements, while Leonardo Saraiva wrote an expanded version of #link_to.

Alfonso Uceda added SQL joins, Bohdan V. and Manuel Corrales fixed small Lotus::Model issues, while Brenno Costa worked on JRuby support!

We're thankful for the improvements and fixes that Ben Lovell, Rodrigo Panachi, Derk-Jan Karrenbeld, Cẩm Huỳnh and Andrii Ponomarov did on lotusrb.


Default Format

We deprecated default_format in favor of default_request_format.

We also introduced default_response_format to force a MIME Type, without the need of specify it for each action. It defaults to :html, but if you are building a JSON API, you may find useful to set it to :json.

# apps/web/application.rb
# ...
module Web
  class Application < Lotus::Application
    configure do
      # If you are using this:
      default_format :xml

      # Please rename into:
      default_request_format :xml

Hack Day

Did you always wanted to play with Lotus but you keep saying: "I don't have time"?

As Lotus will approach to 1.0, we'll need your help, your feedback.. and your fun! So, here's the deal.. we're organizing a Hack Day later this year!

⬇️ If you want to get alerted, please consider to subscribe to our mailing list using the form below. ⬇️

Until then, happy hacking!