mbuzz 0.6.2

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.
@@ -0,0 +1,505 @@
1
+ # mbuzz Ruby Gem v0.5.0 - Four-Call Model
2
+
3
+ **Status**: Complete
4
+ **Last Updated**: 2025-11-29
5
+ **Breaking Change**: Yes (from 0.4.0)
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ Simplify the SDK to a 4-method model:
12
+
13
+ | Method | Purpose |
14
+ |--------|---------|
15
+ | `Mbuzz.init` | Configure SDK |
16
+ | `Mbuzz.event` | Track journey steps |
17
+ | `Mbuzz.conversion` | Track revenue outcomes |
18
+ | `Mbuzz.identify` | Link visitor to user + store traits |
19
+
20
+ ---
21
+
22
+ ## Changes from v0.4.0
23
+
24
+ | Change | Before (v0.4.0) | After (v0.5.0) |
25
+ |--------|-----------------|----------------|
26
+ | Configuration | `Mbuzz.configure { \|c\| ... }` | `Mbuzz.init(api_key: ...)` |
27
+ | Track events | `Mbuzz.track(event_type:, ...)` | `Mbuzz.event("event_type", ...)` |
28
+ | Link visitor | `Mbuzz.alias(user_id:, visitor_id:)` | Merged into `identify` |
29
+ | Identify | `Mbuzz.identify(user_id:, traits:)` | `Mbuzz.identify(user_id, traits:, visitor_id:)` |
30
+
31
+ ---
32
+
33
+ ## New Public API
34
+
35
+ ### `Mbuzz.init`
36
+
37
+ ```ruby
38
+ Mbuzz.init(
39
+ api_key: "sk_live_...", # Required
40
+ api_url: "https://mbuzz.co/api/v1", # Optional - API endpoint
41
+ debug: false # Optional - enable debug logging
42
+ )
43
+ ```
44
+
45
+ **Note**: Session timeout is fixed at 30 minutes (cookie expiry). HTTP timeout defaults to 5 seconds.
46
+
47
+ ### `Mbuzz.event`
48
+
49
+ ```ruby
50
+ Mbuzz.event("add_to_cart",
51
+ product_id: "SKU-123",
52
+ price: 49.99
53
+ )
54
+ # Returns: { success: true, event_id: "evt_abc123" } or false
55
+ ```
56
+
57
+ ### `Mbuzz.conversion`
58
+
59
+ ```ruby
60
+ Mbuzz.conversion("purchase",
61
+ revenue: 99.99,
62
+ order_id: "ORD-123"
63
+ )
64
+ # Returns: { success: true, conversion_id: "conv_abc123" } or false
65
+ ```
66
+
67
+ ### `Mbuzz.identify`
68
+
69
+ ```ruby
70
+ # Links visitor (from cookie) to user + stores traits
71
+ Mbuzz.identify("user_123",
72
+ traits: { email: "jane@example.com", name: "Jane" }
73
+ )
74
+
75
+ # Or with explicit visitor_id
76
+ Mbuzz.identify("user_123",
77
+ visitor_id: "abc123...",
78
+ traits: { email: "jane@example.com" }
79
+ )
80
+ # Returns: true on success, false on failure
81
+ ```
82
+
83
+ **Backend Response**: The API returns `{ success: true, identity_id: "idt_...", visitor_linked: true/false }` but the SDK simplifies this to a boolean for ease of use.
84
+
85
+ ---
86
+
87
+ ## Removed
88
+
89
+ - `Mbuzz.configure` → use `Mbuzz.init`
90
+ - `Mbuzz.track` → use `Mbuzz.event`
91
+ - `Mbuzz.alias` → use `Mbuzz.identify` with `visitor_id:`
92
+
93
+ ---
94
+
95
+ ## Files to Change
96
+
97
+ | File | Action |
98
+ |------|--------|
99
+ | `lib/mbuzz/version.rb` | Bump to 0.5.0 |
100
+ | `lib/mbuzz.rb` | New API methods |
101
+ | `lib/mbuzz/client.rb` | Update signatures |
102
+ | `lib/mbuzz/client/identify_request.rb` | Accept visitor_id |
103
+ | `lib/mbuzz/client/alias_request.rb` | DELETE |
104
+ | `README.md` | Complete rewrite |
105
+ | `CHANGELOG.md` | Add 0.5.0 entry |
106
+
107
+ ---
108
+
109
+ ## README Content
110
+
111
+ ```markdown
112
+ # mbuzz
113
+
114
+ Server-side multi-touch attribution for Ruby. Track customer journeys, attribute conversions, know which channels drive revenue.
115
+
116
+ ## Installation
117
+
118
+ Add to your Gemfile:
119
+
120
+ ```ruby
121
+ gem 'mbuzz'
122
+ ```
123
+
124
+ Then:
125
+
126
+ ```bash
127
+ bundle install
128
+ ```
129
+
130
+ ## Quick Start
131
+
132
+ ### 1. Initialize
133
+
134
+ ```ruby
135
+ # config/initializers/mbuzz.rb
136
+ Mbuzz.init(api_key: ENV['MBUZZ_API_KEY'])
137
+ ```
138
+
139
+ ### 2. Track Events
140
+
141
+ Track steps in the customer journey:
142
+
143
+ ```ruby
144
+ Mbuzz.event("page_view", url: request.url)
145
+ Mbuzz.event("add_to_cart", product_id: "SKU-123", price: 49.99)
146
+ Mbuzz.event("checkout_started", cart_total: 99.99)
147
+ ```
148
+
149
+ ### 3. Track Conversions
150
+
151
+ Record revenue-generating outcomes:
152
+
153
+ ```ruby
154
+ Mbuzz.conversion("purchase",
155
+ revenue: 99.99,
156
+ order_id: order.id
157
+ )
158
+ ```
159
+
160
+ ### 4. Identify Users
161
+
162
+ Link visitors to known users (enables cross-device attribution):
163
+
164
+ ```ruby
165
+ # On signup or login
166
+ Mbuzz.identify(current_user.id,
167
+ traits: {
168
+ email: current_user.email,
169
+ name: current_user.name
170
+ }
171
+ )
172
+ ```
173
+
174
+ ## Rails Integration
175
+
176
+ mbuzz provides:
177
+ - Middleware for visitor and session cookie management
178
+ - `mbuzz_visitor_id` helper in controllers
179
+
180
+ ## Configuration Options
181
+
182
+ ```ruby
183
+ Mbuzz.init(
184
+ api_key: "sk_live_...", # Required - from mbuzz.co dashboard
185
+ api_url: "https://mbuzz.co/api/v1", # Optional - API endpoint
186
+ debug: false # Optional - enable debug logging
187
+ )
188
+ ```
189
+
190
+ ## The 4-Call Model
191
+
192
+ | Method | When to Use |
193
+ |--------|-------------|
194
+ | `init` | Once on app boot |
195
+ | `event` | User interactions, funnel steps |
196
+ | `conversion` | Purchases, signups, any revenue event |
197
+ | `identify` | Login, signup, when you know the user |
198
+
199
+ ## Error Handling
200
+
201
+ mbuzz never raises exceptions. All methods return `false` on failure and log errors in debug mode.
202
+
203
+ ## Requirements
204
+
205
+ - Ruby 2.7+
206
+ - Rails 6.0+ (for automatic integration) or any Rack app
207
+
208
+ ## Links
209
+
210
+ - [Documentation](https://mbuzz.co/docs)
211
+ - [Dashboard](https://mbuzz.co/dashboard)
212
+
213
+ ## License
214
+
215
+ MIT License
216
+ ```
217
+
218
+ ---
219
+
220
+ ## CHANGELOG Entry
221
+
222
+ ```markdown
223
+ ## [0.5.0] - 2025-XX-XX
224
+
225
+ ### Breaking Changes
226
+
227
+ - `Mbuzz.configure` replaced with `Mbuzz.init`
228
+ - `Mbuzz.track` renamed to `Mbuzz.event`
229
+ - `Mbuzz.alias` removed - merged into `Mbuzz.identify`
230
+ - `Mbuzz.identify` signature changed: positional user_id, keyword traits/visitor_id
231
+
232
+ ### Added
233
+
234
+ - `Mbuzz.init(api_key:, ...)` - new configuration method
235
+ - `Mbuzz.event(event_type, **properties)` - cleaner event tracking
236
+ - Automatic visitor linking in `identify` when visitor_id available
237
+
238
+ ### Removed
239
+
240
+ - `Mbuzz.configure` block syntax
241
+ - `Mbuzz.track` method
242
+ - `Mbuzz.alias` method
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Backend Requirements ✅ COMPLETE
248
+
249
+ Backend `POST /api/v1/identify` must:
250
+ 1. ✅ Accept optional `visitor_id` in payload
251
+ 2. ✅ Link visitor to identity if visitor_id present
252
+ 3. ✅ Store/update traits
253
+ 4. ✅ Check for retroactive attribution (triggers `ReattributionJob`)
254
+ 5. ✅ Return `{ success: true, identity_id: "...", visitor_linked: true/false }`
255
+
256
+ Backend `POST /api/v1/alias` ✅ REMOVED (commit 5c4b555)
257
+
258
+ ---
259
+
260
+ ## Session & Cookie Management
261
+
262
+ ### Cookie Specifications
263
+
264
+ | Cookie | Name | Format | Max-Age | Purpose |
265
+ |--------|------|--------|---------|---------|
266
+ | Visitor | `_mbuzz_vid` | 64 hex chars | 2 years | Persistent visitor ID |
267
+ | Session | `_mbuzz_sid` | 64 hex chars | 30 min | Session tracking |
268
+
269
+ ### Middleware Implementation
270
+
271
+ ```ruby
272
+ # lib/mbuzz/middleware/tracking.rb
273
+ module Mbuzz
274
+ module Middleware
275
+ class Tracking
276
+ SESSION_TIMEOUT = 1800 # 30 minutes
277
+ VISITOR_COOKIE = "_mbuzz_vid"
278
+ SESSION_COOKIE = "_mbuzz_sid"
279
+
280
+ def initialize(app)
281
+ @app = app
282
+ end
283
+
284
+ def call(env)
285
+ @request = Rack::Request.new(env)
286
+
287
+ env[Mbuzz::ENV_VISITOR_ID_KEY] = visitor_id
288
+ env[Mbuzz::ENV_SESSION_ID_KEY] = session_id
289
+
290
+ Mbuzz::RequestContext.with_context(request: @request) do
291
+ status, headers, body = @app.call(env)
292
+ set_cookies(headers)
293
+ [status, headers, body]
294
+ end
295
+ end
296
+
297
+ private
298
+
299
+ def visitor_id
300
+ @visitor_id ||= @request.cookies[VISITOR_COOKIE] || SecureRandom.hex(32)
301
+ end
302
+
303
+ def session_id
304
+ @session_id ||= @request.cookies[SESSION_COOKIE] || SecureRandom.hex(32)
305
+ end
306
+
307
+ def set_cookies(headers)
308
+ Rack::Utils.set_cookie_header!(headers, VISITOR_COOKIE, visitor_cookie_options)
309
+ Rack::Utils.set_cookie_header!(headers, SESSION_COOKIE, session_cookie_options)
310
+ end
311
+
312
+ def visitor_cookie_options
313
+ { value: visitor_id, path: "/", max_age: 63072000, httponly: true, same_site: :lax, secure: @request.ssl? }
314
+ end
315
+
316
+ def session_cookie_options
317
+ { value: session_id, path: "/", max_age: SESSION_TIMEOUT, httponly: true, same_site: :lax, secure: @request.ssl? }
318
+ end
319
+ end
320
+ end
321
+ end
322
+ ```
323
+
324
+ ### Request Context (URL/Referrer Enrichment)
325
+
326
+ ```ruby
327
+ # lib/mbuzz/request_context.rb
328
+ module Mbuzz
329
+ class RequestContext
330
+ def self.with_context(request:)
331
+ Thread.current[:mbuzz_context] = new(request)
332
+ yield
333
+ ensure
334
+ Thread.current[:mbuzz_context] = nil
335
+ end
336
+
337
+ def self.current
338
+ Thread.current[:mbuzz_context]
339
+ end
340
+
341
+ attr_reader :request
342
+
343
+ def initialize(request)
344
+ @request = request
345
+ end
346
+
347
+ def url
348
+ request.url
349
+ end
350
+
351
+ def referrer
352
+ request.referrer
353
+ end
354
+
355
+ def visitor_id
356
+ request.env[Mbuzz::ENV_VISITOR_ID_KEY]
357
+ end
358
+
359
+ def session_id
360
+ request.env[Mbuzz::ENV_SESSION_ID_KEY]
361
+ end
362
+
363
+ def enriched_properties(custom = {})
364
+ { url: url, referrer: referrer }.compact.merge(custom)
365
+ end
366
+ end
367
+ end
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Implementation Checklist
373
+
374
+ ### Phase 1: API Rename
375
+ - [x] Add `Mbuzz.init` (wrapper for configure)
376
+ - [x] Add `Mbuzz.event` (wrapper for track)
377
+ - [x] Deprecate `Mbuzz.configure` with warning
378
+ - [x] Deprecate `Mbuzz.track` with warning
379
+ - [x] Remove `Mbuzz.alias` method
380
+ - [x] Update `Mbuzz.identify` to accept visitor_id
381
+
382
+ ### Phase 2: Internals
383
+ - [x] Update `lib/mbuzz/client/identify_request.rb` for visitor_id
384
+ - [x] Delete `lib/mbuzz/client/alias_request.rb`
385
+ - [x] Update middleware for session cookies (if not already)
386
+ - [x] Add RequestContext for URL/referrer enrichment
387
+
388
+ ### Phase 3: Documentation
389
+ - [x] Rewrite README.md
390
+ - [x] Update CHANGELOG.md
391
+ - [x] Update gemspec description
392
+
393
+ ### Phase 4: Testing
394
+ - [x] Unit tests for new API methods
395
+ - [ ] Integration test with backend
396
+ - [x] Verify cookies set correctly
397
+
398
+ ---
399
+
400
+ ## Migration Guide
401
+
402
+ ### Existing Integrations
403
+
404
+ **No code changes required for basic usage!** The SDK auto-enriches events with URL and referrer.
405
+
406
+ #### Configuration Change
407
+
408
+ ```ruby
409
+ # Before (v0.4.0)
410
+ Mbuzz.configure do |config|
411
+ config.api_key = ENV['MBUZZ_API_KEY']
412
+ end
413
+
414
+ # After (v0.5.0)
415
+ Mbuzz.init(api_key: ENV['MBUZZ_API_KEY'])
416
+ ```
417
+
418
+ #### Event Tracking Change
419
+
420
+ ```ruby
421
+ # Before (v0.4.0)
422
+ Mbuzz.track("add_to_cart", properties: { product_id: "123" })
423
+
424
+ # After (v0.5.0)
425
+ Mbuzz.event("add_to_cart", product_id: "123")
426
+ ```
427
+
428
+ #### Alias → Identify
429
+
430
+ ```ruby
431
+ # Before (v0.4.0)
432
+ Mbuzz.alias(user_id: "user_123", visitor_id: visitor_id)
433
+
434
+ # After (v0.5.0)
435
+ Mbuzz.identify("user_123") # visitor_id auto-captured from cookie
436
+ ```
437
+
438
+ ---
439
+
440
+ ## Testing Plan
441
+
442
+ ### Unit Tests
443
+
444
+ ```ruby
445
+ # test/mbuzz_test.rb
446
+ class MbuzzTest < Minitest::Test
447
+ def test_init_stores_api_key
448
+ Mbuzz.init(api_key: "sk_test_123")
449
+ assert_equal "sk_test_123", Mbuzz.config.api_key
450
+ end
451
+
452
+ def test_event_sends_to_api
453
+ stub_request(:post, "https://mbuzz.co/api/v1/events")
454
+ .to_return(status: 202, body: '{"accepted":1}')
455
+
456
+ result = Mbuzz.event("page_view", url: "https://example.com")
457
+ assert result
458
+ end
459
+
460
+ def test_identify_includes_visitor_id
461
+ stub_request(:post, "https://mbuzz.co/api/v1/identify")
462
+ .with(body: hash_including(visitor_id: "abc123"))
463
+ .to_return(status: 200, body: '{"success":true}')
464
+
465
+ with_visitor_id("abc123") do
466
+ Mbuzz.identify("user_123", traits: { email: "test@example.com" })
467
+ end
468
+ end
469
+ end
470
+ ```
471
+
472
+ ### Integration Test
473
+
474
+ ```ruby
475
+ # test/integration/tracking_test.rb
476
+ class TrackingIntegrationTest < ActionDispatch::IntegrationTest
477
+ def test_full_journey
478
+ # 1. Visit with UTMs
479
+ get "/?utm_source=google&utm_campaign=q4"
480
+ assert cookies["_mbuzz_vid"].present?
481
+ assert cookies["_mbuzz_sid"].present?
482
+
483
+ # 2. Track event
484
+ Mbuzz.event("page_view")
485
+
486
+ # 3. Convert
487
+ Mbuzz.conversion("signup", revenue: 0)
488
+
489
+ # 4. Identify
490
+ result = Mbuzz.identify("user_123")
491
+ assert result[:visitor_linked]
492
+ end
493
+ end
494
+ ```
495
+
496
+ ---
497
+
498
+ ## Success Metrics
499
+
500
+ After deployment, verify:
501
+
502
+ 1. **Visitor creation**: `Visitor.where(created_at: 1.day.ago..).count` should match unique visitors
503
+ 2. **Session tracking**: Sessions with UTMs should have `initial_utm` populated
504
+ 3. **Event enrichment**: Events should have `url` in properties
505
+ 4. **Attribution accuracy**: Conversions attributed to original acquisition channel
data/mbuzz-0.6.0.gem ADDED
Binary file
data/sig/mbuzz.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Mbuzz
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mbuzz
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.2
5
+ platform: ruby
6
+ authors:
7
+ - mbuzz team
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-12-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ description: 'Track customer journeys, attribute conversions, know which channels
28
+ drive revenue. Simple 4-call API: init, event, conversion, identify. Works with
29
+ Rails, Sinatra, and any Rack application.'
30
+ email:
31
+ - support@mbuzz.co
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - CHANGELOG.md
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - lib/mbuzz.rb
41
+ - lib/mbuzz/api.rb
42
+ - lib/mbuzz/client.rb
43
+ - lib/mbuzz/client/conversion_request.rb
44
+ - lib/mbuzz/client/identify_request.rb
45
+ - lib/mbuzz/client/session_request.rb
46
+ - lib/mbuzz/client/track_request.rb
47
+ - lib/mbuzz/configuration.rb
48
+ - lib/mbuzz/controller_helpers.rb
49
+ - lib/mbuzz/middleware/tracking.rb
50
+ - lib/mbuzz/railtie.rb
51
+ - lib/mbuzz/request_context.rb
52
+ - lib/mbuzz/version.rb
53
+ - lib/mbuzz/visitor/identifier.rb
54
+ - lib/specs/old/SPECIFICATION.md
55
+ - lib/specs/old/conversions.md
56
+ - lib/specs/old/event_ids_response.md
57
+ - lib/specs/old/v0.2.0_breaking_changes.md
58
+ - lib/specs/old/v2.0.0_sessions_upgrade.md
59
+ - lib/specs/v0.5.0_four_call_model.md
60
+ - mbuzz-0.6.0.gem
61
+ - sig/mbuzz.rbs
62
+ homepage: https://mbuzz.co
63
+ licenses:
64
+ - MIT
65
+ metadata:
66
+ homepage_uri: https://mbuzz.co
67
+ source_code_uri: https://github.com/mbuzz/mbuzz-ruby
68
+ changelog_uri: https://github.com/mbuzz/mbuzz-ruby/blob/main/CHANGELOG.md
69
+ documentation_uri: https://mbuzz.co/docs
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 3.0.0
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.5.16
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Server-side multi-touch attribution for Ruby
89
+ test_files: []