mbuzz 0.7.0 → 0.7.1
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 +4 -4
- data/README.md +57 -0
- data/lib/mbuzz/current.rb +28 -0
- data/lib/mbuzz/middleware/tracking.rb +27 -0
- data/lib/mbuzz/version.rb +1 -1
- data/lib/mbuzz.rb +58 -15
- data/lib/specs/v0.7.0_deterministic_sessions.md +8 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eb41261b9e19c46609f4e7a8867d73b5a902acecc3db3362b4a47e2cacefa9db
|
|
4
|
+
data.tar.gz: ce644b10fa63055065960bb72a61eb53077d4bbe110eac40ed5bd6918d2487c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03bed0944d6ee7286e4f002d05032c8ced19d013da1dae69d429046b1ebad510cae67510d48d9af53fac8a8832a1a1495657c6b671a16976fabb5eb8ec316f3d
|
|
7
|
+
data.tar.gz: 4a0693caaa53219fbf4368c1b194722889c516e88eea6f7bfe781f7a44293659977e6691901f238f45ef66f08f367d674c2f22e6f8a63e22f3326368e6ae8f10
|
data/README.md
CHANGED
|
@@ -121,6 +121,63 @@ Mbuzz.user_id # Current user ID (from session["user_id"])
|
|
|
121
121
|
Mbuzz.session_id # Current session ID (from cookie)
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
## Background Jobs
|
|
125
|
+
|
|
126
|
+
When tracking from background jobs (Sidekiq, GoodJob, etc.), there's no HTTP request context. Rails 7+ handles this automatically via `CurrentAttributes`.
|
|
127
|
+
|
|
128
|
+
### Rails 7+ (Automatic)
|
|
129
|
+
|
|
130
|
+
mbuzz uses `ActiveSupport::CurrentAttributes` which Rails automatically serializes into ActiveJob payloads:
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
# In your controller - just enqueue the job
|
|
134
|
+
class OrdersController < ApplicationController
|
|
135
|
+
def create
|
|
136
|
+
@order = Order.create!(order_params)
|
|
137
|
+
ProcessOrderJob.perform_later(@order.id)
|
|
138
|
+
# visitor_id is automatically captured and passed to the job
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# In your job - mbuzz just works!
|
|
143
|
+
class ProcessOrderJob < ApplicationJob
|
|
144
|
+
def perform(order_id)
|
|
145
|
+
order = Order.find(order_id)
|
|
146
|
+
# Mbuzz::Current.visitor_id was restored by Rails
|
|
147
|
+
Mbuzz.conversion("purchase", revenue: order.total)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**How it works:**
|
|
153
|
+
1. Middleware captures `visitor_id` from cookie into `Mbuzz::Current`
|
|
154
|
+
2. Controller enqueues job
|
|
155
|
+
3. Rails serializes `Mbuzz::Current` into job payload
|
|
156
|
+
4. Job runs → Rails restores `Mbuzz::Current`
|
|
157
|
+
5. `Mbuzz.conversion()` reads from `Current` - works!
|
|
158
|
+
|
|
159
|
+
### Alternative: Explicit visitor_id
|
|
160
|
+
|
|
161
|
+
For non-Rails apps or when you need more control:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
# Store visitor_id on your model
|
|
165
|
+
class Order < ApplicationRecord
|
|
166
|
+
before_create { self.mbuzz_visitor_id = Mbuzz.visitor_id }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Pass explicitly in background job
|
|
170
|
+
class ProcessOrderJob
|
|
171
|
+
def perform(order_id)
|
|
172
|
+
order = Order.find(order_id)
|
|
173
|
+
Mbuzz.conversion("purchase",
|
|
174
|
+
visitor_id: order.mbuzz_visitor_id, # Explicit
|
|
175
|
+
revenue: order.total
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
```
|
|
180
|
+
|
|
124
181
|
## Rack / Sinatra Integration
|
|
125
182
|
|
|
126
183
|
For non-Rails apps, add the middleware manually:
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mbuzz
|
|
4
|
+
# CurrentAttributes for automatic background job context propagation.
|
|
5
|
+
#
|
|
6
|
+
# Rails automatically serializes CurrentAttributes into ActiveJob payloads
|
|
7
|
+
# and restores them when jobs execute. This means visitor_id captured during
|
|
8
|
+
# the original request is available in background jobs without any manual
|
|
9
|
+
# passing or database storage.
|
|
10
|
+
#
|
|
11
|
+
# How it works:
|
|
12
|
+
# 1. Middleware captures visitor_id from cookie
|
|
13
|
+
# 2. Stores in Mbuzz::Current.visitor_id
|
|
14
|
+
# 3. Controller enqueues background job
|
|
15
|
+
# 4. Rails serializes Current attributes into job payload
|
|
16
|
+
# 5. Job runs on different thread/process
|
|
17
|
+
# 6. Rails restores Current.visitor_id before job executes
|
|
18
|
+
# 7. Mbuzz.event/conversion reads from Current.visitor_id
|
|
19
|
+
#
|
|
20
|
+
# This is why customers don't need to store visitor_id in their database.
|
|
21
|
+
#
|
|
22
|
+
class Current < ActiveSupport::CurrentAttributes
|
|
23
|
+
attribute :visitor_id
|
|
24
|
+
attribute :user_id
|
|
25
|
+
attribute :ip
|
|
26
|
+
attribute :user_agent
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -18,10 +18,14 @@ module Mbuzz
|
|
|
18
18
|
env[ENV_VISITOR_ID_KEY] = context[:visitor_id]
|
|
19
19
|
env[ENV_USER_ID_KEY] = context[:user_id]
|
|
20
20
|
|
|
21
|
+
store_in_current_attributes(context, request)
|
|
22
|
+
|
|
21
23
|
RequestContext.with_context(request: request) do
|
|
22
24
|
status, headers, body = @app.call(env)
|
|
23
25
|
set_visitor_cookie(headers, context, request)
|
|
24
26
|
[status, headers, body]
|
|
27
|
+
ensure
|
|
28
|
+
reset_current_attributes
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
|
|
@@ -90,6 +94,29 @@ module Mbuzz
|
|
|
90
94
|
options[:secure] = true if request.ssl?
|
|
91
95
|
options
|
|
92
96
|
end
|
|
97
|
+
|
|
98
|
+
# Store context in CurrentAttributes for background job propagation
|
|
99
|
+
def store_in_current_attributes(context, request)
|
|
100
|
+
return unless defined?(Mbuzz::Current)
|
|
101
|
+
|
|
102
|
+
Mbuzz::Current.visitor_id = context[:visitor_id]
|
|
103
|
+
Mbuzz::Current.user_id = context[:user_id]
|
|
104
|
+
Mbuzz::Current.ip = extract_ip(request)
|
|
105
|
+
Mbuzz::Current.user_agent = request.user_agent
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def reset_current_attributes
|
|
109
|
+
return unless defined?(Mbuzz::Current)
|
|
110
|
+
|
|
111
|
+
Mbuzz::Current.reset
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def extract_ip(request)
|
|
115
|
+
forwarded = request.env["HTTP_X_FORWARDED_FOR"]
|
|
116
|
+
return forwarded.split(",").first.strip if forwarded
|
|
117
|
+
|
|
118
|
+
request.ip
|
|
119
|
+
end
|
|
93
120
|
end
|
|
94
121
|
end
|
|
95
122
|
end
|
data/lib/mbuzz/version.rb
CHANGED
data/lib/mbuzz.rb
CHANGED
|
@@ -9,6 +9,9 @@ require_relative "mbuzz/client"
|
|
|
9
9
|
require_relative "mbuzz/middleware/tracking"
|
|
10
10
|
require_relative "mbuzz/controller_helpers"
|
|
11
11
|
|
|
12
|
+
# CurrentAttributes for automatic background job context propagation (Rails only)
|
|
13
|
+
require_relative "mbuzz/current" if defined?(ActiveSupport::CurrentAttributes)
|
|
14
|
+
|
|
12
15
|
require_relative "mbuzz/railtie" if defined?(Rails::Railtie)
|
|
13
16
|
|
|
14
17
|
module Mbuzz
|
|
@@ -62,18 +65,25 @@ module Mbuzz
|
|
|
62
65
|
# Context Accessors
|
|
63
66
|
# ============================================================================
|
|
64
67
|
|
|
68
|
+
# Returns visitor_id from Current (background jobs) or request context
|
|
65
69
|
def self.visitor_id
|
|
66
|
-
RequestContext.current&.request&.env&.dig(ENV_VISITOR_ID_KEY)
|
|
70
|
+
current_visitor_id || RequestContext.current&.request&.env&.dig(ENV_VISITOR_ID_KEY)
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
def self.
|
|
70
|
-
|
|
73
|
+
def self.user_id
|
|
74
|
+
current_user_id || RequestContext.current&.request&.env&.dig(ENV_USER_ID_KEY)
|
|
71
75
|
end
|
|
72
|
-
private_class_method :fallback_visitor_id
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
# Check Current attributes (for background job support)
|
|
78
|
+
def self.current_visitor_id
|
|
79
|
+
defined?(Current) ? Current.visitor_id : nil
|
|
80
|
+
end
|
|
81
|
+
private_class_method :current_visitor_id
|
|
82
|
+
|
|
83
|
+
def self.current_user_id
|
|
84
|
+
defined?(Current) ? Current.user_id : nil
|
|
76
85
|
end
|
|
86
|
+
private_class_method :current_user_id
|
|
77
87
|
|
|
78
88
|
# ============================================================================
|
|
79
89
|
# 4-Call Model API
|
|
@@ -82,20 +92,30 @@ module Mbuzz
|
|
|
82
92
|
# Track an event (journey step)
|
|
83
93
|
#
|
|
84
94
|
# @param event_type [String] The name of the event
|
|
95
|
+
# @param visitor_id [String, nil] Explicit visitor ID (required for background jobs)
|
|
85
96
|
# @param properties [Hash] Custom event properties (url, referrer auto-added)
|
|
86
97
|
# @param identifier [Hash, nil] Optional identifier for cross-device identity resolution
|
|
87
98
|
# @return [Hash, false] Result hash on success, false on failure
|
|
88
99
|
#
|
|
89
|
-
# @example
|
|
100
|
+
# @example Normal usage (within request context)
|
|
90
101
|
# Mbuzz.event("add_to_cart", product_id: "SKU-123", price: 49.99)
|
|
91
102
|
#
|
|
103
|
+
# @example Background job (must pass explicit visitor_id)
|
|
104
|
+
# Mbuzz.event("order_processed", visitor_id: order.mbuzz_visitor_id, order_id: order.id)
|
|
105
|
+
#
|
|
92
106
|
# @example With identifier for cross-device tracking
|
|
93
107
|
# Mbuzz.event("page_view", identifier: { email: "user@example.com" })
|
|
94
108
|
#
|
|
95
|
-
def self.event(event_type, identifier: nil, **properties)
|
|
109
|
+
def self.event(event_type, visitor_id: nil, identifier: nil, **properties)
|
|
110
|
+
resolved_visitor_id = visitor_id || self.visitor_id
|
|
111
|
+
resolved_user_id = user_id
|
|
112
|
+
|
|
113
|
+
# Must have at least one identifier
|
|
114
|
+
return false unless resolved_visitor_id || resolved_user_id
|
|
115
|
+
|
|
96
116
|
Client.track(
|
|
97
|
-
visitor_id:
|
|
98
|
-
user_id:
|
|
117
|
+
visitor_id: resolved_visitor_id,
|
|
118
|
+
user_id: resolved_user_id,
|
|
99
119
|
event_type: event_type,
|
|
100
120
|
properties: enriched_properties(properties),
|
|
101
121
|
ip: current_ip,
|
|
@@ -113,6 +133,7 @@ module Mbuzz
|
|
|
113
133
|
# Track a conversion (revenue-generating outcome)
|
|
114
134
|
#
|
|
115
135
|
# @param conversion_type [String] The type of conversion
|
|
136
|
+
# @param visitor_id [String, nil] Explicit visitor ID (required for background jobs)
|
|
116
137
|
# @param revenue [Numeric, nil] Revenue amount
|
|
117
138
|
# @param user_id [String, nil] User ID for acquisition-linked conversions
|
|
118
139
|
# @param is_acquisition [Boolean] Mark this as the acquisition conversion for this user
|
|
@@ -121,9 +142,12 @@ module Mbuzz
|
|
|
121
142
|
# @param properties [Hash] Custom properties
|
|
122
143
|
# @return [Hash, false] Result hash on success, false on failure
|
|
123
144
|
#
|
|
124
|
-
# @example Basic conversion
|
|
145
|
+
# @example Basic conversion (within request context)
|
|
125
146
|
# Mbuzz.conversion("purchase", revenue: 99.99, order_id: "ORD-123")
|
|
126
147
|
#
|
|
148
|
+
# @example Background job (must pass explicit visitor_id)
|
|
149
|
+
# Mbuzz.conversion("purchase", visitor_id: order.mbuzz_visitor_id, revenue: 99.99)
|
|
150
|
+
#
|
|
127
151
|
# @example Acquisition conversion (marks signup as THE acquisition moment)
|
|
128
152
|
# Mbuzz.conversion("signup", user_id: "user_123", is_acquisition: true)
|
|
129
153
|
#
|
|
@@ -133,9 +157,14 @@ module Mbuzz
|
|
|
133
157
|
# @example With identifier for cross-device tracking
|
|
134
158
|
# Mbuzz.conversion("purchase", identifier: { email: "user@example.com" })
|
|
135
159
|
#
|
|
136
|
-
def self.conversion(conversion_type, revenue: nil, user_id: nil, is_acquisition: false, inherit_acquisition: false, identifier: nil, **properties)
|
|
160
|
+
def self.conversion(conversion_type, visitor_id: nil, revenue: nil, user_id: nil, is_acquisition: false, inherit_acquisition: false, identifier: nil, **properties)
|
|
161
|
+
resolved_visitor_id = visitor_id || self.visitor_id
|
|
162
|
+
|
|
163
|
+
# Must have at least one identifier (visitor_id or user_id)
|
|
164
|
+
return false unless resolved_visitor_id || user_id
|
|
165
|
+
|
|
137
166
|
Client.conversion(
|
|
138
|
-
visitor_id:
|
|
167
|
+
visitor_id: resolved_visitor_id,
|
|
139
168
|
user_id: user_id,
|
|
140
169
|
conversion_type: conversion_type,
|
|
141
170
|
revenue: revenue,
|
|
@@ -181,12 +210,26 @@ module Mbuzz
|
|
|
181
210
|
private_class_method :enriched_properties
|
|
182
211
|
|
|
183
212
|
def self.current_ip
|
|
184
|
-
RequestContext.current&.ip
|
|
213
|
+
current_attributes_ip || RequestContext.current&.ip
|
|
185
214
|
end
|
|
186
215
|
private_class_method :current_ip
|
|
187
216
|
|
|
188
217
|
def self.current_user_agent
|
|
189
|
-
RequestContext.current&.user_agent
|
|
218
|
+
current_attributes_user_agent || RequestContext.current&.user_agent
|
|
190
219
|
end
|
|
191
220
|
private_class_method :current_user_agent
|
|
221
|
+
|
|
222
|
+
def self.current_attributes_ip
|
|
223
|
+
return nil unless defined?(Current)
|
|
224
|
+
|
|
225
|
+
Current.ip
|
|
226
|
+
end
|
|
227
|
+
private_class_method :current_attributes_ip
|
|
228
|
+
|
|
229
|
+
def self.current_attributes_user_agent
|
|
230
|
+
return nil unless defined?(Current)
|
|
231
|
+
|
|
232
|
+
Current.user_agent
|
|
233
|
+
end
|
|
234
|
+
private_class_method :current_attributes_user_agent
|
|
192
235
|
end
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# mbuzz SDK v0.7.0 - Deterministic Session IDs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **⚠️ SUPERSEDED**: This spec has been replaced by server-side session resolution.
|
|
4
|
+
> See: `multibuzz/lib/specs/1_visitor_session_tracking_spec.md`
|
|
5
|
+
>
|
|
6
|
+
> **What changed**: Instead of SDKs generating deterministic session IDs using time-buckets,
|
|
7
|
+
> the server now handles all session resolution using IP + User-Agent fingerprinting with
|
|
8
|
+
> a true 30-minute sliding window. SDKs no longer manage session cookies.
|
|
9
|
+
|
|
10
|
+
**Status**: SUPERSEDED (2026-01-09)
|
|
4
11
|
**Last Updated**: 2025-12-29
|
|
5
12
|
**Breaking Change**: No (backward compatible)
|
|
6
13
|
**Affects**: All SDKs (Ruby, Python, PHP, Node)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mbuzz
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mbuzz team
|
|
@@ -47,6 +47,7 @@ files:
|
|
|
47
47
|
- lib/mbuzz/client/track_request.rb
|
|
48
48
|
- lib/mbuzz/configuration.rb
|
|
49
49
|
- lib/mbuzz/controller_helpers.rb
|
|
50
|
+
- lib/mbuzz/current.rb
|
|
50
51
|
- lib/mbuzz/middleware/tracking.rb
|
|
51
52
|
- lib/mbuzz/railtie.rb
|
|
52
53
|
- lib/mbuzz/request_context.rb
|