mbuzz 0.6.7 → 0.7.0
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/CHANGELOG.md +48 -0
- data/lib/mbuzz/client/conversion_request.rb +13 -1
- data/lib/mbuzz/client/track_request.rb +7 -3
- data/lib/mbuzz/client.rb +7 -9
- data/lib/mbuzz/controller_helpers.rb +0 -4
- data/lib/mbuzz/middleware/tracking.rb +3 -99
- data/lib/mbuzz/request_context.rb +6 -0
- data/lib/mbuzz/version.rb +1 -1
- data/lib/mbuzz.rb +28 -15
- metadata +1 -3
- data/lib/mbuzz/client/session_request.rb +0 -43
- data/lib/mbuzz/session/id_generator.rb +0 -33
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fa7c025330829c88ab9a75a22ca286d14adc1df7872295c5519df4c89536db9
|
|
4
|
+
data.tar.gz: 62d6675e5ffe9504e2262f84551c286366acc2ad42b6ca6d224367902d9ec3fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb90968b0d4c17bdc7c5019fa0058c257233bc00e1921f0b70a3dbf885fd8aecd1f9d80ab125d31ab535be5e55a44ecc2e0d46e9cb9c51fef4b9305634241b2f
|
|
7
|
+
data.tar.gz: cef7cbbc7817a6d7c4eca635594ac9e3b9661d81b33cc7d956376cb9fd763a6d216651e6ec499146e31dca042f024216f3c85c08c6d83f4f31dfb1fff64e8b56
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-01-09
|
|
9
|
+
|
|
10
|
+
### Breaking Changes
|
|
11
|
+
|
|
12
|
+
- **Session cookie removed** - SDK no longer sets or reads `_mbuzz_sid` cookie
|
|
13
|
+
- **Session ID generation removed** - Server handles all session resolution
|
|
14
|
+
- **`Mbuzz.session_id` removed** - Use server-side session resolution instead
|
|
15
|
+
- **`Mbuzz::Client.session()` removed** - Sessions are created server-side
|
|
16
|
+
- **`session_id` parameter removed from `Client.track()`** - Not needed with server-side resolution
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Cross-device identity resolution** - New `identifier` parameter for linking sessions across devices
|
|
21
|
+
- `Mbuzz.event("page_view", identifier: { email: "user@example.com" })`
|
|
22
|
+
- `Mbuzz.conversion("purchase", identifier: { email: "user@example.com" })`
|
|
23
|
+
- **Conversion fingerprint fallback** - `ip` and `user_agent` parameters on `Client.conversion()`
|
|
24
|
+
- When visitor_id is not found, server can find visitor via recent session with same fingerprint
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **Simplified middleware** - Only manages visitor cookie (`_mbuzz_vid`), no session handling
|
|
29
|
+
- **Server-side session resolution** - All session creation and resolution happens on the API server
|
|
30
|
+
- Enables true 30-minute sliding windows (vs fixed time buckets)
|
|
31
|
+
- Eliminates duplicate visitor problem from concurrent Turbo/Hotwire requests
|
|
32
|
+
- Better cross-device tracking with identity resolution
|
|
33
|
+
|
|
34
|
+
### Migration Guide
|
|
35
|
+
|
|
36
|
+
1. Remove any code that reads `Mbuzz.session_id` or `_mbuzz_sid` cookie
|
|
37
|
+
2. Remove any calls to `Mbuzz::Client.session()`
|
|
38
|
+
3. Ensure `ip` and `user_agent` are passed to track/conversion calls (handled automatically if using middleware)
|
|
39
|
+
4. Optionally add `identifier` parameter for cross-device tracking
|
|
40
|
+
|
|
41
|
+
## [0.6.8] - 2025-12-30
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **Server-side session resolution support** - SDK now forwards `ip` and `user_agent` to the API for server-side session identification
|
|
46
|
+
- `ip` and `user_agent` parameters on `Mbuzz::Client.track()`
|
|
47
|
+
- `RequestContext#ip` method with proxy header support (X-Forwarded-For, X-Real-IP)
|
|
48
|
+
- `Mbuzz.event` automatically extracts and forwards ip/user_agent from request context
|
|
49
|
+
|
|
50
|
+
### Technical Details
|
|
51
|
+
|
|
52
|
+
- IP extraction priority: `X-Forwarded-For` (first IP) > `X-Real-IP` > direct IP
|
|
53
|
+
- Enables accurate session tracking without client-side cookies
|
|
54
|
+
- Backwards compatible - ip/user_agent are optional parameters
|
|
55
|
+
|
|
8
56
|
## [0.6.0] - 2025-12-05
|
|
9
57
|
|
|
10
58
|
### Added
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Mbuzz
|
|
4
4
|
class Client
|
|
5
5
|
class ConversionRequest
|
|
6
|
-
def initialize(event_id:, visitor_id:, user_id:, conversion_type:, revenue:, currency:, is_acquisition:, inherit_acquisition:, properties:)
|
|
6
|
+
def initialize(event_id:, visitor_id:, user_id:, conversion_type:, revenue:, currency:, is_acquisition:, inherit_acquisition:, properties:, ip: nil, user_agent: nil, identifier: nil)
|
|
7
7
|
@event_id = event_id
|
|
8
8
|
@visitor_id = visitor_id
|
|
9
9
|
@user_id = user_id
|
|
@@ -13,6 +13,9 @@ module Mbuzz
|
|
|
13
13
|
@is_acquisition = is_acquisition
|
|
14
14
|
@inherit_acquisition = inherit_acquisition
|
|
15
15
|
@properties = properties
|
|
16
|
+
@ip = ip
|
|
17
|
+
@user_agent = user_agent
|
|
18
|
+
@identifier = identifier
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def call
|
|
@@ -51,6 +54,7 @@ module Mbuzz
|
|
|
51
54
|
base_payload
|
|
52
55
|
.merge(optional_identifiers)
|
|
53
56
|
.merge(optional_acquisition_fields)
|
|
57
|
+
.merge(fingerprint_fields)
|
|
54
58
|
end
|
|
55
59
|
|
|
56
60
|
def base_payload
|
|
@@ -78,6 +82,14 @@ module Mbuzz
|
|
|
78
82
|
end
|
|
79
83
|
end
|
|
80
84
|
|
|
85
|
+
def fingerprint_fields
|
|
86
|
+
{}.tap do |h|
|
|
87
|
+
h[:ip] = @ip if @ip
|
|
88
|
+
h[:user_agent] = @user_agent if @user_agent
|
|
89
|
+
h[:identifier] = @identifier if @identifier
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
81
93
|
def present?(value) = value && !value.to_s.strip.empty?
|
|
82
94
|
def hash?(value) = value.is_a?(Hash)
|
|
83
95
|
end
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
module Mbuzz
|
|
4
4
|
class Client
|
|
5
5
|
class TrackRequest
|
|
6
|
-
def initialize(user_id, visitor_id,
|
|
6
|
+
def initialize(user_id, visitor_id, event_type, properties, ip = nil, user_agent = nil, identifier = nil)
|
|
7
7
|
@user_id = user_id
|
|
8
8
|
@visitor_id = visitor_id
|
|
9
|
-
@session_id = session_id
|
|
10
9
|
@event_type = event_type
|
|
11
10
|
@properties = properties
|
|
11
|
+
@ip = ip
|
|
12
|
+
@user_agent = user_agent
|
|
13
|
+
@identifier = identifier
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def call
|
|
@@ -36,9 +38,11 @@ module Mbuzz
|
|
|
36
38
|
{
|
|
37
39
|
user_id: @user_id,
|
|
38
40
|
visitor_id: @visitor_id,
|
|
39
|
-
session_id: @session_id,
|
|
40
41
|
event_type: @event_type,
|
|
41
42
|
properties: @properties,
|
|
43
|
+
ip: @ip,
|
|
44
|
+
user_agent: @user_agent,
|
|
45
|
+
identifier: @identifier,
|
|
42
46
|
timestamp: Time.now.utc.iso8601
|
|
43
47
|
}.compact
|
|
44
48
|
end
|
data/lib/mbuzz/client.rb
CHANGED
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
require_relative "client/track_request"
|
|
4
4
|
require_relative "client/identify_request"
|
|
5
5
|
require_relative "client/conversion_request"
|
|
6
|
-
require_relative "client/session_request"
|
|
7
6
|
|
|
8
7
|
module Mbuzz
|
|
9
8
|
class Client
|
|
10
|
-
def self.track(user_id: nil, visitor_id: nil,
|
|
11
|
-
TrackRequest.new(user_id, visitor_id,
|
|
9
|
+
def self.track(user_id: nil, visitor_id: nil, event_type:, properties: {}, ip: nil, user_agent: nil, identifier: nil)
|
|
10
|
+
TrackRequest.new(user_id, visitor_id, event_type, properties, ip, user_agent, identifier).call
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
def self.identify(user_id:, visitor_id: nil, traits: {})
|
|
15
14
|
IdentifyRequest.new(user_id, visitor_id, traits).call
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
def self.conversion(event_id: nil, visitor_id: nil, user_id: nil, conversion_type:, revenue: nil, currency: "USD", is_acquisition: false, inherit_acquisition: false, properties: {})
|
|
17
|
+
def self.conversion(event_id: nil, visitor_id: nil, user_id: nil, conversion_type:, revenue: nil, currency: "USD", is_acquisition: false, inherit_acquisition: false, properties: {}, ip: nil, user_agent: nil, identifier: nil)
|
|
19
18
|
ConversionRequest.new(
|
|
20
19
|
event_id: event_id,
|
|
21
20
|
visitor_id: visitor_id,
|
|
@@ -25,12 +24,11 @@ module Mbuzz
|
|
|
25
24
|
currency: currency,
|
|
26
25
|
is_acquisition: is_acquisition,
|
|
27
26
|
inherit_acquisition: inherit_acquisition,
|
|
28
|
-
properties: properties
|
|
27
|
+
properties: properties,
|
|
28
|
+
ip: ip,
|
|
29
|
+
user_agent: user_agent,
|
|
30
|
+
identifier: identifier
|
|
29
31
|
).call
|
|
30
32
|
end
|
|
31
|
-
|
|
32
|
-
def self.session(visitor_id:, session_id:, url:, referrer: nil, started_at: nil)
|
|
33
|
-
SessionRequest.new(visitor_id, session_id, url, referrer, started_at).call
|
|
34
|
-
end
|
|
35
33
|
end
|
|
36
34
|
end
|
|
@@ -17,13 +17,10 @@ module Mbuzz
|
|
|
17
17
|
|
|
18
18
|
env[ENV_VISITOR_ID_KEY] = context[:visitor_id]
|
|
19
19
|
env[ENV_USER_ID_KEY] = context[:user_id]
|
|
20
|
-
env[ENV_SESSION_ID_KEY] = context[:session_id]
|
|
21
20
|
|
|
22
21
|
RequestContext.with_context(request: request) do
|
|
23
|
-
create_session_if_new(context, request)
|
|
24
|
-
|
|
25
22
|
status, headers, body = @app.call(env)
|
|
26
|
-
|
|
23
|
+
set_visitor_cookie(headers, context, request)
|
|
27
24
|
[status, headers, body]
|
|
28
25
|
end
|
|
29
26
|
end
|
|
@@ -51,9 +48,7 @@ module Mbuzz
|
|
|
51
48
|
def build_request_context(request)
|
|
52
49
|
{
|
|
53
50
|
visitor_id: resolve_visitor_id(request),
|
|
54
|
-
|
|
55
|
-
user_id: user_id_from_session(request),
|
|
56
|
-
new_session: new_session?(request)
|
|
51
|
+
user_id: user_id_from_session(request)
|
|
57
52
|
}.freeze
|
|
58
53
|
end
|
|
59
54
|
|
|
@@ -61,91 +56,15 @@ module Mbuzz
|
|
|
61
56
|
visitor_id_from_cookie(request) || Visitor::Identifier.generate
|
|
62
57
|
end
|
|
63
58
|
|
|
64
|
-
def resolve_session_id(request)
|
|
65
|
-
session_id_from_cookie(request) || generate_session_id(request)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
59
|
def visitor_id_from_cookie(request)
|
|
69
60
|
request.cookies[VISITOR_COOKIE_NAME]
|
|
70
61
|
end
|
|
71
62
|
|
|
72
|
-
def session_id_from_cookie(request)
|
|
73
|
-
request.cookies[SESSION_COOKIE_NAME]
|
|
74
|
-
end
|
|
75
|
-
|
|
76
63
|
def user_id_from_session(request)
|
|
77
64
|
request.session[SESSION_USER_ID_KEY] if request.session
|
|
78
65
|
end
|
|
79
66
|
|
|
80
|
-
|
|
81
|
-
session_id_from_cookie(request).nil?
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def generate_session_id(request)
|
|
85
|
-
existing_visitor_id = visitor_id_from_cookie(request)
|
|
86
|
-
|
|
87
|
-
if existing_visitor_id
|
|
88
|
-
Session::IdGenerator.generate_deterministic(visitor_id: existing_visitor_id)
|
|
89
|
-
else
|
|
90
|
-
Session::IdGenerator.generate_from_fingerprint(
|
|
91
|
-
client_ip: client_ip(request),
|
|
92
|
-
user_agent: user_agent(request)
|
|
93
|
-
)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def client_ip(request)
|
|
98
|
-
request.env["HTTP_X_FORWARDED_FOR"]&.split(",")&.first&.strip ||
|
|
99
|
-
request.env["HTTP_X_REAL_IP"] ||
|
|
100
|
-
request.ip ||
|
|
101
|
-
"unknown"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def user_agent(request)
|
|
105
|
-
request.user_agent || "unknown"
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Session creation
|
|
109
|
-
|
|
110
|
-
def create_session_if_new(context, request)
|
|
111
|
-
return unless context[:new_session]
|
|
112
|
-
|
|
113
|
-
create_session_async(context, request)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def create_session_async(context, request)
|
|
117
|
-
# Capture values in local variables for thread safety
|
|
118
|
-
visitor_id = context[:visitor_id]
|
|
119
|
-
session_id = context[:session_id]
|
|
120
|
-
url = request.url
|
|
121
|
-
referrer = request.referer
|
|
122
|
-
|
|
123
|
-
Thread.new do
|
|
124
|
-
create_session(visitor_id, session_id, url, referrer)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def create_session(visitor_id, session_id, url, referrer)
|
|
129
|
-
Client.session(
|
|
130
|
-
visitor_id: visitor_id,
|
|
131
|
-
session_id: session_id,
|
|
132
|
-
url: url,
|
|
133
|
-
referrer: referrer
|
|
134
|
-
)
|
|
135
|
-
rescue => e
|
|
136
|
-
log_session_error(e)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def log_session_error(error)
|
|
140
|
-
Mbuzz.config.logger&.error("Session creation failed: #{error.message}")
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Cookie setting
|
|
144
|
-
|
|
145
|
-
def set_cookies(headers, context, request)
|
|
146
|
-
set_visitor_cookie(headers, context, request)
|
|
147
|
-
set_session_cookie(headers, context, request)
|
|
148
|
-
end
|
|
67
|
+
# Cookie setting - only visitor cookie (sessions are server-side)
|
|
149
68
|
|
|
150
69
|
def set_visitor_cookie(headers, context, request)
|
|
151
70
|
Rack::Utils.set_cookie_header!(
|
|
@@ -155,14 +74,6 @@ module Mbuzz
|
|
|
155
74
|
)
|
|
156
75
|
end
|
|
157
76
|
|
|
158
|
-
def set_session_cookie(headers, context, request)
|
|
159
|
-
Rack::Utils.set_cookie_header!(
|
|
160
|
-
headers,
|
|
161
|
-
SESSION_COOKIE_NAME,
|
|
162
|
-
session_cookie_options(context, request)
|
|
163
|
-
)
|
|
164
|
-
end
|
|
165
|
-
|
|
166
77
|
def visitor_cookie_options(context, request)
|
|
167
78
|
base_cookie_options(request).merge(
|
|
168
79
|
value: context[:visitor_id],
|
|
@@ -170,13 +81,6 @@ module Mbuzz
|
|
|
170
81
|
)
|
|
171
82
|
end
|
|
172
83
|
|
|
173
|
-
def session_cookie_options(context, request)
|
|
174
|
-
base_cookie_options(request).merge(
|
|
175
|
-
value: context[:session_id],
|
|
176
|
-
max_age: SESSION_COOKIE_MAX_AGE
|
|
177
|
-
)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
84
|
def base_cookie_options(request)
|
|
181
85
|
options = {
|
|
182
86
|
path: VISITOR_COOKIE_PATH,
|
|
@@ -33,6 +33,12 @@ module Mbuzz
|
|
|
33
33
|
@request.user_agent
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def ip
|
|
37
|
+
@request.env["HTTP_X_FORWARDED_FOR"]&.split(",")&.first&.strip ||
|
|
38
|
+
@request.env["HTTP_X_REAL_IP"] ||
|
|
39
|
+
@request.ip
|
|
40
|
+
end
|
|
41
|
+
|
|
36
42
|
def enriched_properties(custom = {})
|
|
37
43
|
{ url: url, referrer: referrer }.compact.merge(custom)
|
|
38
44
|
end
|
data/lib/mbuzz/version.rb
CHANGED
data/lib/mbuzz.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require_relative "mbuzz/version"
|
|
4
4
|
require_relative "mbuzz/configuration"
|
|
5
5
|
require_relative "mbuzz/visitor/identifier"
|
|
6
|
-
require_relative "mbuzz/session/id_generator"
|
|
7
6
|
require_relative "mbuzz/request_context"
|
|
8
7
|
require_relative "mbuzz/api"
|
|
9
8
|
require_relative "mbuzz/client"
|
|
@@ -18,20 +17,15 @@ module Mbuzz
|
|
|
18
17
|
EVENTS_PATH = "/events"
|
|
19
18
|
IDENTIFY_PATH = "/identify"
|
|
20
19
|
CONVERSIONS_PATH = "/conversions"
|
|
21
|
-
SESSIONS_PATH = "/sessions"
|
|
22
20
|
|
|
23
21
|
VISITOR_COOKIE_NAME = "_mbuzz_vid"
|
|
24
22
|
VISITOR_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 2 # 2 years
|
|
25
23
|
VISITOR_COOKIE_PATH = "/"
|
|
26
24
|
VISITOR_COOKIE_SAME_SITE = "Lax"
|
|
27
25
|
|
|
28
|
-
SESSION_COOKIE_NAME = "_mbuzz_sid"
|
|
29
|
-
SESSION_COOKIE_MAX_AGE = 30 * 60 # 30 minutes
|
|
30
|
-
|
|
31
26
|
SESSION_USER_ID_KEY = "user_id"
|
|
32
27
|
ENV_USER_ID_KEY = "mbuzz.user_id"
|
|
33
28
|
ENV_VISITOR_ID_KEY = "mbuzz.visitor_id"
|
|
34
|
-
ENV_SESSION_ID_KEY = "mbuzz.session_id"
|
|
35
29
|
|
|
36
30
|
# ============================================================================
|
|
37
31
|
# Configuration
|
|
@@ -81,10 +75,6 @@ module Mbuzz
|
|
|
81
75
|
RequestContext.current&.request&.env&.dig(ENV_USER_ID_KEY)
|
|
82
76
|
end
|
|
83
77
|
|
|
84
|
-
def self.session_id
|
|
85
|
-
RequestContext.current&.request&.env&.dig(ENV_SESSION_ID_KEY)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
78
|
# ============================================================================
|
|
89
79
|
# 4-Call Model API
|
|
90
80
|
# ============================================================================
|
|
@@ -93,18 +83,24 @@ module Mbuzz
|
|
|
93
83
|
#
|
|
94
84
|
# @param event_type [String] The name of the event
|
|
95
85
|
# @param properties [Hash] Custom event properties (url, referrer auto-added)
|
|
86
|
+
# @param identifier [Hash, nil] Optional identifier for cross-device identity resolution
|
|
96
87
|
# @return [Hash, false] Result hash on success, false on failure
|
|
97
88
|
#
|
|
98
89
|
# @example
|
|
99
90
|
# Mbuzz.event("add_to_cart", product_id: "SKU-123", price: 49.99)
|
|
100
91
|
#
|
|
101
|
-
|
|
92
|
+
# @example With identifier for cross-device tracking
|
|
93
|
+
# Mbuzz.event("page_view", identifier: { email: "user@example.com" })
|
|
94
|
+
#
|
|
95
|
+
def self.event(event_type, identifier: nil, **properties)
|
|
102
96
|
Client.track(
|
|
103
97
|
visitor_id: visitor_id,
|
|
104
|
-
session_id: session_id,
|
|
105
98
|
user_id: user_id,
|
|
106
99
|
event_type: event_type,
|
|
107
|
-
properties: enriched_properties(properties)
|
|
100
|
+
properties: enriched_properties(properties),
|
|
101
|
+
ip: current_ip,
|
|
102
|
+
user_agent: current_user_agent,
|
|
103
|
+
identifier: identifier
|
|
108
104
|
)
|
|
109
105
|
end
|
|
110
106
|
|
|
@@ -121,6 +117,7 @@ module Mbuzz
|
|
|
121
117
|
# @param user_id [String, nil] User ID for acquisition-linked conversions
|
|
122
118
|
# @param is_acquisition [Boolean] Mark this as the acquisition conversion for this user
|
|
123
119
|
# @param inherit_acquisition [Boolean] Inherit attribution from user's acquisition conversion
|
|
120
|
+
# @param identifier [Hash, nil] Optional identifier for cross-device identity resolution
|
|
124
121
|
# @param properties [Hash] Custom properties
|
|
125
122
|
# @return [Hash, false] Result hash on success, false on failure
|
|
126
123
|
#
|
|
@@ -133,7 +130,10 @@ module Mbuzz
|
|
|
133
130
|
# @example Recurring revenue (inherits attribution from acquisition)
|
|
134
131
|
# Mbuzz.conversion("payment", user_id: "user_123", revenue: 49.00, inherit_acquisition: true)
|
|
135
132
|
#
|
|
136
|
-
|
|
133
|
+
# @example With identifier for cross-device tracking
|
|
134
|
+
# Mbuzz.conversion("purchase", identifier: { email: "user@example.com" })
|
|
135
|
+
#
|
|
136
|
+
def self.conversion(conversion_type, revenue: nil, user_id: nil, is_acquisition: false, inherit_acquisition: false, identifier: nil, **properties)
|
|
137
137
|
Client.conversion(
|
|
138
138
|
visitor_id: visitor_id,
|
|
139
139
|
user_id: user_id,
|
|
@@ -141,7 +141,10 @@ module Mbuzz
|
|
|
141
141
|
revenue: revenue,
|
|
142
142
|
is_acquisition: is_acquisition,
|
|
143
143
|
inherit_acquisition: inherit_acquisition,
|
|
144
|
-
properties: enriched_properties(properties)
|
|
144
|
+
properties: enriched_properties(properties),
|
|
145
|
+
ip: current_ip,
|
|
146
|
+
user_agent: current_user_agent,
|
|
147
|
+
identifier: identifier
|
|
145
148
|
)
|
|
146
149
|
end
|
|
147
150
|
|
|
@@ -176,4 +179,14 @@ module Mbuzz
|
|
|
176
179
|
RequestContext.current.enriched_properties(custom_properties)
|
|
177
180
|
end
|
|
178
181
|
private_class_method :enriched_properties
|
|
182
|
+
|
|
183
|
+
def self.current_ip
|
|
184
|
+
RequestContext.current&.ip
|
|
185
|
+
end
|
|
186
|
+
private_class_method :current_ip
|
|
187
|
+
|
|
188
|
+
def self.current_user_agent
|
|
189
|
+
RequestContext.current&.user_agent
|
|
190
|
+
end
|
|
191
|
+
private_class_method :current_user_agent
|
|
179
192
|
end
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mbuzz team
|
|
@@ -44,14 +44,12 @@ files:
|
|
|
44
44
|
- lib/mbuzz/client.rb
|
|
45
45
|
- lib/mbuzz/client/conversion_request.rb
|
|
46
46
|
- lib/mbuzz/client/identify_request.rb
|
|
47
|
-
- lib/mbuzz/client/session_request.rb
|
|
48
47
|
- lib/mbuzz/client/track_request.rb
|
|
49
48
|
- lib/mbuzz/configuration.rb
|
|
50
49
|
- lib/mbuzz/controller_helpers.rb
|
|
51
50
|
- lib/mbuzz/middleware/tracking.rb
|
|
52
51
|
- lib/mbuzz/railtie.rb
|
|
53
52
|
- lib/mbuzz/request_context.rb
|
|
54
|
-
- lib/mbuzz/session/id_generator.rb
|
|
55
53
|
- lib/mbuzz/version.rb
|
|
56
54
|
- lib/mbuzz/visitor/identifier.rb
|
|
57
55
|
- lib/specs/old/SPECIFICATION.md
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Mbuzz
|
|
4
|
-
class Client
|
|
5
|
-
class SessionRequest
|
|
6
|
-
def initialize(visitor_id, session_id, url, referrer, started_at)
|
|
7
|
-
@visitor_id = visitor_id
|
|
8
|
-
@session_id = session_id
|
|
9
|
-
@url = url
|
|
10
|
-
@referrer = referrer
|
|
11
|
-
@started_at = started_at || Time.now.utc.iso8601
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def call
|
|
15
|
-
return false unless valid?
|
|
16
|
-
|
|
17
|
-
Api.post(SESSIONS_PATH, payload)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
attr_reader :visitor_id, :session_id, :url, :referrer, :started_at
|
|
23
|
-
|
|
24
|
-
def valid?
|
|
25
|
-
present?(visitor_id) && present?(session_id) && present?(url)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def payload
|
|
29
|
-
{
|
|
30
|
-
session: {
|
|
31
|
-
visitor_id: visitor_id,
|
|
32
|
-
session_id: session_id,
|
|
33
|
-
url: url,
|
|
34
|
-
referrer: referrer,
|
|
35
|
-
started_at: started_at
|
|
36
|
-
}.compact
|
|
37
|
-
}
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def present?(value) = value && !value.to_s.strip.empty?
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "digest"
|
|
4
|
-
require "securerandom"
|
|
5
|
-
|
|
6
|
-
module Mbuzz
|
|
7
|
-
module Session
|
|
8
|
-
class IdGenerator
|
|
9
|
-
SESSION_TIMEOUT_SECONDS = 1800
|
|
10
|
-
SESSION_ID_LENGTH = 64
|
|
11
|
-
FINGERPRINT_LENGTH = 32
|
|
12
|
-
|
|
13
|
-
class << self
|
|
14
|
-
def generate_deterministic(visitor_id:, timestamp: Time.now.to_i)
|
|
15
|
-
time_bucket = timestamp / SESSION_TIMEOUT_SECONDS
|
|
16
|
-
raw = "#{visitor_id}_#{time_bucket}"
|
|
17
|
-
Digest::SHA256.hexdigest(raw)[0, SESSION_ID_LENGTH]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def generate_from_fingerprint(client_ip:, user_agent:, timestamp: Time.now.to_i)
|
|
21
|
-
fingerprint = Digest::SHA256.hexdigest("#{client_ip}|#{user_agent}")[0, FINGERPRINT_LENGTH]
|
|
22
|
-
time_bucket = timestamp / SESSION_TIMEOUT_SECONDS
|
|
23
|
-
raw = "#{fingerprint}_#{time_bucket}"
|
|
24
|
-
Digest::SHA256.hexdigest(raw)[0, SESSION_ID_LENGTH]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def generate_random
|
|
28
|
-
SecureRandom.hex(32)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|