An Actor
subclass that defers spawning children to a start
method that is called automatically if instantiated in an Actor
context.
Return the Ravn::Actor
that corresponds with name
.
def self::[]( name )
actor = @registry[ name ]
return actor&.reference
end
Clear any registered actors from the registry.
def self::clear_registry
return if @registry.empty?
self.log.warn "Clearing %d actors from the registry" % [ @registry.length ]
@registry.keys.each do |name|
actor = @registry.delete( name ) or next
actor.tell( :terminate! )
end
end
Create a new Concurrent-compatible logger that will route messages through the receiver’s logger.
def self::logger_wrapper
return lambda do |loglevel, progname, message = nil, &block|
progname = "%p %s" % [ self, progname ]
Loggability[ self ].add( loglevel, message, progname, &block )
end
end
Create a new Actor
and start it if it’s being spawned.
def initialize( * )
super()
if Concurrent::Actor.current
self.start
Ravn::Actor.register( self )
end
end
Register the given actor
as corresponding with its name
.
def self::register( actor )
name = actor.name&.to_sym or raise "Actor has no name"
if @registry.key?( name ) && @registry[ name ].reference != actor.reference
self.log.warn "Registering another %p actor; collides with %p" % [ name, actor ]
else
self.log.debug "Registering %p as %p" % [ actor, name ]
end
@registry[ name ] = actor
end
registered?( actor_name )
Returns true
if the given actor
is registered.
def self::registered?( actor_name )
return @registry.key?( actor_name )
end
to_spawn_options( name_or_opts, *args )
Overridden to inject the receiver’s logger if not already present.
def self::to_spawn_options( name_or_opts, *args )
options = super
options[ :logger ] ||= self.logger_wrapper if $VERBOSE
return options
end
Unregister the given actor
. No error is raised if the actor wasn’t registered.
def self::unregister( actor )
name = actor.name&.to_sym or raise "Actor has no name"
if (registered_actor = @registry.delete( name ))
self.log.debug "Unregistered %p actor from %p" % [ registered_actor, name ]
registered_actor.tell( :terminate! )
else
self.log.warn "Ignoring unregistration of unregistered %p: %p" % [ name, actor ]
end
end
call_action_handler( action, *args )
Call an action handler for the given action
, passing it the specified args
.
def call_action_handler( action, *args )
handler = "handle_%s" % [ action ]
if self.respond_to?( handler )
return self.public_send( handler, *args )
else
self.log.warn "Unhandled action %p (no #%s method)" % [ action, handler ]
end
end
call_event_handler( event )
Call an event handler for the given event
, passing it the specified args
.
def call_event_handler( event )
handler = "handle_%s_event" % [ event ]
if self.respond_to?( handler )
return self.public_send( handler )
end
end
Apply any necessary filtering to message
that route it to the receiver’s children. By default, this just passes the message along to every child unconditionally.
def filter_down( message )
message.audit {}
self.log.debug "Filtering a message down the tree." if $VERBOSE
self.propagate_message( message )
end
Apply any necessary filtering to message
that routes it to the receiver’s parent. By default, this just passes the message along to the parent actor unconditionally.
def filter_up( message )
message.audit {}
self.log.debug "Filtering a message up the tree via %p." % [ self.parent ] if $VERBOSE
self.parent.tell( message )
end
handle_dump_tree( tree=nil )
Return a representation of the actor tree starting with the receiver as the root as a TTY::Tree object.
def handle_dump_tree( tree=nil )
tree ||= TTY::Tree.new
desc = self.tree_node_description
self.log.debug "Adding node to tree: %p" % [ desc ]
if self.children.empty?
tree.node( desc, LeafTreeNode )
else
tree.node( desc, TreeNode ) do |*|
self.children.each do |child|
child.ask!( dump_tree: tree )
end
end
end
return tree
end
handle_terminated_event()
Default termination handler.
def handle_terminated_event
self.log.warn "Terminated."
end
Returns true
if the receiving Actor
has spawned children.
def has_children?
return self.core&.children&.size&.nonzero?
end
Return the detailed part of the inspect
output.
def inspect_details
if self.running_as_actor?
return self.name.inspect
else
return "(not in an actor tree)"
end
end
Actor
API – handle lower-level Actor
events.
def on_event( event )
event = event.first if event.is_a?( Array )
self.call_event_handler( event )
super
end
Actor
API – handling incoming messages.
def on_message( message )
case message
when Ravn::Message
self.log.debug "Handling a %p" % [ message.class ] if $VERBOSE
return self.route_ravn_message( message )
when Array
self.log.debug "Handling a action-style message: %p" % [ message ] if $VERBOSE
self.call_action_handler( *message )
when Hash
self.log.debug "Handling a action-style message: %p" % [ message ] if $VERBOSE
self.call_action_handler( *(message.each_pair.first) )
else
self.log.debug "No handler for %p." % [ message ]
end
end
propagate_message( message )
Propagate a message
to each of the actor’s children.
def propagate_message( message )
self.children.each {|child| child.tell(message) } if self.has_children?
end
reflect_message( message )
Reflect the given message
to all the Actor’s children except the sender. If the receiver is not running in an Actor
truee, the message is ignored.
def reflect_message( message )
if self.running_as_actor?
sending_child = self.envelope.sender
self.log.debug "Reflecting message to all children except %p" %
[ sending_child ] if $VERBOSE
self.children.filter {|child| child != sending_child }.each do |child|
child.tell( message )
end
else
self.log.warn "Not reflecting %p: not running as an actor." % [ message ]
end
end
route_ravn_message( message )
Route the given message
.
def route_ravn_message( message )
if self.routing_down?
self.filter_down( message )
else
self.filter_up( message )
end
end
Return true
if the current message is coming down the tree or if there is no tree.
def routing_down?
unless @envelope
self.log.debug "Routing down because actor is outside of a tree"
return true
end
return (@envelope.sender == self.parent)
end
Returns true
if the receiver was started as an Actor
.
def running_as_actor?
return self.core ? true : false
end
Do any post-initialization startup (e.g., spawn children, etc.)
Return a single-line description of the node suitable for display in the dumped tree.
def tree_node_description
return "%s: %p" % [ self.name, self ]
end