Class: Toggly::DefinitionsProvider

Inherits:
Object
  • Object
show all
Defined in:
lib/toggly/definitions_provider.rb

Overview

Provider for fetching feature definitions from Toggly API.

Constant Summary collapse

FALLBACK_REFRESH_INTERVAL =

Fallback HTTP refresh interval when WebSocket is connected (20 minutes)

20 * 60
WS_RECONNECT_DELAY =

Delay before attempting WebSocket reconnection (seconds)

5

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config:, logger: nil, on_definitions_updated: nil) ⇒ DefinitionsProvider

Returns a new instance of DefinitionsProvider.

Parameters:

  • config (Config)

    Configuration

  • logger (Logger, nil) (defaults to: nil)

    Optional logger

  • on_definitions_updated (Proc, nil) (defaults to: nil)

    Callback when WS signals an update



29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/toggly/definitions_provider.rb', line 29

def initialize(config:, logger: nil, on_definitions_updated: nil)
  @config = config
  @logger = logger
  @on_definitions_updated = on_definitions_updated
  @etag = nil
  @last_modified = nil

  # WebSocket state
  @ws = nil
  @ws_connected = false
  @ws_thread = nil
  @ws_closing = false
  @last_fallback_refresh = nil
end

Instance Attribute Details

#ws_connectedBoolean (readonly)

Returns Whether the WebSocket connection is active.

Returns:

  • (Boolean)

    Whether the WebSocket connection is active



24
25
26
# File 'lib/toggly/definitions_provider.rb', line 24

def ws_connected
  @ws_connected
end

Instance Method Details

#fetch(force: false) ⇒ Hash?

Fetch definitions from the API

Parameters:

  • force (Boolean) (defaults to: false)

    Force fetch even if cached

Returns:

  • (Hash, nil)

    Hash of definitions, or nil if not modified

Raises:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/toggly/definitions_provider.rb', line 50

def fetch(force: false)
  return nil if @config.offline_mode?

  uri = URI.parse(@config.definitions_endpoint)
  http = build_http(uri)
  request = build_request(uri, force)

  response = http.request(request)
  handle_response(response)
rescue Net::OpenTimeout, Net::ReadTimeout => e
  raise NetworkError, "Request timeout: #{e.message}"
rescue SocketError, Errno::ECONNREFUSED => e
  raise NetworkError, "Connection failed: #{e.message}"
rescue StandardError => e
  raise NetworkError, "Request failed: #{e.message}"
end

#reset_cacheObject

Reset cached headers (force full fetch next time)



68
69
70
71
# File 'lib/toggly/definitions_provider.rb', line 68

def reset_cache
  @etag = nil
  @last_modified = nil
end

#should_skip_refresh?Boolean

Check whether the periodic refresh should be skipped because the WebSocket connection is active and the fallback interval has not elapsed.

Returns:

  • (Boolean)

    true if the refresh should be skipped



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/toggly/definitions_provider.rb', line 77

def should_skip_refresh?
  return false unless @ws_connected
  return false if @last_fallback_refresh.nil?

  if (Time.now - @last_fallback_refresh) < FALLBACK_REFRESH_INTERVAL
    log_debug("WebSocket connected; skipping periodic HTTP refresh")
    true
  else
    @last_fallback_refresh = Time.now
    false
  end
end

#start_websocketObject

Start a WebSocket connection for live definition updates. Requires the websocket-client-simple gem. If the gem is not installed this method logs a message and returns without error.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/toggly/definitions_provider.rb', line 93

def start_websocket
  unless HAS_WEBSOCKET
    log_debug("websocket-client-simple gem not available; live updates disabled")
    return
  end

  return if @config.offline_mode?

  @ws_closing = false

  @ws_thread = Thread.new do
    loop do
      break if @ws_closing

      connect_websocket
      break if @ws_closing

      log_debug("WebSocket disconnected, reconnecting in #{WS_RECONNECT_DELAY}s")
      sleep(WS_RECONNECT_DELAY)
    end
  end

  @ws_thread.abort_on_exception = false
end

#stop_websocketObject

Stop the WebSocket connection and its management thread.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/toggly/definitions_provider.rb', line 119

def stop_websocket
  @ws_closing = true
  @ws_connected = false

  if @ws
    begin
      @ws.close
    rescue StandardError
      # Ignore errors during close
    end
    @ws = nil
  end

  return unless @ws_thread

  @ws_thread.kill
  @ws_thread = nil
end