lib/vissen/input/message/base.rb in vissen-input-0.2.2 vs lib/vissen/input/message/base.rb in vissen-input-0.3.0
- old
+ new
@@ -11,11 +11,13 @@
# factory for all the implementations that it knows about (see
# `.factory`).
class Base
include Message
+ # @see Message
DATA_LENGTH = 3
+ # @see Message
STATUS = 0
# Checks message data consistency with the class default matcher.
#
# @return [true, false] true if the message data matches the class
@@ -23,10 +25,13 @@
def valid?
self.class.matcher.match? data
end
class << self
+ extend Forwardable
+ # rubocop:disable Metrics/AbcSize
+
# Returns a new instance of a Matcher, configured to match this
# particular Message class. Subclasses of Base can utilize the same
# functionality by simply redefining STATUS and, if necessary,
# STATUS_MASK.
#
@@ -52,18 +57,30 @@
else
Matcher.new(self) { |d| (d[0] & mask) == val }
end
end
- # Accessor for the class default matcher.
+ # rubocop:enable Metrics/AbcSize
+
+ # @!method match?(obj)
+ # Accessor to `Matcher#match?` on the default class matcher.
#
- # @param message [#to_a] the message or data to match.
- # @return [true, false] see `Matcher#match?`.
- def match?(message)
- matcher.match? message
- end
+ # @see Matcher#match?
+ #
+ # @param obj [#to_a] the message or data to match.
+ # @return [true, false] (see Matcher#match?)
+ def_delegator :klass_matcher, :match?, :match?
+ # @!method match(obj)
+ # Accessor to `Matcher#match` on the default class matcher.
+ #
+ # @see Matcher#match
+ #
+ # @param obj [Hash, Message] the message to match.
+ # @return [false, Object, Message] (see Matcher#match)
+ def_delegator :klass_matcher, :match, :match
+
# Creates a new factory with all the subclasses of base added to it as
# matchers.
#
# @return [MessageFactory] a factory configured to build all
# subclasses of Base.
@@ -72,10 +89,12 @@
MessageFactory.new @subclasses.map(&:matcher)
end
# Alias to `#matcher` that swaps named arguments for positional ones.
#
+ # @see #matcher
+ #
# @param (see #matcher)
# @return (see #matcher)
def [](channel, number = nil)
matcher channel: channel, number: number
end
@@ -114,31 +133,75 @@
new data, timestamp
end
protected
+ # Called automatically by inheriting classes.
+ #
+ # @param subclass [Base] the inheriting class.
+ # @return [nil]
def inherited(subclass)
(@subclasses ||= []) << subclass
+ nil
end
+ # Helper method to validate a status value.
+ #
+ # @raise [RangeError] if the status is outside its allowable range.
+ #
+ # @param status [Integer] the status to validate.
+ # @return [true] when the status is valid.
def validate_status(status)
raise RangeError unless (status & ~STATUS_MASK).zero?
+ true
end
+ # Helper method to validate a channel value.
+ #
+ # @raise [RangeError] if the channel is outside its allowable range.
+ #
+ # @param channel [Integer] the channel to validate.
+ # @return [true] when the channel is valid.
def validate_channel(channel)
raise RangeError unless (channel & ~CHANNEL_MASK).zero?
+ true
end
+ # Creates a value and mask that can be used to match the first byte of
+ # a message.
+ #
+ # == Usage
+ # The following example illustrates how the value and mask are
+ # intended to be used.
+ #
+ # value, mask = status_value_and_mask
+ # 0x42 & mask == value # => true if STATUS == 0x40
+ #
+ # value, mask = status_value_and_mask 3
+ # 0x42 & mask == value # => false since channel is 2
+ #
+ # @param channel [Integer] the channel to match.
+ # @return [Array<Integer>] a value and a mask.
def status_value_and_mask(channel = nil)
if channel
validate_channel channel
[(self::STATUS & self::STATUS_MASK) + (channel & CHANNEL_MASK),
self::STATUS_MASK + CHANNEL_MASK]
else
[self::STATUS, self::STATUS_MASK]
end
end
+ # The klass matcher is the most generic `Matcher` for the message
+ # class and is cached to avoid duplication. By default the default
+ # `Matcher` uses the value and mask returned by
+ # `#status_value_and_mask` to match messages. Subclasses that need
+ # different behaviour can pass a block to be forwarded directly to the
+ # matcher (see Matcher.new).
+ #
+ # @param block [Proc] the block that should be passed to
+ # `Matcher.new` when first creating the matcher.
+ # @return [Matcher] a matcher that matches all messages of this type.
def klass_matcher(&block)
return @klass_matcher if defined?(@klass_matcher)
unless block_given?
val, mask = status_value_and_mask