An extension of discordcr's
adds middleware functionality, similar to that of webserver libraries. The goal
is to provide a very customizable way to conditionally execute event handlers,
and make great reuse of code and state between events.
Add this to your application's
dependencies: discordcr-middleware: github: z64/discordcr-middleware
Require the extension:
Any class that implements
def call(payload, context, &block) can be used as
class Middleware def call(payload, context) # Do things with payload and context.. yield end end
payload is the
Discord event payload that triggered the event handler. A
middleware can be designed to handle multiple kinds of payloads by overloading
call and restricting
payload to different types:
class Middleware def call(payload : Discord::Message, context) # Do things with a message.. yield end def call(payload : Discord::Gateway::PresenceUpdatePayload, context) # Do things with a presence update.. yield end end
If you try to use a middleware class with an event that it doesn't support, this will result in a compile-time error.
context is an instance of
Context. This is a special class that allows
you to store arbitrary
class objects to be referenced later in the middleware
chain. How the library handles
context and how it should be used will be
outlined in the next section, but in brief:
class Foo end foo = Foo.new context.put(foo) context[Foo] == foo # => true
&block passed to
call is a block to be yielded to that
determines whether or not middleware that follow should be executed.
You can define an
#initialize for your middleware that will let you configure
how you want your middleware to behave for different event handlers, as opposed
to creating another middleware class with extremely similar behavior.
Usage with Client
Any event handler can have a middleware chain applied to it.
The event handling process goes like this:
- The message event is received by the client
- An "empty"
Contextis initialized, and the receiving
Clientis added to it
- Each middleware in the chain is added to
Context. This allows you to access the middleware that have run previously in the chain, either from inside one of the middleware or in the event handler itself.
- The event is passed through each middleware, providing that each one
yields. If any middleware does not
yield, execution of the rest of the chain will stop.
client.on_message_create(MiddlewareA.new, MiddlewareB.new) do |payload, context| payload # => Discord::Message context # => Discord::Context context[Discord::Client] == client # => true context[MiddlewareA] # => MiddlewareA context[MiddlewareB] # => MiddlewareB end
It's also worth noting you can share the same instance of a middleware between multiple event handlers:
middleware = Middleware.new client.on_message_create(middleware) do |payload| # ... end client.on_message_update(middleware) do |payload| # ... end
This is useful, for example, for a middleware that has some kind of state that
affects multiple handlers. You could have a single
Prefix middleware instance
that stores the client's prefix in memory, so that when you update the prefix,
the runtime behavior on all of your handlers updates at once.
The event handler block is also optional, making it possible to have pure-middleware chains:
A collection of basic, common use-case middleware are provided in
Require them explicitly to make use of them:
require "discordcr-middleware/middleware/prefix" DiscordMiddleware::Prefix.new("!help")
- z64 - creator, maintainer
Inspired by the raze web framework and its middleware system.