= Hexx
{}[https://rubygems.org/gems/hexx]
{}[https://travis-ci.org/nepalez/hexx]
{}[https://codeclimate.com/github/nepalez/hexx]
{}[https://gemnasium.com/nepalez/hexx]
{}[https://coveralls.io/r/nepalez/hexx]
{}[https://github.com/nepalez/hexx/blob/master/LICENSE.rdoc]
The module provides a base service objects class and some features for the
domain models (entities).
Provides:
* +Service+ base class for decoupling a domain business logics from a controller
* +Models+ module for extending domain models.
The module is expected to be used in PORO domain. For usage in active record
domains consider { hexx-active_record }[https://github.com/nepalez/hexx-active_record]
gem instead.
== Installation
Add this line to your application's Gemfile:
gem "hexx", "~> 2.0"
And then execute:
$ bundle
Or install it yourself as:
$ gem install hexx
== Pattern
Service objects decouple business logics from:
* Domain _models_ (entities).
* Delivery mechanism _controllers_ (such as Rails framework).
To follow object oriented architecture Hexx services exploit the
*Observer* (Listener) pattern via { Wisper }[https://github.com/krisleech/wisper]
gem.
Some examples of this pattern implementation are available at the
{ wisper gem wiki }[https://github.com/krisleech/wisper/wiki].
=== Service
A typical service object is shown below:
require 'hexx'
class AddItem < Hexx::Service
# whitelists parameters and defines corresponding attributes.
allow_params :name
# defines some validation using ActiveModel::Validations helpers.
validate :name, presence: true
# runs a service
def run
# re-raises StandardErrors as Hexx::Service::Invalid
escape { MyModel.save! }
rescue => err # Hexx::Service::Invalid only
publish :error, err.messages
else
publish :success
end
end
Usage of the service (in a Rails controller):
class ItemsController < ActionController::Base
# Creates an item with given name
def create
service = AddItem.new params.allow(:name)
service.subscribe self, prefix: :on
service.run
end
# Publishes a success message
def on_created(item, messages)
@item, @messages = item, messages
render "created", status: 201
end
# Publishes an error messages
def on_error(messages)
@messages = messages
render "error"
end
end
The controller knows nothing about the action itself. It only needs to
sort out a request and send it to a corresponding service.
Any controller action does one thing only.
* +create+ action sorts out requests.
* both +on_success+ and +on_created+ listens to services and report their
results back to client.
=== Models and Entities
The module also defines Hexx::Models module to extend domain models
(entities). This allows coercion of attributes with +attr_coerced+ helper:
class User
extend Hexx::Models
attr_coerced :name, type: ActiveSupport::Multibyte::Chars
end
user = User.new name: "Ivan"
user.name
# => "Ivan"
user.name.class
# => ActiveSupport::Multibyte::Chars
The method redefines both getter and setter and can be used for value
preparation:
class StrippedString < String
def initialize(value)
super value.strip
end
end
class User
extend Hexx::Models
attr_coerced :name, type: StrippedString
end
user = User.name " Ivan "
user.name # => "Ivan"
== Dependencies
The module provides methods +depends_on+ and +config+ to create
dependency injection framework.
Declare gem dependencies (external classes and modules).
# lib/my_gem.rb
module MyGem
extend Hexx::Dependencies
depends_on :get_item, :add_item
end
Inject gem dependencies:
# config/my_gem.rb
MyGem.configure do |config|
config.get_item = AnotherGem::Services::Get # as a constant
config.add_item = "AnotherGem::Services::Add" # as a name of a constant
end
Use them somewhere in a code:
MyGem.get_item # => AnotherGem::Services::Get
MyGem.add_item # => AnotherGem::Services::Add # the name is constantized
== Relevant Links
* Matt Wynne's talk {Hexagonal Rails}[http://www.confreaks.com/videos/977-goruco2012-hexagonal-rails]
* { wisper }[http://www.github.com/krisleech/wisper] gem by Kris Leech.
== License
The project is distributed under the {MIT LICENSE}[LICENSE.rdoc].