Ravn::

Message class

A generic message type for evented systems

Constants

OPTIONAL_FIELDS

An Array of Symbols containing the names of optional fields

REQUIRED_FIELDS

An Array of Symbols containing the names of required fields

SIMPLE_TYPES

Object classes which can be serialized as they are (i.e., do not require simplification)

VALID_FIELDS

An Array of Symbols containing the names of all valid fields

VALID_TYPE_PATTERN

Validation pattern for the type

Attributes

audit_trail R

The message’s audit trail, which is a list of frames from which audit has been called.

data R

The payload data associated with the event (a Hash)

id R

The message’s unique ID

mtime R

The monotonic time the message was created.

recast_from RW

The ID of the event this event was recast from, or nil if it hasn’t been recast

time R

The time the message was created.

type RW

The type of event.

Public Class Methods

new( type, fields={} )

Create a new event of the specified type and with the given payload.

# File lib/ravn/message.rb, line 109
def initialize( type, fields={} )
        raise ArgumentError, "invalid type %p" % [ type ] unless self.class.valid_type?( type )

        @type = type

        self.log.debug "Fields are: %p" % [ fields ]
        @id    = fields[ :id ]    || Ravn.uuid.generate
        @time  = fields[ :time ]  || Ravn.time
        @mtime = fields[ :mtime ] || Ravn.monotonic_time
        @data  = fields[ :data ]  || {}

        @recast_from = fields[ :recast_from ]

        @audit_trail = []
        self.audit {}
end
optional_fields()

Return an Array of Symbols that describe which fields are optional for messages.

# File lib/ravn/message.rb, line 61
def self::optional_fields
        return OPTIONAL_FIELDS
end
recast( original, new_type ) { |new_instance| ... }

Transform the given original event into an instance of the receiving class with a new_type and the equivalent data. Values from the original‘s data that correspond to fields in the new type will be converted to fields, and optional fields of the original that are not present in the new class will be converted to data values.

# File lib/ravn/message.rb, line 78
def self::recast( original, new_type )
        fields = Ravn::DataUtilities.deep_copy( original.fields )
        fields[:data] ||= {}
        self.log.debug "Recasting fields: %p" % [ fields ] if $VERBOSE

        # Move valid fields fields from fields[:data] to fields, but don't replace any
        # that already exist.
        (fields[ :data ].keys & self.valid_fields).each do |field|
                self.log.debug "Moving %p from data to a top-level field." % [ field ] if $VERBOSE
                value = fields[:data].delete( field )
                fields[ field ] ||= value
        end

        # Move invalid fields from fields to fields[:data]
        (fields.keys - self.valid_fields).each do |field|
                self.log.debug "Moving %p from a top-level field to data." % [ field ] if $VERBOSE
                fields[ :data ][ field ] = fields.delete( field )
        end

        fields[ :recast_from ] = fields.delete( :id )

        self.log.debug "  fields are now: %p" % [ fields ] if $VERBOSE
        new_instance = new( new_type, fields )
        yield( new_instance ) if block_given?
        new_instance.freeze if original.frozen?

        return new_instance
end
required_fields()

Return an Array of Symbols that describe which fields are required for messages.

# File lib/ravn/message.rb, line 54
def self::required_fields
        return REQUIRED_FIELDS
end
valid_fields()

Return an Array of Symbols that describe which fields are valid for messages.

# File lib/ravn/message.rb, line 68
def self::valid_fields
        return self.required_fields | self.optional_fields
end
valid_type?( type_string )

Returns true if the given type_string is a valid Helios message type.

# File lib/ravn/message.rb, line 47
def self::valid_type?( type_string )
        return VALID_TYPE_PATTERN.match?( type_string )
end

Public Instance Methods

audit( &block )

Add a callsite to the message’s audit trail.

# File lib/ravn/message.rb, line 230
def audit( &block )
        if block
                self.audit_trail << self.current_audit_callsite( block )
        else
                self.log.warn "Audit with no block!"
        end
rescue => err
        self.log.error( err )
end
current_audit_callsite( block )

Return a description of the top frame of the current call stack for auditing.

# File lib/ravn/message.rb, line 250
def current_audit_callsite( block )
        loc = block.binding
        return "%p [%s:%d]" % [ loc.receiver, *loc.source_location ]
end
fields()

Return the fields of the message as a Hash.

# File lib/ravn/message.rb, line 170
def fields
        return self.optional_fields.merge( self.required_fields )
end
initialize_copy( original )

Copy initializer — copy the internal datastructures too.

# File lib/ravn/message.rb, line 128
def initialize_copy( original )
        super

        @id = Ravn.uuid.generate
        @data = deep_copy( original.data )
end
inspect_details()

Return the detail part of the inspect output.

# File lib/ravn/message.rb, line 257
def inspect_details
        details = "{%s} id: %s time: %0.3f" % [
                self.type,
                self.id,
                self.time,
        ]

        details << " (recast from %s)" % [ self.recast_from ] if self.recast_from

        return details
end
log_audit_trail()

Write a log message containing the Message’s current audit trail.

# File lib/ravn/message.rb, line 242
def log_audit_trail
        trail = self.audit_trail.map {|frame| "  %s" % [frame] }.join( "\n" )
        self.log.debug "Audit trail: \n%s" % [ trail ]
end
optional_fields()

Return a Hash of the options fields of the message which have been set.

# File lib/ravn/message.rb, line 185
def optional_fields
        return self.class.optional_fields.each_with_object( {} ) do |field, hash|
                value = self.public_send( field )
                hash[ field ] = simplify( value ) if value
        end
end
recast( new_type ) { |new_message| ... }

Return a clone of the receiver, but with a new_type.

# File lib/ravn/message.rb, line 194
def recast( new_type )
        new_message = self.dup
        new_message.type = new_type

        yield( new_message ) if block_given?

        new_message.recast_from = self.id
        new_message.freeze if self.frozen?

        return new_message
end
required_fields()

Return a Hash of the required fields of the message.

# File lib/ravn/message.rb, line 176
def required_fields
        return self.class.required_fields.each_with_object( {} ) do |field, hash|
                value = self.public_send( field )
                hash[ field ] = simplify( value )
        end
end
type_category()

Return the category (second segment) part of the message type.

# File lib/ravn/message.rb, line 215
def type_category
        match = self.type_match
        return match[ :category ]
end
type_details()

Return the details (trailing segments) part of the message type as an Array of Strings.

# File lib/ravn/message.rb, line 223
def type_details
        match = self.type_match
        return match[ :details ]&.split( '.' )[ 1..-1 ]
end
type_source()

Return the source (prefix) part of the message type.

# File lib/ravn/message.rb, line 208
def type_source
        match = self.type_match
        return match[ :source ]
end

Protected Instance Methods

type_match()

Return a MatchData object from matching the message’s type against the VALID_TYPE_PATTERN.

# File lib/ravn/message.rb, line 277
def type_match
        return self.type.match( VALID_TYPE_PATTERN )
end