Observers
Observe objects/keys of any kind and trigger actions and events on them.
Observers are decoupled from the objects they observe. Instead of directly observing a particular object, they observe a key that represents that object. Anything can be observed out of the box; a class, instance, struct, symbol or string... you just need to observe it:
class MySubscriber
include Observers
observe MyPublisher
def self.handle
# This method will be called upon trigger.
end
endAdd observers from the object being observed with:
class MyPublisher
include Observers
observers << MySubscriber
endℹ️ Observers are ordered, called in the order that they are defined and can be reordered via an array-like interface.
Triggers
Actions
Call the my_action method on all observers of MyPublisher with:
class MyPublisher
include Observers
trigger action: :my_action
endEvents
Trigger events on observers with the event keyword argument:
class MyPublisher
include Observers
trigger action: :my_action, event: MyEvent.new(my_data)
endℹ️ All observers to MyPublisher will have their my_action(event:) method called with the event passed in as a keyword argument.
Keys
Call actions on all observers of a differeent object/key with a key: keyword argument:
trigger key: OtherPublisher, action: :my_action
trigger key: OtherPublisher, action: :my_action
trigger key: OtherPublisher, action: :my_action, event: MyEvent.new(event_data)Integrations
LowEvent
Observers integrates with LowEvent for a more event-centric API.
Inherit you event class from LowEvent:
class MyEvent < LowEvent
def initialize(data:, action: :render)
super(key: self.class, action:)
@data = data
end
endObserve it with:
class MyObserver
include Observers
observe MyEvent
def render(event:)
event.data
end
endTrigger the event and its observer with:
MyEvent.trigger(data: "Rendered") # => "Rendered"ℹ️ Events define their own actions.
API
Default Action
The default action that will be called on an observer is handle/handle(event:). This happens when the trigger's, event's or observer's action are nil.
Overriding Actions
An action can be overridden at each layer:
- On the
triggermethod by including anaction:keyword argument - On the event by populating its
actionattribute - On the observer by configuring an
action:onobserve:
class MySubscriber
include Observers
observe MyPublisher, action: :clear_cache
def self.clear_cache
# The `clear_cache` method will be called regardless of the trigger's action/event's action.
end
endAction Precedence
-
observe action:andobservers << my_object, action:- Overridestriggerand event actions -
trigger action:- Overrides event actions - Event's
@action- Overrides the default action of each observer
Observers
The observers method is more flexible than it seems.
Reference an object other than self to observe:
observers(my_object) << my_observerOverride the default action of the observer with:
observers.push(my_observer, action: :overridden_action)ℹ️ Note: push needs to be used instead of << in this situation so that Ruby doesn't get confused about the syntax.
Actions
-
trigger- Calls all observers. Returns the last non-nil value. -
take- Calls all observers up until the first non-nil value. Returns the first non-nil value.
Config
Copy and paste the following and change the defaults to configure Observers:
# This configuration should be set before the class that includes Observers is required.
Observers.configure do |config|
# A lambda to call when actions/events are triggered for a key without observers.
config.empty_observers_callback = nil # Or "->(key) { MyLogger.log('my message') }"
endInstallation
Add gem 'observers' to your Gemfile then:
bundle install