Module: Ably::Modules::StateEmitter

Overview

Note:

This module requires that the method #logger is defined.

StateEmitter module adds a set of generic state related methods to a class on the assumption that the instance variable @state is used exclusively, the Enum STATE is defined prior to inclusion of this module, and the class is an EventEmitter. It then emits state changes.

It also ensures the EventEmitter is configured to restrict permitted events to the the available STATEs or EVENTs if defined i.e. if EVENTs includes an additional type such as :update, then it will support all EVENTs being emitted. EVENTs must be a superset of STATEs

Examples:

class Connection
  include Ably::Modules::EventEmitter
  extend  Ably::Modules::Enum
  STATE = ruby_enum('STATE',
    :initialized,
    :connecting,
    :connected
  )
  include Ably::Modules::StateEmitter
end

connection = Connection.new
connection.state = :connecting     # emits :connecting event via EventEmitter, returns STATE.Connecting
connection.state?(:connected)      # => false
connection.connecting?             # => true
connection.state                   # => STATE.Connecting
connection.state = :invalid        # raises an Exception as only a valid state can be defined
connection.emit :invalid           # raises an Exception as only a valid state can be used for EventEmitter
connection.change_state :connected # emits :connected event via EventEmitter, returns STATE.Connected
connection.once_or_if(:connected) { puts 'block called once when state is connected or becomes connected' }

Instance Method Summary collapse

Instance Method Details

#once_or_if(target_states, options = {}) { ... } ⇒ void

This method returns an undefined value.

If the current state matches the target_state argument the block is called immediately. Else the block is called once when the target_state is reached.

If the option block :else is provided then if any state other than target_state is reached, the :else block is called, however only one of the blocks will ever be called

Parameters:

  • target_states (Symbol, Ably::Modules::Enum, Array)

    a single state or array of states that once met, will fire the success block only once

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :else (Proc)

    block called once the state has changed to anything but target_state

Yields:

  • block is called if the state is matched immediately or once when the state is reached

Raises:

  • (ArgumentError)


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/ably/modules/state_emitter.rb', line 77

def once_or_if(target_states, options = {}, &block)
  raise ArgumentError, 'Block required' unless block_given?

  if Array(target_states).any? { |target_state| state == target_state }
    safe_yield block
  else
    failure_block   = options.fetch(:else, nil)
    failure_wrapper = nil

    success_wrapper = lambda do |*args|
      yield
      off(&success_wrapper)
      off(&failure_wrapper) if failure_wrapper
    end

    failure_wrapper = lambda do |*args|
      failure_block.call(*args)
      off(&success_wrapper)
      off(&failure_wrapper)
    end if failure_block

    Array(target_states).each do |target_state|
      safe_unsafe_method options[:unsafe], :once, target_state, &success_wrapper

      safe_unsafe_method options[:unsafe], :once_state_changed do |*args|
        failure_wrapper.call(*args) unless state == target_state
      end if failure_block
    end
  end
end

#once_state_changed(options = {}) { ... } ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Calls the block once when the state changes

Yields:

  • block is called once the state changes

Raises:

  • (ArgumentError)


121
122
123
124
125
126
127
128
129
130
# File 'lib/ably/modules/state_emitter.rb', line 121

def once_state_changed(options = {}, &block)
  raise ArgumentError, 'Block required' unless block_given?

  once_block = lambda do |*args|
    off(*self.class::STATE.map, &once_block)
    yield(*args)
  end

  safe_unsafe_method options[:unsafe], :once, *self.class::STATE.map, &once_block
end

#stateSymbol

Current state Enum

Returns:

  • (Symbol)

    state



39
40
41
# File 'lib/ably/modules/state_emitter.rb', line 39

def state
  STATE(@state)
end

#state=(new_state, *args) ⇒ Symbol Also known as: change_state

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Set the current state Enum

Returns:

  • (Symbol)

    new state



55
56
57
58
59
60
61
# File 'lib/ably/modules/state_emitter.rb', line 55

def state=(new_state, *args)
  if state != new_state
    logger.debug { "#{self.class}: StateEmitter changed from #{state} => #{new_state}" } if respond_to?(:logger, true)
    @state = STATE(new_state)
    emit @state, *args
  end
end

#state?(check_state) ⇒ Boolean

Evaluates if check_state matches current state

Returns:

  • (Boolean)


47
48
49
# File 'lib/ably/modules/state_emitter.rb', line 47

def state?(check_state)
  state == check_state
end

#unsafe_once_or_if(target_states, options = {}, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Equivalent of #once_or_if but any exception raised in a block will bubble up and cause this client library to fail. This method should only be used internally by the client library.



111
112
113
# File 'lib/ably/modules/state_emitter.rb', line 111

def unsafe_once_or_if(target_states, options = {}, &block)
  once_or_if(target_states, options.merge(unsafe: true), &block)
end

#unsafe_once_state_changed(&block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Equivalent of #once_state_changed but any exception raised in a block will bubble up and cause this client library to fail. This method should only be used internally by the client library.



135
136
137
# File 'lib/ably/modules/state_emitter.rb', line 135

def unsafe_once_state_changed(&block)
  once_state_changed(unsafe: true, &block)
end