Class: Letterapp::Client
- Inherits:
-
Object
- Object
- Letterapp::Client
- Defined in:
- lib/letterapp/client.rb
Overview
A Letter 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.
send_email (transactional) sits outside that machinery: it always performs
the request and returns the result. Batching a password reset would be a
bug, not an optimization.
Instance Method Summary collapse
-
#close ⇒ Object
Flush, stop the background thread, and block until drained.
-
#flush ⇒ Object
Send everything currently queued and block until it completes.
-
#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).
-
#identify(user_id:, email: nil, traits: nil, timezone: nil, timestamp: nil, message_id: nil) ⇒ Object
Queue an identify call (creates or updates a contact).
-
#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
constructor
A new instance of Client.
-
#send_email(to:, subject:, html: nil, text: nil, from: nil, from_name: nil, reply_to: nil, headers: nil, tag: nil, metadata: nil, idempotency_key: nil) ⇒ Object
Send one transactional email and return the parsed result Hash ("id", "messageId", "status", "replayed", ...).
-
#track(user_id:, event:, properties: nil, timestamp: nil, message_id: nil) ⇒ Object
Queue a track call (records an event).
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.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/letterapp/client.rb', line 52 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.}") } @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
#close ⇒ Object
Flush, stop the background thread, and block until drained.
137 138 139 140 141 142 143 144 145 146 |
# File 'lib/letterapp/client.rb', line 137 def close @mutex.synchronize do return if @closed @closed = true @cond.broadcast end @thread&.join(@read_timeout * (@max_retries + 2)) flush end |
#flush ⇒ Object
Send everything currently queued and block until it completes.
127 128 129 130 131 132 133 134 |
# File 'lib/letterapp/client.rb', line 127 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).
84 85 86 87 |
# File 'lib/letterapp/client.rb', line 84 def group(user_id:, account_id:, name: nil, traits: nil, timestamp: nil, message_id: nil) enqueue(serialize_group(user_id, account_id, name, traits, , )) 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).
78 79 80 81 |
# File 'lib/letterapp/client.rb', line 78 def identify(user_id:, email: nil, traits: nil, timezone: nil, timestamp: nil, message_id: nil) enqueue(serialize_identify(user_id, email, traits, timezone, , )) end |
#send_email(to:, subject:, html: nil, text: nil, from: nil, from_name: nil, reply_to: nil, headers: nil, tag: nil, metadata: nil, idempotency_key: nil) ⇒ Object
Send one transactional email and return the parsed result Hash ("id", "messageId", "status", "replayed", ...).
Named send_email rather than send because Object#send is Ruby's
dynamic dispatch: shadowing it on a client object would break every
client.send(:private_method) and metaprogramming call in the host app.
Never queued: the call performs the request and returns once the provider
has accepted the message. Pass idempotency_key wherever the call can be
retried (a job with retries, a webhook handler): a replay returns the
original send rather than mailing the recipient twice, and it is what
makes retrying a 5xx safe. Without one this method does not retry,
because a duplicate email is worse than a failed one.
from defaults to the project's sender and must be on a verified domain.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/letterapp/client.rb', line 109 def send_email(to:, subject:, html: nil, text: nil, from: nil, from_name: nil, reply_to: nil, headers: nil, tag: nil, metadata: nil, idempotency_key: nil) raise Error, "send_email: to is required" if blank?(to) raise Error, "send_email: subject is required" if blank?(subject) raise Error, "send_email: provide html, text, or both" if blank?(html) && blank?(text) body = { "to" => to, "subject" => subject } { "html" => html, "text" => text, "from" => from, "fromName" => from_name, "replyTo" => reply_to, "headers" => headers, "tag" => tag, "metadata" => , "idempotencyKey" => idempotency_key }.each { |key, value| body[key] = value unless value.nil? } request("/v1/send", body, max_retries: idempotency_key ? @max_retries : 0) || {} end |
#track(user_id:, event:, properties: nil, timestamp: nil, message_id: nil) ⇒ Object
Queue a track call (records an event).
90 91 92 |
# File 'lib/letterapp/client.rb', line 90 def track(user_id:, event:, properties: nil, timestamp: nil, message_id: nil) enqueue(serialize_track(user_id, event, properties, , )) end |