ActiveDuty
Service objects, based loosely on Interactor.
Design
Define the logic in run and call it with call:
Basic usage
class AuthenticateUser < ActiveDuty::Base
def initialize(email, password)
@email, @password = email, password
end
def run
if user = User.authenticate(@email, @password)
context.user = user
else
fail!
end
end
endUsage:
service = AuthenticateUser.new("josh@example.com", password: "password")
service.call
if service.success?
# do something
else
puts service.errors.inspect
end Advanced usage
init_with
A DSL for initializing.
Ordered arguments
class AuthenticateUser < ActiveDuty::Base
init_with :username, :password
end Is the equivalent of:
class AuthenticateUser < ActiveDuty::Base
attr_reader :username, :password
def initialize(username, password)
@username, @password = username, password
end
end Keyword arguments
class AuthenticateUser < ActiveDuty::Base
init_with uname: :username, pw: :password
end Is the equivalent of:
class AuthenticateUser < ActiveDuty::Base
attr_reader :username, :password
def initialize(uname:, pw:)
@username, @password = uname, pw
end
end Defaults
class AuthenticateUser < ActiveDuty::Base
init_with :username, [:password, nil]
end Is the equivalent of:
class AuthenticateUser < ActiveDuty::Base
attr_reader :username, :password
def initialize(username, password = nil)
@username, @password = username, password
end
end Keyword arguments
class AuthenticateUser < ActiveDuty::Base
init_with uname: [nil, :username], pw: ["good-password", :password]
end Is the equivalent of:
class AuthenticateUser < ActiveDuty::Base
attr_reader :username, :password
def initialize(uname: nil, pw: "good-password")
@username, @password = uname, pw
end
end Callbacks
Supports after_initialize, before_call, after_call. They use ActiveModel::Callbacks under the hood, so the API is familiar.
Context
An OpenStruct for a setting information during the runtime of your service object. Exposed via context.
Errors
Uses ActiveModel::Errors under the hood.
Rollbacks
If your call fails and you need to do some housekeeping, define a rollback:
class AuthenticateUser < ActiveDuty::Base
def rollback
# do something
end
endOr class-level:
class AuthenticateUser < ActiveDuty::Base
rollback do
# do something
end
endFailing
Need to fail? Call fail!
Sequential calls
Want to run service after service?
class ServiceTwo < ActiveDuty::Base; end
class ServiceThree < ActiveDuty::Base; end
class ServiceOne < ActiveDuty::Base
boots ServiceTwo, ServiceThree
end They won't take arguments at this time.