billdogeng 1.0.2.pre.beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +173 -0
- data/lib/billdogeng/analytics.rb +530 -0
- data/lib/billdogeng/client.rb +272 -0
- data/lib/billdogeng/errors.rb +20 -0
- data/lib/billdogeng/flags.rb +518 -0
- data/lib/billdogeng/llm.rb +59 -0
- data/lib/billdogeng/messaging.rb +44 -0
- data/lib/billdogeng/murmur.rb +86 -0
- data/lib/billdogeng/quota_limited.rb +52 -0
- data/lib/billdogeng/surveys.rb +117 -0
- data/lib/billdogeng/targeting.rb +278 -0
- data/lib/billdogeng/transport.rb +182 -0
- data/lib/billdogeng/version.rb +5 -0
- data/lib/billdogeng.rb +37 -0
- metadata +90 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dfff3e8914b52fef194e6ba43a996ca3a9fbf9aa3c35bcb0a842e9df8013f997
|
|
4
|
+
data.tar.gz: fe59c1944e99b1fb80344d75780f5d73024c209d15e051eb10c264576ac9133b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 28df3557077d20a6638ca9bc51d5a8984e33ff413b987fed508f7d5a1f8fdaa8466e5d3f5dc4c2589d7930a4a23af6eb69fb1a92f9995a679631d6ca2d2d08bf
|
|
7
|
+
data.tar.gz: 4b7c2897e20fa020d6c40f58fc651c80e5378c8707d503aa6cf1f1d6eefd4cf88fe0bd16a0ff72a17e6e80e797d3556491013a7c66d1774212f42b052ded2681
|
data/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# billdogeng (Ruby)
|
|
2
|
+
|
|
3
|
+
Official **BilldogEng** server SDK for Ruby — the engagement suite for
|
|
4
|
+
server-side use: **Analytics**, **Feature Flags** (remote + local evaluation),
|
|
5
|
+
**Surveys** (data API), **Messaging** dispatch, and **LLM** observability.
|
|
6
|
+
|
|
7
|
+
Pure Ruby standard library (`net/http`, `json`, `zlib`) — no runtime
|
|
8
|
+
dependencies. Wire-compatible with every other `billdogeng-*` server SDK so the
|
|
9
|
+
cross-language parity corpus passes.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Gemfile
|
|
15
|
+
gem "billdogeng"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
bundle install
|
|
20
|
+
# or
|
|
21
|
+
gem install billdogeng
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require "billdogeng"
|
|
28
|
+
|
|
29
|
+
bd = BilldogEng.new("bd_test_xxx", local_evaluation: true)
|
|
30
|
+
|
|
31
|
+
# Analytics (batched + background flush + gzip + backoff retry)
|
|
32
|
+
bd.capture("user-123", "order_completed", { "revenue" => 49.99 })
|
|
33
|
+
bd.identify("user-123", { "email" => "a@b.com", "plan" => "pro" })
|
|
34
|
+
bd.group_identify("company", "acme", { "seats" => 50 })
|
|
35
|
+
bd.alias("user-123", "anon-abc")
|
|
36
|
+
|
|
37
|
+
# Feature flags (local, deterministic murmurhash3 bucketing)
|
|
38
|
+
on = bd.feature_enabled?("new_checkout", "user-123")
|
|
39
|
+
value = bd.get_feature_flag("new_checkout", "user-123",
|
|
40
|
+
person_properties: { "plan" => "pro" })
|
|
41
|
+
payload = bd.get_feature_flag_payload("new_checkout", "user-123")
|
|
42
|
+
all = bd.get_all_flags("user-123")
|
|
43
|
+
|
|
44
|
+
# Surveys (data API)
|
|
45
|
+
list = bd.surveys.list("user-123")
|
|
46
|
+
config = bd.surveys.fetch(survey_id, "user-123")
|
|
47
|
+
start = bd.surveys.start(survey_id, customer_id: "user-123", idempotency_key: "idem-1")
|
|
48
|
+
bd.surveys.record_partial(survey_id, start["respondent_id"], answers)
|
|
49
|
+
bd.surveys.submit(survey_id, answers, respondent_id: start["respondent_id"])
|
|
50
|
+
bd.surveys.abandon(survey_id, start["respondent_id"])
|
|
51
|
+
|
|
52
|
+
# Messaging dispatch (Bearer JWT, not X-BillDog-API-Key)
|
|
53
|
+
bd.messaging.dispatch(
|
|
54
|
+
project_id: "…",
|
|
55
|
+
channel: "push",
|
|
56
|
+
content: { "title" => "Hi", "body" => "There" },
|
|
57
|
+
targeting: { "type" => "all" },
|
|
58
|
+
scheduling: { "deliveryType" => "immediate" },
|
|
59
|
+
access_token: "<supabase-session-jwt>",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# LLM observability
|
|
63
|
+
bd.llm.capture_trace(
|
|
64
|
+
trace_id: "t-1", span_id: "s-1", model: "claude-opus-4-8",
|
|
65
|
+
input_text: "hello", output_text: "hi",
|
|
66
|
+
prompt_tokens: 10, completion_tokens: 5, duration_ms: 123, cost_usd: 0.002,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Flush remaining events and stop the background timer.
|
|
70
|
+
bd.shutdown
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
| Option | Default | Description |
|
|
76
|
+
| ------------------- | ------------------------------ | -------------------------------------------- |
|
|
77
|
+
| `host` | `https://api.billdog.io/v1` | Base URL for all requests |
|
|
78
|
+
| `flush_at` | `20` | Queue size that triggers an automatic flush |
|
|
79
|
+
| `flush_interval` | `10_000` | Background flush cadence (ms) |
|
|
80
|
+
| `max_queue_size` | `1000` | Drop oldest events beyond this |
|
|
81
|
+
| `gzip` | `true` | Gzip large request bodies |
|
|
82
|
+
| `local_evaluation` | `false` | Evaluate feature flags locally (server only) |
|
|
83
|
+
| `request_timeout` | `10_000` | Per-request timeout (ms) |
|
|
84
|
+
| `max_retries` | `3` | Retry attempts for 5xx / 429 / network |
|
|
85
|
+
| `enable_logging` | `false` | Verbose diagnostics to stderr |
|
|
86
|
+
|
|
87
|
+
Auth: every request sends `X-BillDog-API-Key: <apiKey>` (`bd_test_*` sandbox /
|
|
88
|
+
`bd_live_*` live). Messaging dispatch instead authenticates with a per-call
|
|
89
|
+
Bearer JWT (`access_token`).
|
|
90
|
+
|
|
91
|
+
## Exhausted allowance (`quota_limited`)
|
|
92
|
+
|
|
93
|
+
Ingestion answers a drained meter with **HTTP 200** and a top-level
|
|
94
|
+
`quota_limited` array — never an error status — so your app is never broken by
|
|
95
|
+
your BillDog meter filling up. The data in that batch was **not** stored.
|
|
96
|
+
|
|
97
|
+
The SDK reads those 200s and suspends: it stops flushing, `capture` /
|
|
98
|
+
`identify` / `group_identify` / `alias` become no-ops (buffering data the server
|
|
99
|
+
has already refused would just grow your process's memory), and the reason is
|
|
100
|
+
written to stderr **once**, regardless of `enable_logging` — it is the only
|
|
101
|
+
signal your pipeline has gone dark:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
[BilldogEng:analytics] Your free plan's monthly events allowance (1,000,000) is exhausted — this data was not stored.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It also heals itself: every 15 minutes one probe batch is allowed through, and
|
|
108
|
+
the first one accepted resumes normal flushing (logging `allowance restored`
|
|
109
|
+
once). So a monthly reset or a plan upgrade brings the pipeline back with no
|
|
110
|
+
redeploy. `flush` / `shutdown` never raise or block on a quota refusal.
|
|
111
|
+
|
|
112
|
+
## Feature-flag evaluation
|
|
113
|
+
|
|
114
|
+
Local evaluation fetches flag **definitions** from `/feature-flag-definitions`
|
|
115
|
+
once, caches them (5-minute TTL / `reload_feature_flag_definitions`), and
|
|
116
|
+
evaluates deterministically:
|
|
117
|
+
|
|
118
|
+
1. missing / inactive → `false`
|
|
119
|
+
2. the flag's `targeting_rule` — the ONE canonical audience DSL, evaluated by
|
|
120
|
+
`BilldogEng::Targeting` — must match, else `false`. AND/OR combinators, date
|
|
121
|
+
windows, rule status, group membership, experiment-variant dependencies and
|
|
122
|
+
the full operator set all resolve locally, with no server round-trip:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
bd.get_feature_flag("beta", "user-123",
|
|
126
|
+
country: "US", platform: "ios",
|
|
127
|
+
app_version: "2.5", sdk_version: "1.4",
|
|
128
|
+
groups: { "organisation" => "acme" },
|
|
129
|
+
person_properties: { "plan" => "pro" },
|
|
130
|
+
experiment_variants: { "exp-1" => "treatment" })
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`country` / `platform` / `app_version` / `sdk_version` each fall back to a
|
|
134
|
+
same-named person property when not passed explicitly.
|
|
135
|
+
3. `bucket = murmurhash3("flag:{key}:{distinct_id}") % 100`; ON iff `bucket < rollout_percentage`
|
|
136
|
+
4. multivariate: walk `variants` by cumulative rollout against an INDEPENDENT
|
|
137
|
+
hash, `murmurhash3("flag-variant:{key}:{distinct_id}") % 100`
|
|
138
|
+
|
|
139
|
+
Conditions that only the database can answer (`segment`, `cohort`,
|
|
140
|
+
`survey_answer`, `event_fired_in_window`, `group_property`) are never guessed at:
|
|
141
|
+
they go **cold** and the flag is resolved against the server instead. Such flags
|
|
142
|
+
are normally already marked `requires_server_evaluation`.
|
|
143
|
+
|
|
144
|
+
The `murmurhash3` (32-bit, seed 0, UTF-8 bytes) is identical across web / iOS /
|
|
145
|
+
Android / every server SDK, so a user buckets the same everywhere. Both the
|
|
146
|
+
bucketing and the audience evaluator are pinned to fixtures generated from the
|
|
147
|
+
backend (`tests/fixtures/flag-bucketing.json`, `tests/fixtures/flag-targeting.json`).
|
|
148
|
+
|
|
149
|
+
## Tests
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
bundle install && bundle exec rspec
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Covers: batching (10 captures → 1 POST), identify/group shapes, the 12 canonical
|
|
156
|
+
murmurhash3 vectors + rollout/targeting/multivariate eval, 503-then-200 retry
|
|
157
|
+
with backoff, the `quota_limited` 200 suspend / self-heal path, and a survey
|
|
158
|
+
round-trip + messaging dispatch shape (all against a local WEBrick stub).
|
|
159
|
+
|
|
160
|
+
## BillDog Auth
|
|
161
|
+
|
|
162
|
+
Session-token verification is **not available in this SDK yet**.
|
|
163
|
+
[`billdogeng-node`](https://www.npmjs.com/package/billdogeng-node) is the only
|
|
164
|
+
server SDK that can verify a BillDog Auth token offline today.
|
|
165
|
+
|
|
166
|
+
Until it lands here, either verify in a Node service, or call the
|
|
167
|
+
`auth-introspect` endpoint over HTTP — that costs a network round-trip per
|
|
168
|
+
check, and in exchange tells you whether the session is live *right now* rather
|
|
169
|
+
than only that the token was valid when it was issued.
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|