Skip to content

Instantly share code, notes, and snippets.

@Thomascountz
Last active May 30, 2018 15:31
Show Gist options
  • Save Thomascountz/4223ff8b86a111142ba32679aebd08aa to your computer and use it in GitHub Desktop.
Save Thomascountz/4223ff8b86a111142ba32679aebd08aa to your computer and use it in GitHub Desktop.
State Pattern Example in Ruby

State Pattern Example in Ruby

The State pattern suggests to create new classes for all possible states of a context object and to extract the state-related behaviors into these classes. The context will contain a reference to a state object that represents its current state. Instead of performing a behavior on its own, the context will delegate the execution to a state object. To change the context's state, one would pass another state object to the context. But to make states interchangeable, all states classes must follow the common interface, and the context must communicate with its state object via that interface. The described structure may look like the Strategy pattern, but there is one key difference. In the State pattern, the context, as wells as particular states, can initiate the transitions from one state to another.

Read more about the State Pattern here: https://refactoring.guru/design-patterns/state

Example Usage

$ ⇒  pry
[1] pry(main)> require './state_pattern.rb'
=> true

[2] pry(main)> document = Document.new
=> #<Document:0x007f98b6a44470 @current_user=#<struct Document::User role="admin"> ...>>>

[3] pry(main)> document.publish
=> "under review"
[4] pry(main)> document.unpublish
=> "draft"

[5] pry(main)> document.publish
=> "under review"
[6] pry(main)> document.publish
=> "published"
[7] pry(main)> document.publish
NotImplementedError: NotImplementedError

[8] pry(main)> document.unpublish
=> "draft"

If the context.current_user.role != "admin"

[6] pry(main)> document.publish
=> "Admin must review."

[7] pry(main)> document.unpublish
=> "draft"
[8] pry(main)> document.publish
=> "under review"
[9] pry(main)> document.publish
=> "Admin must review."
class Document
attr_reader :state, :current_user
def initialize
@state = DraftState.new(self)
@current_user = User.new('admin')
end
def set_state(new_state)
@state = new_state
end
def publish
@state.publish
end
def unpublish
@state.unpublish
end
User = Struct.new(:role)
end
class State
attr_reader :context
def initialize(context)
@context = context
end
def publish
raise NotImplementedError
end
def unpublish
raise NotImplementedError
end
end
class DraftState < State
def publish
next_state = ReviewState.new(context)
context.set_state(next_state)
next_state.to_s
end
def to_s
'draft'
end
end
class ReviewState < State
def publish
if context.current_user.role == 'admin'
next_state = PublishedState.new(context)
context.set_state(next_state)
next_state.to_s
else
"Admin must review."
end
end
def unpublish
next_state = DraftState.new(context)
context.set_state(next_state)
next_state.to_s
end
def to_s
'under review'
end
end
class PublishedState < State
def unpublish
next_state = DraftState.new(context)
context.set_state(next_state)
next_state.to_s
end
def to_s
'published'
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment