Features

This patch release ships new features and improved code generators.

RESTful nested resource(s)

RESTful resource(s) can be nested with infinite levels.

# apps/web/config/routes.rb
resources :users do
  resource  :avatar
  resources :events
end

This generates the following routes:

% lotus routes

     new_user_avatar GET, HEAD  /users/:user_id/avatar/new
         user_avatar POST       /users/:user_id/avatar
         user_avatar GET, HEAD  /users/:user_id/avatar
    edit_user_avatar GET, HEAD  /users/:user_id/avatar/edit
         user_avatar PATCH      /users/:user_id/avatar
         user_avatar DELETE     /users/:user_id/avatar
         user_events GET, HEAD  /users/:user_id/events
      new_user_event GET, HEAD  /users/:user_id/events/new
         user_events POST       /users/:user_id/events
          user_event GET, HEAD  /users/:user_id/events/:id
     edit_user_event GET, HEAD  /users/:user_id/events/:id/edit
          user_event PATCH      /users/:user_id/events/:id
          user_event DELETE     /users/:user_id/events/:id
               users GET, HEAD  /users
            new_user GET, HEAD  /users/new
               users POST       /users
                user GET, HEAD  /users/:id
           edit_user GET, HEAD  /users/:id/edit
                user PATCH      /users/:id
                user DELETE     /users/:id

The corresponding endpoints are actions like Web::Controllers::Users::Avatar::Create or Web::Controllers::Users::Events::Show.

Dirty Tracking

Entities can now track changed attributes and dirty status.

# lib/bookshelf/entities/user.rb
class User
  include Lotus::Entity
  include Lotus::Entity::DirtyTracking
  attributes :name, :age
end

This new feature is activated by including Lotus::Entity::DirtyTracking. It exposes #changed? and #changed_attributes.

# Usage
user = User.new(name: 'L')
user.changed? # => false

user.age = 33
user.changed?           # => true
user.changed_attributes # => {:age=>33}

user = UserRepository.create(user)
user.changed? # => false

user.update(name: 'Luca')
user.changed?           # => true
user.changed_attributes # => {:name=>"Luca"}

user = UserRepository.update(user)
user.changed? # => false

result = UserRepository.find(user.id)
result.changed? # => false

When an entity is initialized and after it's created or updated, the state is clean. If we mutate the state with an assigment or with Entity#update, #changed? returns true.

Timestamps

If an entity has :created_at and/or :updated_at attributes, when it's persisted, the repository will take care of manage these values for us.

# lib/bookshelf/entities/user.rb
class User
  include Lotus::Entity
  attributes :name, :created_at, :updated_at
end

We have mapped both these attributes as DateTime.


user = User.new(name: 'L')
puts user.created_at # => nil
puts user.updated_at # => nil

user = UserRepository.create(user)
puts user.created_at.to_s # => "2015-05-15T10:12:20+00:00"
puts user.updated_at.to_s # => "2015-05-15T10:12:20+00:00"

user.name = "Luca"
user      = UserRepository.update(user)
puts user.created_at.to_s # => "2015-05-15T10:12:20+00:00"
puts user.updated_at.to_s # => "2015-05-15T10:12:23+00:00"

Application Generator

Lotus default architecture is named Container, as it allows to run multiple Lotus (and Rack) applications within the same Ruby process.

When we generate a new project with lotus new bookshelf, it creates a default application named Web, under apps/web. This architecture enforces a separation between components and it will make our life easier if we want extract microservices at a later stage.

If we want to introduce a new component in our container (eg. an admin pane, a metrics dashboard), we can use the new application generator.

lotus generate app admin

This command will add a new application named Admin under apps/admin, and it will be available under the /admin URL namespace.

Model Generator

This last feature is really helpful while developing our domain model. It generates an entity, a repository and the related test files.

% lotus generate model user
  create  lib/bookshelf/entities/user.rb
  create  lib/bookshelf/repositories/user_repository.rb
  create  spec/bookshelf/entities/user_spec.rb
  create  spec/bookshelf/repositories/user_repository_spec.rb

What's Next

New features such as migrations, form helpers and improved security are under development right now. We're pushing to ship soon.