Eventish
Yet another events library with a simple API to handle... events 🎉
Main features:
- composable: just require the components that you need;
- with adapters: support ActiveSupport::Notifications and Wisper for pub/sub events;
- with async events: support ActiveJob for events background execution;
- with callbacks wrapper: support ActiveRecord callbacks;
- with plugins: a simple logger and a Rails logger are included;
- with conditional event execution (overriding
callable?
).
This component can be useful to:
- decouple callbacks from the side-effect;
- permit to test the side-effects in isolation;
- permit to spy/block the side-effects;
- permit to execute the side-effects from other contexts.
Please ⭐ if you like it.
Do you want to speak by events? Try eventish 😄
Install
- Add to your Gemfile:
gem 'eventish'
(and executebundle
) - Create an initializer - ex. config/initializers/eventish.rb:
require 'eventish/adapters/active_support'
Eventish.setup do |config|
config.adapter = Eventish::Adapters::ActiveSupport
end
Rails.configuration.to_prepare do
# NOTE: required to load the event descendants when eager_load is off
unless Rails.configuration.eager_load
events = Rails.root.join('app/events/**/*.rb').to_s
Dir[events].sort.each { |event| require event }
end
end
Rails.configuration.after_initialize do
Eventish::SimpleEvent.subscribe_all # NOTE: events will be available after this point
Eventish.publish('app_loaded') # just a test event
end
- Create some events - ex. app/events/main/app_loaded_event.rb:
module Main
class AppLoadedEvent < Eventish::SimpleEvent
def call(_none, _options = {})
puts '> App loaded event'
end
end
end
For a complete example please take a look at the dummy app in the specs.
Adapters
Used for events subscription and publishing.
ActiveSupport Notification:
# initializer setup
require 'eventish/adapters/active_support'
Eventish.setup do |config|
config.adapter = Eventish::Adapters::ActiveSupport
end
Wisper (NOTE: around callback wrappers are not supported):
# initializer setup
require 'wisper'
require 'eventish/adapters/wisper'
Eventish.setup do |config|
config.adapter = Eventish::Adapters::Wisper
end
Simple events
Generic events not related to a specific component.
# initializer setup
Rails.configuration.after_initialize do
# Subscribe all Eventish::SimpleEvent descendants using the configured adapter
# The descendants event classes must be loaded before this point - see eager_load notes in the Install section
Eventish::SimpleEvent.subscribe_all
end
Sample event - ex. app/events/main/test_event.rb:
module Main
class TestEvent < Eventish::SimpleEvent
def call(_none, _options = {})
puts '> A test event'
end
class << self
def event_name
# this is optional, if not set the class name to string will be used
'some_event'
end
end
end
end
Publish the event with:
Eventish.publish('some_event')
Async events
Events executed in a background process. Only ActiveJob is supported for now.
# initializer setup
require 'active_job/railtie'
require 'eventish/active_job_event'
Rails.configuration.after_initialize do
Eventish::ActiveJobEvent.subscribe_all
end
Sample event - ex. app/events/notifications/user_after_save_commit_event.rb:
module Notifications
class UserAfterCommitEvent < Eventish::ActiveJobEvent
def call(user, _options = {})
Rails.logger.info ">>> User ##{user.id} after commit notification"
end
end
end
Callbacks
Wrappers for ActiveRecord callbacks using the postfix _event
(ex. after_commit_event SomeEvent
).
# initializer setup
require 'eventish/active_record/callback'
# sample model
class SomeModel < ActiveRecord::Base
extend ::Eventish::ActiveRecord::Callback
before_validation_event SomeBeforeValidationEvent
end
The related callback will be setup by the wrapper and the specified event class will be invoked accordingly.
Plugins
A plugins system is available for custom processing, a logger and a Rails logger are included in the gem.
# initializer setup
require 'eventish/plugins/rails_logger' # without rails_ for a simple stdout logger
Eventish.setup do |config|
config.before_event = [Eventish::Plugins::RailsLogger]
config.after_event = [Eventish::Plugins::RailsLogger]
end
A sample plugin:
module Eventish::Plugins::RailsLogger
class << self
def call(target, _args, event:, hook: nil, &_block)
Rails.logger.debug "EVENT: #{hook} #{event.class.event_name} on #{target.inspect}"
end
end
end
Plugins can also be configured for single events overriding before_event and after_event.
Conditional events
Optionally an event can override the callable?
method to define preconditions on the execution of the call
method.
Example:
class TestEvent < Eventish::SimpleEvent
def callable?(target)
target.ready?
end
def call(target, _options = {})
puts '> A test event'
end
end
Do you like it? Star it!
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: about me.
Contributors
- Mattia Roccoberton: author
License
The gem is available as open-source under the terms of the MIT.