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