Gaskit
Gaskit is a flexible, pluggable, and structured operations framework for Ruby and Rails applications. It provides a consistent way to implement application logic using service objects, query objects, flows, and contracts โ with robust support for early exits, structured logging, duration tracking, and failure handling.
โจ Features
- โ
Operation
,Service
, andQuery
classes - ๐ Customizable result and early exit contracts via
use_contract
- ๐งฑ Composable multi-step flows using
Flow
DSL - ๐งช Built-in error declarations and early exits via
exit(:key)
- โฑ Integrated duration tracking and structured logging
- ๐งฐ Generators for Rails to scaffold operations, services, queries, flows, and repositories
- ๐ช Hook system for before/after/around instrumentation and auditing
๐ฆ Installation
Add this line to your application's Gemfile:
gem 'gaskit'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install gaskit
๐ง Configuration
You can configure Gaskit via an initializer:
Gaskit.configure do |config|
config.context_provider = -> { { request_id: RequestStore.store[:request_id] } }
config.setup_logger(Logger.new($stdout), formatter: Gaskit::Logger.formatter(:pretty))
end
๐ Usage
Define an Operation
class MyOp < Gaskit::Operation
use_contract :service
def call(user_id:)
user = User.find_by(id: user_id)
exit(:not_found, "User not found") unless user
logger.info("Found user id=#{user_id}")
user
end
end
Handle Result
result = MyOp.call(user_id: 42)
if result.success?
puts "Found user: #{result.value}"
elsif result.early_exit?
puts "Early exit: #{result.to_h[:exit]}"
else
puts "Failure: #{result.to_h[:error]}"
end
Define Errors
class AuthOp < Gaskit::Operation
error :unauthorized, "Not allowed", code: "AUTH-001"
def call
exit(:unauthorized)
end
end
Composing Flows
class CheckoutFlow < Gaskit::Flow
step AddToCart
step ApplyDiscount
step FinalizeOrder
end
result = CheckoutFlow.call(user_id: 123)
๐ช Hooks
Use use_hooks
to activate instrumentation:
class HookedOp < Gaskit::Operation
use_contract :service
use_hooks :audit
before do |op|
op.logger.info("Starting operation")
end
after do |op, result:, error:|
op.logger.info("Finished with result=#{result.inspect} error=#{error.inspect}")
end
def call
"hello"
end
end
Register global hooks via:
Gaskit.hooks.register(:before, :audit) { |op| puts "Before: #{op.class}" }
๐งช Generators
# Generate an operation
rails generate gaskit:operation CreateUser
# Generate a query
rails generate gaskit:query FetchUsers
# Generate a service
rails generate gaskit:service RegisterAccount
# Generate a flow
rails generate gaskit:flow Checkout AddToCart ApplyDiscount FinalizeOrder
# Generate a repository
rails generate gaskit:repository User
๐ Contracts
You can define contracts using registered result types:
class MyResult < Gaskit::OperationResult; end
Gaskit.contracts.register(:custom, MyResult)
class CustomOp < Gaskit::Operation
use_contract :custom
end
Or override just part of the contract:
class CustomResult < Gaskit::OperationResult; end
class PartialContractOp < Gaskit::Operation
use_contract :service, result: CustomResult
end
๐งฑ Repositories
class UserRepository < Gaskit::Repository
model User
def find_by_name_or_slug(name, profile_slug)
where(name: name).or(where(profile_slug: profile_slug))
end
end
users = UserRepository.where(active: true)
user = UserRepository.find_by_name_or_slug("User", "user123")
๐ Logging
Gaskit includes a flexible logger with support for structured JSON or pretty logs:
logger = Gaskit::Logger.new(self.class)
logger.info("Started process", context: { user_id: 1 })
Planned Features
- Caching Flow operations to provide replaying and resume on failure.
๐ค Contributing
Bug reports and pull requests are welcome! Feel free to fork, extend, and share improvements.
๐ License
This gem is licensed under the MIT License.
Made with โค๏ธ by bnlucas