-
-
Save vladfaust/455b343f539188af43a91188d06a1a07 to your computer and use it in GitHub Desktop.
EDA demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Onyx::EDA | |
abstract struct Event | |
end | |
end | |
class Onyx::EDA | |
abstract class Channel | |
# Send *event* to this channel. | |
abstract def send(event : Event) | |
# Send *events* to this channel. | |
abstract def send(events : Enumerable(Event)) | |
# Subscribes *proc* to *event*. | |
# Returns `true` in case of success, i.e. it hasn't been already subscribed. | |
def subscribe(event : T.class, &proc : Proc(T, Nil)) : Bool forall T | |
set = @subscribers[event]? | |
unless set | |
set = Set(Tuple(Void*, Void*)).new | |
@subscribers[event] = set | |
end | |
tuple = {proc.pointer, proc.closure_data} | |
!!unless set.includes?(tuple) | |
set << tuple | |
end | |
end | |
# Unsubscribe a *proc* subscriber from *event*. | |
# Returns `false` if such a subscriber is not in the subscribers list | |
# (probably has already been unsubscribed or never been at all). | |
def unsubscribe(event : T.class, &proc : Proc(T, Nil)) : Bool forall T | |
set = @subscribers[event]? | |
return false unless set | |
tuple = {proc.pointer, proc.closure_data} | |
result = !!if set.includes?(tuple) | |
set.delete(tuple) | |
end | |
unless set.size > 0 | |
@subscribers.delete(event) | |
end | |
return result | |
end | |
# Unsubscribe **all** subscribers from *event*. | |
# Returns `false` in case if there were no any subscribers to the event. | |
def unsubscribe(event : T.class) : Bool forall T | |
!!@subscribers.delete(event) | |
end | |
# Will only call subscribers' procs subscribed | |
# to this particular *event* and or to its descendants. | |
# | |
# ``` | |
# abstract struct AbstractEvent < Onyx::EDA::Event | |
# end | |
# | |
# struct BarEvent < AbstractEvent | |
# getter bar | |
# | |
# def initialize(@bar : String) | |
# end | |
# end | |
# | |
# channel.subscribe(BarEvent) do |event| | |
# puts "Got #{event.class}" | |
# end | |
# | |
# channel.subscribe(AbstractEvent) do |event| | |
# puts "Got #{event.class}" | |
# end | |
# | |
# channel.send(BarEvent.new) | |
# | |
# sleep | |
# | |
# # Got AbstractEvent | |
# # Got BarEvent | |
# ``` | |
protected def notify(event : Event) | |
matched = false | |
{% for subclass in Event.all_subclasses %} | |
if {{subclass}} === event | |
matched = true | |
@subscribers[{{subclass}}]?.try &.each do |(pointer, closure_data)| | |
Proc({{subclass}}, Nil).new(pointer, closure_data).call(event.as({{subclass}})) | |
end | |
end | |
{% end %} | |
raise "BUG: Unknown event #{event.class}" unless matched | |
end | |
# Subscribers to this channel by `Event`. | |
# Due to inability of storing `Proc(T)` as an instance variable, | |
# its internal representation -- two pointers -- is used instead. | |
@subscribers = Hash(Event.class, Set(Tuple(Void*, Void*))).new | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment