Hexx

Gem Version Bild
Status Code Metrics Dependency Status Coverage Status License

A part of framework for rapid Rails API development following the hexagonal architecture.

Provides:

  • UseCase base class for decoupling a domain business logics from a controller

  • Thor generators for scaffolding use cases, controllers and routes.

Installation

Add this line to your application's Gemfile:

gem "hexx"

And then execute:

$ bundle

Or install it yourself as:

$ gem install hexx

Usage

Controllers are expected to serve as a thin delivery layer between a client and a use case. They should only orchestrate interactions of use case with its listeners: the controller itself, mailers and other outer services.

In a typical case the controller should constist from:

  • actions serving as outer ports to client, listening to routes and calling corresponding use cases

  • methods servinge as inner ports called by use case notifications (see wisper for more details).

Use case

# app/my_project/use_cases/get_item.rb
require 'hexx'

module MyProject
  class GetItem < Hexx::Base

    # whitelists parameters taken by an initializer
    allow_params :id

    # runs some validation (on the basis of ActiveModel::Validations)
    validate :id, presence: true

    # runs a use case
    def run!
      validate!
      # provide case-specific business rules
    end
  end
end

Rails controller

# app/controllers/my_project/rails/api/v1/items_controller.rb
require 'my_project'
require 'rails'

module MyProject
  module Rails
    module Api
      module V1
        class ItemsController < ActionController::Base

        # An action (outer port) of the controller
        def show
          use_case = GetItem.new params.allow(:id)
          # subscribes the controller for listening to the use case
          # notifications via the controller's inner ports.
          use_case.subscribe self
          use_case.run
        end

        # Inner port to be called by a use case
        def found(item)
          render json: { success: true, item: item }, status: 200
        end
      end
    end
  end
end

Note the module above is not my_project, but my_project-rails (a delivery mechanism for my_project).

Models and Entities

The module also defines module Hexx::Models for including to entities or active_record models. It defines a validate! method:

class User < ActiveRecord::Base
  include Hexx::Models
end

User.new.validate!
# => raises Hexx::RecordInvalid error if the record isn't valid.

It also redefines attr_accessor, attr_reader and attr_writer helpers to allow attribute coercion.

require "attributes/string"

class User < ActiveRecord::Base
  include Hexx::Models

  attr_accessor :name, type: Attributes::String
end

This coerces the name with an Attributes::String class object as if its setter and getter were:

def name=(value)
  super Attributes::String.new(value)
end

def name
  Attributes::String.new(super)
end

Generators

The module contains generators, written on the basis of the Thor, namely:

  • request specs (acceptance tests);

  • controllers with their specs (unit tests)

  • routing specs (unit tests);

  • domain specs (acceptance tests for use cases and models);

  • use_cases with their specs (unit tests);

The scaffolded specs contains examples for writing tests rapidly following some conventions.

Request spec (acceptance test for the API)

Request specs used to test API from a user view - by sending requests and checkign their results. Their should be written on the basis of API blueprint. Their shouldn't check the internal states of application, such as models, use_cases etc.

Task is called with 1 argument, that consists from two parts divided by colon:

  • request type (get, post etc.)

  • requests relative address

Example:

$ hexx request get:items/{id}
# => spec/requests/my_module/api/v1/get_items_id_spec.rb

API namespace (those /my_module/api/v1) will be added by default.

Controller action with a corresponding route

A scaffolder provides tests for both a controller and its router. It also scaffolds a controller action itself. The router action should be added manually.

Except for the requests specs (above) the controller spec is an unit test that checks outer and inner controller action in isolation from both router and a use case. It only checks that outer actions calls proper use cases, and inner actions returns proper responce to a user.

Task is called with 3 argumets:

  • name of the controller

  • request type and action name, divided by colon

  • name of the use case for the action

Example:

$ hexx controller items get:show get_item
# => spec/routing/my_module/api/v1/items_routing_spec.rb
# => spec/controllers/my_module/api/v1/items_controller_spec.rb
# => app/controllers/my_module/api/v1/items_controller.rb

Acceptance spec for the domain

The domain spec checks the inner part of the domain (use_cases and models) as a whole. Because the domain interacts with outer word through the use cases only, the acceptance test should call use_cases and check its results. It should not check inner states of the domain (models, entities, repositories, databases etc.)

Task is called with 1 argument:

  • name of the use_case.

Example:

$ hexx domain get_item
# => spec/my_module/domain/get_item_spec.rb

UseCase

The unit tests for checking a use_case in isolation.

Task is called with 1 argument:

  • name of the use_case.

Example:

$ hexx use_case get_item
# => spec/my_module/use_cases/get_item_spec.rb
# => app/my_module/use_cases/get_item.rb

Dependency

Scaffolds the external dependency setting with a corresponding specs.

Task is called with 1 argument:

  • name of the dependency.

Example:

$hexx dependency some_module
# => spec/my_module_spec.rb
# => lib/my_module.rb

Relevant Links

1

Matt Wynne's talk Hexagonal Rails

Changelog

To review changes between versions see the CHANGELOG.

License

The project is distributed under the MIT LICENSE.