π§© A lightweight, expressive DSL for writing service objects in Ruby.
Define business logic in a clean, readable structure using expects, promises, and lifecycle blocks (prepare, validate, perform) with context-based control flow.
expectsandpromisesfor defining input/output contractsprepare,validate,performblock hooksctx.fail!(message, with: SomeError)for graceful failureServiceDSL::Resultobject withsuccess?,errors, and dynamic access- Minimal dependencies, works with any Ruby project
class CreateUser
include ServiceDSL
expects :name, @email
promises :user
prepare do |ctx|
@email = ctx.email.strip.downcase
end
validate do |ctx|
ctx.fail!("Email invalid", with: ArgumentError) unless ctx.email.include?("@")
raise "Name cannot be blank" if ctx.name.to_s.strip.empty? # any errors are caught by ServiceDSL
end
perform do |ctx|
ctx.user = create_user
end
private
def create_user
User.create!(name: context.name, email: @email) # context is available
end
end
result = CreateUser.call(name: "Max", email: " max@example.com ")
if result.success?
puts result.user
else
puts result.errors.join(", ")
endexpects :key1, :key2β required input keyspromises :keyβ required outputsprepare { |ctx| }β run before validationvalidate { |ctx| }β run before perform; can raise or call ctx.fail!perform do |ctx| endβ main logic block
ctx.key # β access expected input
ctx.key = value # β set promised output
ctx.fail!("msg", with: SomeErrorClass) # β stop andmark as failed
result.success? # => true/false
result.errors # => ["Email invalid"]
result.user # => dynamic access to promised values