Pushdown::SpecHelpers::

StateTransitionMatcher

class
Superclass
Object
Included Modules
RSpec::Matchers
Extended With
Loggability

RSpec matcher for matching transitions of Pushdown::States

Constants

DEFAULT_CALLBACK

Attributes

additional_expectations[R]
callback[R]
expected_type[R]
failure_description[R]
result[R]
state[R]
target_state[R]

Public Class Methods

anchor
new()

Create a new matcher that expects a transition to occur.

# File lib/pushdown/spec_helpers.rb, line 25
def initialize
        @expected_type = nil
        @target_state = nil
        @callback = nil
        @additional_expectations = []
        @state = nil
        @result = nil
        @failure_description = nil
end

Public Instance Methods

anchor
failure_message()

RSpec matcher API – return a message describing an expectation failure.

# File lib/pushdown/spec_helpers.rb, line 59
def failure_message
        return "%p: %s" % [ self.state, self.describe_failure ]
end
anchor
failure_message_when_negated()

RSpec matcher API – return a message describing an expectation being met when the matcher was used in a negated context.

# File lib/pushdown/spec_helpers.rb, line 66
def failure_message_when_negated
        return "%p: %s" % [ self.state, self.describe_negated_failure ]
end
anchor
matches?( state )

RSpec matcher API – returns true if all expectations are met after calling

update on the specified state.

# File lib/pushdown/spec_helpers.rb, line 47
def matches?( state )
        @state = state

        return self.update_ran_without_error? &&
                self.update_returned_transition? &&
                self.correct_transition_type? &&
                self.correct_target_state? &&
                self.matches_additional_expectations?
end
anchor
on_an_event( event, *args )

Specify that the operation that should cause the transition is the on_event callback.

# File lib/pushdown/spec_helpers.rb, line 105
def on_an_event( event, *args )
        raise ScriptError, "can't specify more than one callback" if self.callback
        @callback = [ :on_event, event, *args ]
        return self
end
Also aliased as: on_event
anchor
on_event( event, *args )
Alias for: on_an_event
anchor
on_update()

Specify that the operation that should cause the transition is the update callback. This is the default.

# File lib/pushdown/spec_helpers.rb, line 97
def on_update
        raise ScriptError, "can't specify more than one callback" if self.callback
        @callback = [ :update ]
        return self
end
anchor
to( state_name )
Alias for: to_state
anchor
to_state( state_name )

Add an additional expectation that the state that is transitioned to be of the given state_name. This only applies to transitions that take a target state type. Expecting a particular state_name in transitions which do not take a state is undefined behavior.

# File lib/pushdown/spec_helpers.rb, line 88
def to_state( state_name )
        @target_state = state_name
        return self
end
Also aliased as: to
anchor
via( transition_type )
Alias for: via_transition_type
anchor
via_transition_type( transition_type )

Add an additional expectation that the transition that occurs be of the specified transition_type.

# File lib/pushdown/spec_helpers.rb, line 77
def via_transition_type( transition_type )
        @expected_type = transition_type
        return self
end
Also aliased as: via

Protected Instance Methods

anchor
correct_target_state?()

Returns true if a target state was specified and the transition which occurred was to that state.

# File lib/pushdown/spec_helpers.rb, line 164
def correct_target_state?
        state_name = self.target_state or return true

        case self.result
        when Pushdown::Transition
                return self.result.respond_to?( :state_class ) &&
                        self.result.state_class.type_name == state_name
        when Symbol
                self.state.class.transitions[ self.result ][ 1 ] == state_name
        end
end
anchor
correct_transition_type?()

Returns true if a transition type was specified and the transition which occurred was of that type.

# File lib/pushdown/spec_helpers.rb, line 148
def correct_transition_type?
        type = self.expected_type or return true

        case self.result
        when Pushdown::Transition
                self.result.type_name == type
        when Symbol
                self.state.class.transitions[ self.result ].first == type
        else
                raise "unexpected transition result type %p" % [ self.result ]
        end
end
anchor
describe_additional_expectations()

Return an Array of descriptions of the members that were expected to be included in the state body, if any were specified. If none were specified, returns an empty Array.

# File lib/pushdown/spec_helpers.rb, line 233
def describe_additional_expectations
        return self.additional_expectations.map( &:description )
end
anchor
describe_failure()

Build an appropriate failure messages for the matcher.

# File lib/pushdown/spec_helpers.rb, line 178
def describe_failure
        desc = String.new( "expected to transition" )
        desc << " via %s" % [ self.expected_type ] if self.expected_type
        desc << " to %s" % [ self.target_state ] if self.target_state

        if self.callback
                methname, *args = self.callback
                desc << " when #%s is called" % [ methname ]
                desc << " with %s" % [ args.map(&:inspect).join(', ') ] if !args.empty?
        end

        desc << ', but '

        case self.result
        when Exception
                err = self.result
                desc << "got %p: %s" % [ err.class, err.message ]
        when Symbol
                transition = self.state.class.transitions[ self.result ]

                if transition
                        desc << "it returned a %s transition " % [ transition.first ]
                        desc << "to %s " % [ transition[1] ] if transition[1]
                        desc << " instead"
                else
                        desc << "it returned an unmapped Symbol (%p)" % [ self.result ]
                end
        when Pushdown::Transition
                desc << "it returned a %s transition" % [ self.result.type_name ]
                desc << " to %s" % [ self.result.state_class.type_name ] if
                        self.result.respond_to?( :state_class )
                desc << " instead"
        else
                desc << "it did not (returned: %p)" % [ self.result ]
        end

        return desc
end
anchor
describe_negated_failure()

Build an appropriate failure message for the matcher.

# File lib/pushdown/spec_helpers.rb, line 219
def describe_negated_failure
        desc = String.new( "expected not to transition" )
        desc << " via %s" % [ self.expected_type ] if self.expected_type
        desc << " to %s" % [ self.target_state ] if self.target_state

        desc << ', but it did.'

        return desc
end
anchor
matches_additional_expectations?()

Check that any additional matchers registered via the .and mutator also match the parsed state body.

# File lib/pushdown/spec_helpers.rb, line 240
def matches_additional_expectations?
        return self.additional_expectations.all? do |matcher|
                matcher.matches?( self.parsed_state_body ) or
                        fail_with( matcher.failure_message )
        end
end
anchor
update_ran_without_error?()

Call the state's update callback and record the result, then return true if no exception was raised.

# File lib/pushdown/spec_helpers.rb, line 119
def update_ran_without_error?
        operation = self.callback || DEFAULT_CALLBACK

        @result = begin
                        self.state.public_send( *operation )
                rescue => err
                        err
                end

        return !@result.is_a?( Exception )
end
anchor
update_returned_transition?()

Returns true if the result of calling update was a Transition or a Symbol that corresponds with a valid transition.

# File lib/pushdown/spec_helpers.rb, line 134
def update_returned_transition?
        case self.result
        when Pushdown::Transition
                return true
        when Symbol
                return self.state.class.transitions.include?( self.result )
        else
                return false
        end
end