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 retrict permitted events to the the available STATEs and :error.

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)


74
75
76
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
# File 'lib/ably/modules/state_emitter.rb', line 74

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 = Proc.new do
      yield
      off(&success_wrapper)
      off(&failure_wrapper) if failure_wrapper
    end

    failure_wrapper = proc 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)


118
119
120
121
122
123
124
125
126
127
# File 'lib/ably/modules/state_emitter.rb', line 118

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

  once_block = proc 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



37
38
39
# File 'lib/ably/modules/state_emitter.rb', line 37

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



52
53
54
55
56
57
58
# File 'lib/ably/modules/state_emitter.rb', line 52

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)


44
45
46
# File 'lib/ably/modules/state_emitter.rb', line 44

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.



108
109
110
# File 'lib/ably/modules/state_emitter.rb', line 108

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.



132
133
134
# File 'lib/ably/modules/state_emitter.rb', line 132

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