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.