-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started with Interactify
Interactify is a Ruby gem designed to enhance Rails applications by simplifying complex business logic.
It extends interactors with additional features like conditional execution and asynchronous processing, making your codebase easier to get an overview of.
To get started with Interactify, add it to your Gemfile and run the bundle install
command:
gem 'interactify'
Then, execute:
bundle install
Begin by creating a new Interactify
d Class.
class CreateUser
include Interactify
def call
context.user = User.create(email: context.email)
end
end
Interactify's DSL builds on top of interactor and interactor-contracts and allows you to define complex workflows. Here's a basic example:
class SignupUser
include Interactify
organize CreateUser, SendAddressConfirmationEmail, AssignOrderToUser, SetUserDashboardPath
end
class SendAddressConfirmationEmail
include Interactify
def call
EmailProvider.send_confirmation(email: context.email_address)
end
end
This organizes CreateUser
SendAddressConfirmationEmail
and AssignOrderToUser
, and SetUserDashboardPath
into a single workflow.
RSpec.describe SignupUser do
let(:result) { CreateUser.call(params) }
let(:params) { { email: email } }
let(:email) { '[email protected]' }=
it 'creates a user' do
expect(result).to be_success
expect(result.user).to be_a User
expect(result.user.email).to eq email
end
end
To execute your interactor, simply call:
def create
@signup = SignupUser.call(params)
if @signup.success?
redirect_to @signup.user_dashboard_path
else
render :new
end
end
This works for creating a user, but it falls over when we try to send the email as the email address is nil
.
Why is that if we passed it in in the params
?
If you followed closely you would have spotted that we never set the email_address
on the context.
We pass in email
instead.
Let's stop this kind of thing from happening in future.
Add an expectation to the organizer and the interactors.
class SignupUpser
include Interactify
expect :email
organize CreateUser, SendAddressConfirmationEmail, AssignOrderToUser, SetUserDashboardPath
end
class CreateUser
include Interactify
expect :email
def call
context.user = User.create(email: context.email)
end
end
class SendAddressConfirmationEmail
include Interactify
expect :email
def call
EmailProvider.send_confirmation(email: context.email)
end
end
Notice we now call context.email
in both interactors?
However we can do better than this. As an Interactor::Context
wraps an OpenStruct
it means
any method call whatsoever is valid on context
.
Interactify provides automatic delegation of any expected keys in the class. This means we can rewrite like this
class CreateUser
include Interactify
expect :email
def call
context.user = User.create(email: email)
end
end
class SendAddressConfirmationEmail
include Interactify
expect :email
def call
EmailProvider.send_confirmation(email: email)
end
end
OK so now if we had have put the incorrect method in there it would blow up immediately with a
NoMethodError email_address for SendAddressConfirmationEmail
And we can go a step further using the latest ruby syntax and omit the values in the keyword arguments:
class CreateUser
include Interactify
expect :email
def call
context.user = User.create(email:)
end
end
class SendAddressConfirmationEmail
include Interactify
expect :email
def call
EmailProvider.send_confirmation(email:)
end
end
Much simpler and more robust.
Now let's validate our contract chains. You will only need to add this once to your test suite and it will validate every interactor in the system going forwards.
RSpec.describe 'InteractorWiring' do
it 'validates the interactors in the whole app', :aggregate_failures do
errors = Interactify.validate_app(ignore: [/SomeClassName/, AnotherClass, 'SomeClassNameString'])
expect(errors).to eq ''
end
end
now if had have added an expect :email_address
to the SendAddressConfirmationEmail
, the test above would fail like this:
Missing keys: :email_address
in: SendAddressConfirmationEmail
called by: SignupUser