Skip to content

Instantly share code, notes, and snippets.

@vladfaust

vladfaust/eda.cr Secret

Created January 3, 2019 16:14
Show Gist options
  • Save vladfaust/455b343f539188af43a91188d06a1a07 to your computer and use it in GitHub Desktop.
Save vladfaust/455b343f539188af43a91188d06a1a07 to your computer and use it in GitHub Desktop.
EDA demo
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