Ravn::

Actor class

An Actor subclass that defers spawning children to a start method that is called automatically if instantiated in an Actor context.

Public Class Methods

[]( name )

Return the Ravn::Actor that corresponds with name.

# File lib/ravn/actor.rb, line 68
def self::[]( name )
        actor = @registry[ name ]
        return actor&.reference
end
clear_registry()

Clear any registered actors from the registry.

# File lib/ravn/actor.rb, line 107
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
logger_wrapper()

Create a new Concurrent-compatible logger that will route messages through the receiver’s logger.

# File lib/ravn/actor.rb, line 55
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
new( * )

Create a new Actor and start it if it’s being spawned.

# File lib/ravn/actor.rb, line 123
def initialize( * )
        super()

        # These actions are only called if this instance is being
        # spawned, rather than being instantianted directly via #new.
        if Concurrent::Actor.current
                self.start
                Ravn::Actor.register( self )
        end
end
register( actor )

Register the given actor as corresponding with its name.

# File lib/ravn/actor.rb, line 75
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.

# File lib/ravn/actor.rb, line 101
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.

# File lib/ravn/actor.rb, line 45
def self::to_spawn_options( name_or_opts, *args )
        options = super
        options[ :logger ] ||= self.logger_wrapper if $VERBOSE

        return options
end
unregister( actor )

Unregister the given actor. No error is raised if the actor wasn’t registered.

# File lib/ravn/actor.rb, line 89
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

Public Instance Methods

call_action_handler( action, *args )

Call an action handler for the given action, passing it the specified args.

# File lib/ravn/actor.rb, line 172
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.

# File lib/ravn/actor.rb, line 184
def call_event_handler( event )
        handler = "handle_%s_event" % [ event ]

        if self.respond_to?( handler )
                return self.public_send( handler )
        end
end
filter_down( message )

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.

# File lib/ravn/actor.rb, line 229
def filter_down( message )
        message.audit {}
        self.log.debug "Filtering a message down the tree." if $VERBOSE
        self.propagate_message( message )
end
filter_up( message )

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.

# File lib/ravn/actor.rb, line 239
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.

# File lib/ravn/actor.rb, line 305
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.

# File lib/ravn/actor.rb, line 194
def handle_terminated_event
        self.log.warn "Terminated."
end
has_children?()

Returns true if the receiving Actor has spawned children.

# File lib/ravn/actor.rb, line 247
def has_children?
        # Do this via #core instead of the delegated #children in case it hasn't been
        # started yet.
        return self.core&.children&.size&.nonzero?
end
inspect_details()

Return the detailed part of the inspect output.

# File lib/ravn/actor.rb, line 277
def inspect_details
        if self.running_as_actor?
                return self.name.inspect
        else
                return "(not in an actor tree)"
        end
end
on_event( event )

Actor API – handle lower-level Actor events.

# File lib/ravn/actor.rb, line 164
def on_event( event )
        event = event.first if event.is_a?( Array )
        self.call_event_handler( event )
        super
end
on_message( message )

Actor API – handling incoming messages.

# File lib/ravn/actor.rb, line 146
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.

# File lib/ravn/actor.rb, line 255
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.

# File lib/ravn/actor.rb, line 262
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.

# File lib/ravn/actor.rb, line 217
def route_ravn_message( message )
        if self.routing_down?
                self.filter_down( message )
        else
                self.filter_up( message )
        end
end
routing_down?()

Return true if the current message is coming down the tree or if there is no tree.

# File lib/ravn/actor.rb, line 206
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
running_as_actor?()

Returns true if the receiver was started as an Actor.

# File lib/ravn/actor.rb, line 200
def running_as_actor?
        return self.core ? true : false
end
start()

Do any post-initialization startup (e.g., spawn children, etc.)

# File lib/ravn/actor.rb, line 140
def start
        # No-op
end
tree_node_description()

Return a single-line description of the node suitable for display in the dumped tree.

# File lib/ravn/actor.rb, line 327
def tree_node_description
        return "%s: %p" % [ self.name, self ]
end