Ruby Design Patterns | STATE 🍽️
Tags: ruby, rails, webdev, architecture
Published: 2023-08-18 04:00:00

## Overview
State is a behavioral design pattern that allows an object to change its behavior when its internal state changes.

As the name suggests, this pattern helps us work with the states of a given object. For today’s example, let’s take the states that orders in a restaurant go through. Our order will go through 4 states:

- _New_
- _In Progress_
- _Ready_
- _Received_


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mu8p10gas6o45ye2pxnx.png)

## New
The first state appears when we create a new order. At this point, we will be able to perform two actions:

- _pay for the order_
- _check order status_

## In Progress
This condition appears just after paying. In this state, we can only:

- _check order status_

## Ready
A state that says our order is ready for pickup. At this point, we can take two actions:

- _check order status_
- _take the order_

## Received
The last state appears after receiving the order. At this point, we can:

- _check order status_
- _rate the order_

## Code Example
We’ll first create a **State** class that implements all the methods we want to use on the **Order** class. Each of these methods will raise an error.

```ruby
class State
  def initialize(order)
    @order = order
  end
  
  def pay
    raise 'You cannot use this method on this state'
  end

  def next_state
    raise 'You cannot use this method on this state'
  end
  
  def take
    raise 'You cannot use this method on this state'
  end
  
  def rate(number)
    raise 'You cannot use this method on this state'
  end
end
```

Having the main **State** class, we are able to create subclasses that will represent a particular **State** and implement only those methods that we want to use in a given state.

```ruby
class NewState < State
  def pay
    puts 'Payment has been accepted'

    @order.state = InProgressState.new(@order)
  end
end

class InProgressState < State
  def next_state
    puts 'Order is ready to take'

    @order.state = ReadyState.new(@order)
  end
end

class ReadyState < State
  def take
    puts 'The order has been taken'

    @order.state = ReceivedState.new(@order)
  end
end

class ReceivedState < State
  def rate(number)
    puts "You rated the order to: #{number}"
  end
end
```

We have already created specific **State** classes, so now we can create our **Order** class. This class will contain all the methods we need, but in fact, they will be executed according to a specific state.

```ruby
class Order
  attr_accessor :state

  def initialize
    @state = NewState.new(self)
  end
  
  def pay
    state.pay
  end
  
  def next_state
    state.next_state
  end

  def take
    state.take
  end

  def rate(number)
    state.rate(number)
  end

  def status
    state.class.name.gsub('State', '')
  end
end
```

The order created in this way allows the **Order** to use only those methods that are allowed in a given **State**.

```ruby
order = Order.new
order.status # => "New"

order.take # => RuntimeError: You cannot use this method on this state

order.pay # => Payment has been accepted
order.status # => InProgress

order.next_state # => Order is ready to take
order.take # => The order has been taken

order.take # => RuntimeError: You cannot use this method on this state
order.rate(5) # => "You rated the order to: 5"
```