Nala
Lightweight gem for adding events to use case style classes.
Install
To install via RubyGems run:
$ gem install nala
To install via bundler, add this line to your Gemfile and run bundle install:
gem "nala"Usage
Nala is intended to be used with use case style classes where the class performs
a single action. These classes typically have verb names such as PlaceOrder
etc.
To use, add the Nala::Publisher module to your class and expose a call
instance method. If your class requires data, it can be passed to it via the
constructor.
For example:
class PlaceOrder
include Nala::Publisher
def initialize(params)
@params = params
end
def call
# ...
if order.save
publish(:ok, order)
else
publish(:error)
end
end
endUsage of these use case style classes gives a home to your business logic, removes conditionals from your Rails controllers and helps prevent your models getting too big.
class OrderController < ApplicationController
# ...
def create
PlaceOrder.call(order_params)
.on(:ok) { |order| redirect_to orders_path, :notice => "..." }
.on(:error) { render :new }
end
# ...
endThis comes in handy when there are more than two outcomes to a use case:
class RegistrationController < ApplicationController
# ...
def create
RegisterAccount.call(account_params)
.on(:already_registered) { redirect_to login_path, :notice => "..." }
.on(:ok) { |user| redirect_to dashboard_path, :notice => "..." }
.on(:error) { render :new }
end
# ...
endIf in some cases you don't need to handle the events, you can either use .call or #call depending on your preference as internal calls to #publish will be ignored.
PlaceOrder.call(order_params)
# or
PlaceOrder.new(order_params).callTesting with RSpec
If you are using RSpec then you can use the supplied matcher by adding the
following line to your spec_helper.rb file:
require "nala/rspec"Then within your specs, you can confirm that a handler is called with the following:
let(:block) { block_spy }
it "invokes a handler for a published event" do
SuccessClass.call.on(:success) { block.called! }
expect(block).to be_called
endIf you want to check the arguments that are passed to the block you can use the following:
let(:block) { block_spy }
it "passes multiple arguments to handlers" do
PublishArgsClass.call
.on(:multiple) { |*args| block.called_with!(args) }
expect(block).to be_called_with(:a, :b, :c)
endIf you need check the arguments attributes in more detail you can do the following:
let(:block) { block_spy }
it "passes a user with the correct name" do
RegisterUser.call
.on(:success) { |*args| block.called_with!(args) }
user = block.args.first
expect(user.name).to eq("Andy")
endYou can make the tracking of block arguments less verbose by using the spy
method of Nala::BlockSpy (which block_spy returns) and passing it as an explicit block to the on method:
let(:block) { block_spy }
it "passes multiple arguments to handlers" do
PublishArgsClass.call.on(:multiple, &block.spy)
expect(block).to be_called_with(:a, :b, :c)
endYou can of course spy on multiple blocks in a single spec if you need to. We
recommend naming your block after the event it will be called by (rather than
just block):
let(:success) { block_spy }
let(:notified) { block_spy }
it "calls multiple handlers" do
PlaceOrder.call
.on(:success, &success.spy)
.on(:notified, ¬ified.spy)
expect(success).to be_called_with(order)
expect(notified).to be_called_with(:email)
endIf you would like to stub calls to the .call method of your use case class but want to make sure handlers are called, you can use the supplied emit helper method like so:
allow(SuccessClass).to receive(:call) { emit(:success) }
or you can supply arguments to the block:
allow(SuccessClass).to receive(:call) { emit(:success, "My Argument") }
Notes for maintainers
Building a publishing gem updates
Bump the version number in lib/nala/version.rb and use the same number in the
command below:
gem build nala.gemspec
gem push nala-0.0.3.gem
License
See the LICENSE file for license rights and limitations (MIT).