overture noun

  1. an orchestral piece at the beginning of an opera, suite, play, oratorio, or other extended composition.
  2. an introduction to something more substantial

Overture is the start of something, an introduction. So this gem provides an introduction to your new application (or service). It helps you start developing your application so you can build a full orchestra eventually.


Install it

If you're starting from scratch, you can install the gem manually by running:

$ gem install overture

If you have an existing app and you want to use Overture, you can add it to your Gemfile:

gem 'overture'

What it can do?

Title says can because you can choose what you need from Overture. You don't need to use everything. If you only need a database connection adapter, just pull that. Or if you need a basic Sinatra configuration for a JSON API, just use that.

Overture is a set of tools or modules that you can use independently. All modules can interplay together as well, and they'll let you get something working pretty quickly.

In most cases you only need the hammer to nail a nail.

Creating an app

After you install the gem, you can run this command to generate an app directory layout:

$ overture generate app my-app

The last argument is the name of the folder you want to use for your app. This will generate a directory structure like the following:

├── Gemfile
├── Gemfile.lock
├── Procfile
├── Rakefile
├── bin
│   ├── console
│   └── dev_http_api
├── config
│   ├── environment.rb
│   └── puma.rb
├── config.ru
└── lib
    ├── http_api
    │   ├── endpoints.rb
    │   └── server.rb
    ├── interactors
    └── models

This command will create all necessary files to create a full application. This means you'll have three layers:

Application logic

All logic that is not specific to your domain (business), is handled here. In other words, this is where all your models (entities) live. Specifically in the lib/models directory.

Business logic

All the logic that is specific to your domain (business) goes here. This layer is handled by Interactors, also known as Use Cases. These classes are created in the lib/interactors directory.

Presentation layer

This is basically the output of your application that is used by other applications or services. This layer is managed by a Sinatra app that accepts and responds to JSON HTTP endpoints. The endpoints are defined in the lib/http_api/endpoints.rb file. You can also organize your endpoints in different files/classes, preferably subclasses of the HttpApi::Server class defined in the lib/http_api/server.rb file.

All other files outside of lib are practically configuration files. You can see what they do by opening them.

Once you create your app, you can run bin/dev_http_api to start up a Rack server. This will run in the port 3000 by default but you can change it in the config/puma.rb file or with the PORT env var.

Configuring via environment variables

Overture uses dotenv to load environment variables defined in the .env file of your project.

You should NOT version your .env file. Instead you can add a .env.example file with dummy values.

Connecting to a database

You can use Overture::Database to create a connection to a database using Sequel.

Once you've generated your app structure, in your config/environment.rb file uncomment the line that requires the database module, and the one that creates the connection instance:

require 'overture/database'

DB = Overture::Database.connect(ENV.fetch('DATABASE_URL'))

As you can see, it uses the environment variable DATABASE_URL. You can define it in your .env file (see Configuring via environment variables section).

Model the datasets

You can map your database tables to Ruby classes that inherit the Overture::Model class.

For example if you have a users table, you can create a User model like this:

class User < Overture::Model
end

This will automatically map to the users table following the Sequel convention for the tables naming as described here.

You now can persist and retrieve records in the users table:

User.create(first_name: 'John')
user = User.first(first_name: 'John')
user.update(first_name: 'Juan')

Refer to the Sequel documentation to see what methods are available.

Version your schema with migrations

To create the tables in your database you can do it manually or use migration files that will also help you revert to a specific version if needed.

In order to create a new migration file you run:

$ overture generate migration users

This will create a file under the migrations folder. Open it and change to your needs. See the Sequel migration documentation to know what you can do.

Implement your business logic

As mentioned above, your business logic lis handled by interactors. An interactor is a subclass of the Interactor class. with some methods added for convenience. See the Overture::Interactor class to find what methods are added.

Digest and validate passwords

Overture comes with a password digester module that helps you digest and validate passwords.

To use it, include it in your class and call digest_password(password) or valid_password?(password_digest, plain_password) methods to digest or validate a password respectively.

For example:

require 'overture/password_digester'

class AuthenticateUser < Overture::Interactor
  include Overture::PasswordDigester

  required :email, :password

  def call
      user = User.first(email: context.email)
    fail!('user does not exist' unless user
    context.authenticated = valid_password?(user.password_digest, context.password)
  end
end

Communicate with the exterior

The Overture::HttpApi::Server class is a Sinatra app configured to accept and respond to JSON. It includes the Overture::HttpApi::Helpers module too, which provides some common helpers.

When you generate an app with Overture, it creates a subclass of Overture::HttpApi::Server that you can configure to your needs.

You can define your endpoints in the HttpApi::Endpoints class. Or organize them in subclasses of HttpApi::Server that you can mount in the main HttpApi::Endpoints class.

Serializing your models

When you have a model class (i.e. User), and you want to present it as a JSON. You can use the json() helper method and it will automatically try to find a corresponding serializer for that class based on its name plus the Serializer suffix. In this case it'd be UserSerializer.

The Overture::Serializer produces JSON:API object. And it's based in the Netflix/fast_jsonapi serializer. So as long as you conform with its interface, you can create your own too.

If you have a serializer that does not conforms to the naming convention, you can specify what serializer to use in the json() helper method:

get '/admin/users' do
  users = User.all
  json users, serializer: AdminUserSerializer
end

As mentioned in the beginning, you don't need to use all these components. You could for example only use the Overture::Interactor class, or the Overture::PasswordDigester module in your application.