Class: DuoRuby::Channel

Inherits:
Object
  • Object
show all
Extended by:
HandlerMethods
Defined in:
lib/duoruby/channel.rb,
lib/duoruby/channel/namespace.rb,
lib/duoruby/channel/handler_methods.rb

Overview

Base class for event-driven components in DuoRuby.

Channel provides a flexible pub/sub event system with support for:

  • Class-level handler declarations that are inherited by subclasses

  • Module-level handler declarations that merge into including classes

  • Per-instance handler isolation (class handlers are deep-cloned on initialize)

  • One-shot handlers via #one

  • Wildcard handlers that run on every event via #on with the * event name

  • Removal by event name, proc reference, or Handler token

Server and Socket both inherit from Channel.

Examples:

Declaring handlers at the class level (inherited by instances)

class MyServer < DuoRuby::Server
  on(:join) { |client, room:| group(room) << client }
end

Adding handlers on an instance

server = MyServer.new
token = server.on(:join) { |client, room:| puts "#{client.id} joined #{room}" }
server.off(token)  # remove by token

Direct Known Subclasses

Server, Socket

Defined Under Namespace

Modules: HandlerMethods Classes: Handler, Namespace

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HandlerMethods

included, included

Constructor Details

#initializeChannel

Deep-clones the class-level handlers into this instance so that instance-level #on/#off calls do not affect the class or other instances.



48
49
50
# File 'lib/duoruby/channel.rb', line 48

def initialize
  @handlers = self.class.__send__(:clone_handlers, self.class.handlers)
end

Instance Attribute Details

#handlersHash{String => Array<Handler>} (readonly)

Returns the event-to-handlers map for this object.

Returns:

  • (Hash{String => Array<Handler>})

    the event-to-handlers map for this object



30
31
32
# File 'lib/duoruby/channel.rb', line 30

def handlers
  @handlers
end

Class Method Details

.inherited(subclass) ⇒ Object

Copies the parent class’s handlers into the subclass when a subclass is defined.



41
42
43
44
# File 'lib/duoruby/channel.rb', line 41

def self.inherited(subclass)
  subclass.__send__(:merge_handlers, handlers)
  super
end

Instance Method Details

#channel(name) ⇒ Object



98
99
100
# File 'lib/duoruby/channel.rb', line 98

def channel(name)
  Namespace.new(self, name)
end

#dispatch(event, *args, **params) ⇒ nil Also known as: trigger

Dispatches event to all registered handlers, then to any wildcard (+*+) handlers.

All handlers are invoked via instance_exec so they run in the context of this Channel instance. Positional args and keyword params are forwarded as-is. One-shot handlers are removed immediately after firing.

Parameters:

  • event (String, Symbol)

    the event name

  • args

    positional arguments to forward (e.g. the client in Server handlers)

  • params

    keyword arguments to forward (e.g. message params)

Returns:

  • (nil)


124
125
126
127
128
129
# File 'lib/duoruby/channel.rb', line 124

def dispatch(event, *args, **params)
  event = event.to_s
  results = dispatch_handlers(event, handlers[event], *args, **params)
  dispatch_handlers("*", handlers["*"], event, *args, **params)
  results
end

#handler_for(event) ⇒ Proc?

Returns a Proc wrapping the first registered handler for event, bound to this instance via instance_exec. Returns nil if no handler exists. Used internally; may be useful for adapter integrations.

Parameters:

  • event (String, Symbol)

Returns:

  • (Proc, nil)


108
109
110
111
112
# File 'lib/duoruby/channel.rb', line 108

def handler_for(event)
  event = event.to_s
  handler = handlers[event]&.first
  proc { |*args, **params| instance_exec(*args, **params, &handler.block) } if handler
end

#off(event = nil, handler = nil, &block) ⇒ Object

Removes instance-level handlers. See DuoRuby::Channel::HandlerMethods#off for the full removal semantics — behaviour is identical at the instance level.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/duoruby/channel.rb', line 81

def off(event = nil, handler = nil, &block)
  target = handler || block
  return handlers.clear unless event

  if event.is_a?(Handler)
    handlers[event.event]&.delete_if { |r| handler_identity?(r, event) }
    handlers.delete(event.event) if handlers[event.event]&.empty?
    return
  end

  event = event.to_s
  return handlers.delete(event) unless target

  handlers[event]&.delete_if { |r| handler_identity?(r, target) }
  handlers.delete(event) if handlers[event]&.empty?
end

#on(event, &handler) ⇒ Handler

Registers a persistent instance-level handler for event. Instance handlers stack on top of any class-level handlers that were copied in at initialize time.

Parameters:

  • event (String, Symbol)

    event name; use “*” to match every event

Returns:

Raises:

  • (ArgumentError)


58
59
60
61
62
63
64
# File 'lib/duoruby/channel.rb', line 58

def on(event, &handler)
  raise ArgumentError, "handler required" unless handler

  event = event.to_s
  handlers[event] ||= []
  Handler.new(event, handler, false).tap { |registered| handlers[event] << registered }
end

#one(event, &handler) ⇒ Handler

Registers a one-shot instance-level handler for event. Automatically removed after the first dispatch.

Parameters:

  • event (String, Symbol)

    event name

Returns:

Raises:

  • (ArgumentError)


71
72
73
74
75
76
77
# File 'lib/duoruby/channel.rb', line 71

def one(event, &handler)
  raise ArgumentError, "handler required" unless handler

  event = event.to_s
  handlers[event] ||= []
  Handler.new(event, handler, true).tap { |registered| handlers[event] << registered }
end