Assistant
A tiny, dependency-free Ruby library for writing soft-fail, composable service objects. A service declares its inputs, validates them, runs its body, and returns a uniform result that always carries either a value plus warnings or a list of errors — it never raises for expected failures.
Installation
bundle add assistant
Or without Bundler:
gem install assistant
Ruby >= 3.4 is required.
60-second example
require 'assistant'
class CreateUser < Assistant::Service
input :email, type: String, required: true
input :name, type: String, required: true
input :age, type: Integer, allow_nil: true, default: nil
def validate
return if email.include?('@')
log_item_error(source: :validate, detail: :email, message: 'must contain @')
end
def execute
log_item_warning(source: :execute, detail: :age, message: 'age missing') if age.nil?
User.create!(email:, name:, age:)
end
end
CreateUser.run(email: 'ada@example.com', name: 'Ada')
# => { result: #<User …>, status: :with_warnings, warnings: [#<Assistant::LogItem …>] }
CreateUser.run(email: 'nope', name: 'Ada')
# => { errors: [#<Assistant::LogItem …>], result: nil, status: :with_errors }
The result hash is always one of two shapes:
# Success — status is :ok or :with_warnings
{ result: <Object>, status: :ok | :with_warnings, warnings: Array<Assistant::LogItem> }
# Failure — any error was logged before or during execute
{ errors: Array<Assistant::LogItem>, result: nil, status: :with_errors }
Use #success?, #failure?, and #status on a service instance, or
pattern-match the hash returned by .run directly.
Why another service-object gem?
- No runtime dependencies.
assistantis one require away; it does not pull in ActiveSupport, dry-rb, or anything else. It runs in plain Ruby, Rails, Hanami, Sidekiq workers, and Rake tasks alike. - Soft-fail by design. Validation problems and recoverable failures are
surfaced as
LogItems on the result, not exceptions. Callers pattern-match a hash; they don't writerescueblocks for expected outcomes. - Tiny, frozen surface. The public API documented in
docs/v1/01-api-surface.mdis everything there is. No DSL gymnastics, no plug-in registry, no opinionated serialization. Compose services with the plain#call_serviceprimitive.
Compared to Interactor and
dry-transaction, assistant
keeps a single result shape regardless of success or failure, distinguishes
warnings from errors at the log-item level, and ships RBS signatures plus
a per-class generator (bin/assistant-rbs) out of the box.
Documentation
- Getting started —
docs/getting-started.mdwalks fromgem installto a first working service. - API reference —
docs/api-reference.mdis the hand-written, curated reference for every Frozen symbol; the source of truth for stability labels stays indocs/v1/01-api-surface.md. - Feature catalogue and rationale —
docs/v1/02-features.md. - Upgrading from 0.x —
docs/v1/06-migration-0x-to-1.md. - Deprecations —
docs/deprecations.md. - Runnable sample —
examples/greeter.rb. - Changelog —
CHANGELOG.md.
The deeper guides (docs/guides/inputs.md,
docs/guides/validation.md, docs/guides/logging-and-results.md,
docs/guides/composing-services.md) are tracked in
docs/v1/03-documentation.md and land
in follow-up PRs.
Roadmap
The plan for the 1.0 release lives under
docs/v1/. Every "must" item is shipped; remaining
work is documentation and release-checklist tasks.
Development
bin/setup # install dependencies
bundle exec rake test # Minitest
bundle exec rubocop # style
bundle exec steep check --jobs=1 # type-check RBS signatures
bin/console # IRB session with the gem loaded
To install the gem locally for ad-hoc experimentation:
bundle exec rake install
Contributing
Bug reports and pull requests are welcome at https://github.com/ramongr/assistant. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is released under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Assistant project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the code of conduct.