Class: Matchi::Change::From::To

Inherits:
Object
  • Object
show all
Defined in:
lib/matchi/change/from/to.rb

Overview

Value transition matcher that verifies both initial and final states of an operation.

This matcher ensures that a value not only changes to an expected final state but also starts from a specific initial state. This is particularly useful when testing state transitions where both the starting and ending conditions are important, such as in workflow systems, state machines, or data transformations.

Examples:

Basic string transformation

text = "hello"
matcher = Matchi::Change::From::To.new("hello", "HELLO") { text.to_s }
matcher.match? { text.upcase! }   # => true
matcher.match? { text.reverse! }  # => false  # Wrong final state

text = "other"
matcher.match? { text.upcase! }   # => false  # Wrong initial state

State machine transitions

class Order
  attr_accessor :status
  def initialize(status)
    @status = status
  end
end

order = Order.new(:pending)
matcher = Matchi::Change::From::To.new(:pending, :shipped) { order.status }
matcher.match? { order.status = :shipped }  # => true

Complex object transformations

class User
  attr_accessor :permissions
  def initialize
    @permissions = [:read]
  end

  def promote!
    @permissions += [:write, :delete]
  end
end

user = User.new
matcher = Matchi::Change::From::To.new(
  [:read],
  [:read, :write, :delete]
) { user.permissions }
matcher.match? { user.promote! }  # => true

See Also:

Instance Method Summary collapse

Constructor Details

#initialize(expected_init, expected_new_value, &state) ⇒ To

Initialize the matcher with expected initial and final values.

Examples:

With simple value

To.new("draft", "published") { document.status }

With complex state

To.new([:user], [:user, :admin]) { .roles }

Parameters:

  • expected_init (#==)

    The expected initial value

  • expected_new_value (#==)

    The expected final value

  • state (Proc)

    Block that retrieves the current value

Raises:

  • (ArgumentError)

    if no state block is provided



73
74
75
76
77
78
79
# File 'lib/matchi/change/from/to.rb', line 73

def initialize(expected_init, expected_new_value, &state)
  raise ::ArgumentError, "a block must be provided" unless block_given?

  @expected_init = expected_init
  @expected = expected_new_value
  @state = state
end

Instance Method Details

#match? { ... } ⇒ Boolean

Verifies both initial and final states during a transition.

This method first checks if the initial state matches the expected value, then executes the provided block and verifies the final state. The match fails if either the initial or final state doesn’t match expectations.

Examples:

Basic usage

text = "hello"
matcher = To.new("hello", "HELLO") { text }
matcher.match? { text.upcase! }  # => true

Failed initial state

text = "wrong"
matcher = To.new("hello", "HELLO") { text }
matcher.match? { text.upcase! }  # => false

Yields:

  • Block that should cause the state transition

Yield Returns:

  • (Object)

    The result of the block (not used)

Returns:

  • (Boolean)

    true if both initial and final states match expectations

Raises:

  • (ArgumentError)

    if no block is provided



105
106
107
108
109
110
111
112
113
114
115
# File 'lib/matchi/change/from/to.rb', line 105

def match?
  raise ::ArgumentError, "a block must be provided" unless block_given?

  value_before = @state.call
  return false unless @expected_init == value_before

  yield
  value_after = @state.call

  @expected == value_after
end

#to_sString

Returns a human-readable description of the matcher.

Examples:

To.new("draft", "published").to_s
# => 'change from "draft" to "published"'

Returns:

  • (String)

    A string describing what this matcher verifies



126
127
128
# File 'lib/matchi/change/from/to.rb', line 126

def to_s
  "change from #{@expected_init.inspect} to #{@expected.inspect}"
end