Class: Letterapp::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/letterapp/client.rb

Overview

A Letter ingestion client.

Long-running server (default): identify / group / track enqueue calls that a background thread auto-batches and flushes every 100ms or 50 events. Call close before the process exits.

Serverless: pass flush_at: 1 and call flush at the end of each invocation.

Instance Method Summary collapse

Constructor Details

#initialize(api_key:, base_url: DEFAULT_BASE_URL, flush_at: 50, flush_interval: 0.1, max_retries: 3, open_timeout: 10, read_timeout: 10, on_error: nil) ⇒ Client

Returns a new instance of Client.

Parameters:

  • api_key (String)

    API key (Dashboard -> Settings -> API keys).

  • base_url (String) (defaults to: DEFAULT_BASE_URL)

    API origin. Defaults to api.letter.app.

  • flush_at (Integer) (defaults to: 50)

    Flush after this many queued events (1 = serverless).

  • flush_interval (Float) (defaults to: 0.1)

    Seconds between background flushes.

  • max_retries (Integer) (defaults to: 3)

    Max retry attempts per request.

  • on_error (#call) (defaults to: nil)

    Callback for background transport errors.

Raises:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/letterapp/client.rb', line 39

def initialize(api_key:, base_url: DEFAULT_BASE_URL, flush_at: 50,
               flush_interval: 0.1, max_retries: 3, open_timeout: 10,
               read_timeout: 10, on_error: nil)
  raise Error, "api_key is required" if api_key.nil? || api_key.to_s.empty?

  @api_key = api_key
  @base_url = (base_url || DEFAULT_BASE_URL).sub(%r{/+\z}, "")
  @flush_at = [1, flush_at.to_i].max
  @flush_interval = flush_interval.to_f
  @max_retries = max_retries.to_i
  @open_timeout = open_timeout
  @read_timeout = read_timeout
  @on_error = on_error || ->(err) { warn("[letter] #{err.message}") }

  @uri = URI.parse(@base_url)
  @queue = []
  @mutex = Mutex.new
  @cond = ConditionVariable.new
  @send_mutex = Mutex.new
  @closed = false

  @thread = Thread.new { loop_run }
  at_exit { close }
end

Instance Method Details

#closeObject

Flush, stop the background thread, and block until drained.



92
93
94
95
96
97
98
99
100
101
# File 'lib/letterapp/client.rb', line 92

def close
  @mutex.synchronize do
    return if @closed

    @closed = true
    @cond.broadcast
  end
  @thread&.join(@read_timeout * (@max_retries + 2))
  flush
end

#flushObject

Send everything currently queued and block until it completes.



82
83
84
85
86
87
88
89
# File 'lib/letterapp/client.rb', line 82

def flush
  loop do
    batch = take_batch
    break if batch.nil?

    @send_mutex.synchronize { send_batch(batch) }
  end
end

#group(user_id:, account_id:, name: nil, traits: nil, timestamp: nil, message_id: nil) ⇒ Object

Queue a group call (associates a contact with an account).



71
72
73
74
# File 'lib/letterapp/client.rb', line 71

def group(user_id:, account_id:, name: nil, traits: nil,
          timestamp: nil, message_id: nil)
  enqueue(serialize_group(user_id, , name, traits, timestamp, message_id))
end

#identify(user_id:, email: nil, traits: nil, timezone: nil, timestamp: nil, message_id: nil) ⇒ Object

Queue an identify call (creates or updates a contact).



65
66
67
68
# File 'lib/letterapp/client.rb', line 65

def identify(user_id:, email: nil, traits: nil, timezone: nil,
             timestamp: nil, message_id: nil)
  enqueue(serialize_identify(user_id, email, traits, timezone, timestamp, message_id))
end

#track(user_id:, event:, properties: nil, timestamp: nil, message_id: nil) ⇒ Object

Queue a track call (records an event).



77
78
79
# File 'lib/letterapp/client.rb', line 77

def track(user_id:, event:, properties: nil, timestamp: nil, message_id: nil)
  enqueue(serialize_track(user_id, event, properties, timestamp, message_id))
end