Leopard is a small framework for building concurrent NATS Service API workers.
It uses Concurrent::FixedThreadPool to manage multiple workers in a single process and provides a
minimal DSL for defining endpoints and middleware.
Declarative endpoint definitions with
#endpoint.Declarative JetStream pull consumers with
#jetstream_endpoint.Grouping of endpoints with
#groupSimple concurrency via
#runwith a configurable number of instances.JSON aware message wrapper that gracefully handles parse errors.
Middleware support using
#use.Railway Oriented Design, using Dry::Monads for success and failure handling.
Dry::Configurable settings container.
#loggerdefaults to SemanticLogger (adjustable as the#logger=setting)
Add the gem to your project:
# Gemfilegem'leopard'Then install it with Bundler.
$ bundle installCreate a service class and include Rubyists::Leopard::NatsApiServer.
Define one or more endpoints. Each endpoint receives a
Rubyists::Leopard::MessageWrapper object for each request to the NATS Service API endpoint
that service class is is subscribed to (subject:, or name:). The message handler/callback
is expected to return a Dry::Monads[:result] object, typically a Success or Failure.
classEchoServiceincludeRubyists::Leopard::NatsApiServerendpoint:echodo |msg|
Success(msg.data)endendRun the service by providing the NATS connection details and service options:
EchoService.run(nats_url: 'nats://localhost:4222',service_opts: {name: 'echo'},instances: 4)Middleware can be inserted around endpoint dispatch:
classLoggerMiddlewaredefinitialize(app)@app=appenddefcall(wrapper)puts"received: #{wrapper.data.inspect}"@app.call(wrapper)endendEchoService.useLoggerMiddlewareLeopard can also bind JetStream pull consumers through the same middleware and Dry::Monads::Result
handler contract used by request/reply endpoints.
classEventConsumerincludeRubyists::Leopard::NatsApiServerjetstream_endpoint(:events,stream: 'EVENTS',subject: 'events.created',durable: 'events-created-worker',consumer: {max_deliver: 5},batch: 5,fetch_timeout: 1,nak_delay: 2,)do |msg|
Success(msg.data)endendAny configuration options available for JetStream consumers can be passed via snakeified keys
in the consumer hash, with the exception of durable_name, filter_subject and ack_policy.
durable_name and filter_subject are both derived from the endpoint, and ack_policy is always
set to explicit.
JetStream handlers receive the same Rubyists::Leopard::MessageWrapper as service endpoints.
Leopard will:
ackonSuccessnakonFailure(nak_delay:is optional)termon unhandled exceptions
Each Leopard instances: worker creates its own pull subscription loop, so JetStream consumers
scale with the same process-local concurrency model as the rest of the framework.
The project uses Minitest and RuboCop. Run tests with Rake:
$ bundle exec rake ciThis task starts NATS JetStream through ./ci/nats/start.sh, waits for broker health,
runs RuboCop and the test suite, and then stops the broker.
API documentation can be generated with:
$ bundle exec rake yardDocumentation coverage is enforced with:
$ bundle exec rake yard:verifyIf you want to run the broker yourself, the same script can still be used directly:
$ ./ci/nats/start.shThis project follows the Conventional Commits specification.
To contribute, please follow that commit message format, or your pull request may be rejected.