-
Notifications
You must be signed in to change notification settings - Fork 2
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 installBegin by creating a new Interactifyd Class.
class CreateUser
include Interactify
def call
context.user = User.create(email: context.email)
end
endInteractify'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
endThis 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) { 'test@example.com' }=
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
endTo execute your interactor, simply call:
def create
@signup = SignupUser.call(params)
if @signup.success?
redirect_to @signup.user_dashboard_path
else
render :new
end
endThis 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
endNotice 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
endOK 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
endMuch 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
endnow 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