Hmibo
How May I Be Of service!
Hmibo is a lightweight Ruby gem that provides simple, consistent patterns for service objects. Inspired by personal patterns, it offers structured error handling and logging for business logic in your Rails applications.
Features
- Simple Base Service Class: Clean pattern following personal conventions
- Structured Error Logging: Integrated with LoggerHead for contextual error logging
- Consistent Error Handling: Structured error collection with flexible formats
- Bulk Operations: Specialized service for bulk record creation with individual error tracking
- Rails Testing Helpers: RSpec helpers for easy service testing
- Minimal Dependencies: Uses LoggerHead for enhanced error logging (works great with Rails 7.1+)
Installation
Add this line to your application's Gemfile:
gem 'hmibo'
And then execute:
$ bundle install
Usage
Basic Service Object
class CreateUserService < Hmibo::Base
def initialize(name:, email:, role: 'user')
@name = name
@email = email
@role = role
super()
end
private
attr_reader :name, :email, :role
def perform
add_error("Name is required") if name.blank?
add_error("Email is required") if email.blank?
add_error("Email format is invalid") unless valid_email?
return self if errors?
user = User.create!(name: name, email: email, role: role)
@data = user
self
end
def valid_email?
email.match?(/\A[^@\s]+@[^@\s]+\z/)
end
end
# Usage
result = CreateUserService.call(name: "John Doe", email: "john@example.com")
if result.errors?
puts "Errors: #{result.errors.map { |e| e[:message] }.join(', ')}"
else
puts "User created: #{result.data.name}"
end
Bulk Creation Service
# Create multiple records with error handling
params = [
{ name: "John", email: "john@example.com", client_side_id: "temp-1" },
{ name: "Jane", email: "invalid-email", client_side_id: "temp-2" },
{ name: "Bob", email: "bob@example.com", client_side_id: "temp-3" }
]
result = Hmibo::BulkCreation.call(params, User)
if result.errors?
result.errors.each do |error|
puts "Error for #{error[:id]}: #{error[:message]}"
end
else
puts "All #{result.data.length} users created successfully"
end
Service Response
Every service returns itself with the following interface:
result = SomeService.call(params)
result.errors? # => true/false if there are errors
result.data # => Any data set by the service
result.errors # => Array of error hashes: [{message: "...", code: 422, id: nil}]
Error Handling
Services provide structured error handling with automatic logging:
class ExampleService < Hmibo::Base
private
def perform
# Add individual errors
add_error("Something went wrong")
# Errors are automatically logged with context using LoggerHead
return self if errors?
@data = { success: true }
self
end
end
Automatic Error Logging
Hmibo integrates with LoggerHead to provide structured error logging with context:
class CreateUserService < Hmibo::Base
def perform
# Any unhandled exceptions are automatically logged with context
raise StandardError, "Database connection failed"
end
end
result = CreateUserService.call
# Automatically logs:
# ERROR -- : There was an error in CreateUserService execution: Database connection failed
# ERROR -- : /path/to/backtrace...
Custom Error Context
You can provide custom context for error logging:
class PaymentService < Hmibo::Base
private
def perform
process_payment
rescue => error
log_error(error, context: "processing payment for user #{user_id}")
add_error("Payment processing failed")
self
end
end
Exception Classes
Hmibo provides custom exception classes:
-
Hmibo::ServiceError
- Base service error
Dependencies
Hmibo has one very lightweight dependency:
- LoggerHead - Structured error logging with context
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
License
The gem is available as open source under the terms of the MIT License.