UseCase your code
Installation
Add this line to your application's Gemfile:
I did not (YET) upload this code to the rubygems.
To early adopters use the :git path
gem 'usecasing', :git => 'https://github.com/tdantas/usecasing.git'
And then execute:
$ bundle
Usage
Let's build a Invoice System, right ?
So the product owner will create some usecases/stories to YOU.
Imagine this usecase/story:
As a user I want to finalize an Invoice and an email should be delivered to the customer.
Let's build a controller
class InvoicesController < ApplicationController
def finalize
params[:current_user] = current_user
# params = { invoice_id: 123 , current_user: #<User:007> }
context = FinalizeInvoiceUseCase.perform(params)
if context.success?
redirect_to invoices_path(context.invoice)
else
@errors = context.errors
redirect_to invoices_path
end
end
end
Ok, What is FinalizeInvoiceUseCase ?
FinalizeInvoiceUseCase will be responsible for perform the Use Case/Story.
Each usecase should satisfy the Single Responsability Principle and to achieve this principle, one usecase depends of others usecases building a Chain of Resposability.
class FinalizeInvoiceUseCase < UseCase::Base
depends FindInvoice, ValidateToFinalize, FinalizeInvoice, SendEmail
end
IMHO, when I read this Chain I really know what this class will do.
astute readers will ask: How FindInvoice pass values to ValidateToFinalize ?
When we call in the Controller FinalizeInvoiceUseCase.perform we pass a parameter (Hash) to the usecase.
This is what we call context, the usecase context will be shared between all chain.
class FindInvoice < UseCase::Base
def perform
# available because it was added in the controller context
user = context.current_user
# we could do that in one before_filter
invoice = user.invoices.find(context.invoice_id)
# asign to the context make available to all chain
context.invoice = invoice
end
end
Is the invoice valid to be finalized ?
class ValidateToFinalize < UseCase::Base
def perform
#failure will stop the chain flow and mark the context as error.
failure(:validate, "#{context.invoice.id} not ready to be finalized") unless valid?
end
private
def valid?
#contextual validation to finalize an invoice
end
end
So, after validate, we already know that the invoice exists and it is ready to be finalized.
class FinalizeInvoice < UseCase::Base
def perform
invoice = context.invoice
invoice.finalize! #update database with finalize state
context.customer = invoice.customer
end
end
Oww, yeah, let's notify the customer
class SendEmail < UseCase::Base
def perform
to = context.customer.email
# Call whatever service
EmailService.send('customer_invoice_template', to, locals: { invoice: context.invoice } )
end
end
Let me know what do you think about it.
TODO
Create real case examples (40%)
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request
