track_relay 1.1.0 → 1.3.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 +47 -0
- data/lib/track_relay/client_id/signed_token.rb +80 -0
- data/lib/track_relay/configuration.rb +5 -1
- data/lib/track_relay/errors.rb +5 -0
- data/lib/track_relay/handshake_endpoint.rb +75 -0
- data/lib/track_relay/page_view_tracking.rb +102 -0
- data/lib/track_relay/subscribers/ga4_measurement_protocol.rb +17 -4
- data/lib/track_relay/version.rb +1 -1
- data/lib/track_relay.rb +3 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d71fbb5a7101c3f407ba9fc7c05bf032a5718447a7cacd341647b9b794c47b2
|
|
4
|
+
data.tar.gz: b36598739beda0519f71b98cf906159b3857234dd9510d7aff35d1f33ecd9808
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f71946bad98b203c57850991846de8e3fb95828bc825f5d9ccb2d6b5a205d6531253087a65e77c0e4eb4981f5e95129439ecf081fc719939618daeb93b611ca0
|
|
7
|
+
data.tar.gz: 1b05f9d685e43a6c97e37f306aa6f0a970f1b7fe6ccb774a037a4a26043295d7464a22ec2d821425aad6eb9ac6e1e07461420f7fc8ce6875f3bec1919e3d95e1
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-07-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `TrackRelay::PageViewTracking` — opt-in automatic server-side
|
|
14
|
+
`page_view` emission for hosts delivering GA4 purely via the
|
|
15
|
+
Measurement Protocol (no client gtag). `include
|
|
16
|
+
TrackRelay::PageViewTracking` + `track_page_views` emits exactly one
|
|
17
|
+
`page_view` per full HTML page render — never for JSON/non-HTML,
|
|
18
|
+
turbo-frame fetches, turbo-stream responses, redirects, non-GETs, or
|
|
19
|
+
unsuccessful responses. A host suppression hook (`track_page_views
|
|
20
|
+
if: :method_name` or a proc, instance-exec'd per request) is where
|
|
21
|
+
tracking policy (opt-out, geo, bot checks) plugs in. Events ride the
|
|
22
|
+
normal `TrackRelay.track` path, so `track_gate`, subscribers, and
|
|
23
|
+
GA4 page-context enrichment apply unchanged.
|
|
24
|
+
- The builtin `page_view` event is registered directly with the
|
|
25
|
+
catalog (and therefore the manifest), deliberately bypassing the
|
|
26
|
+
DSL's GA4 reserved-name validation: that guard protects *custom*
|
|
27
|
+
events from shadowing gtag auto-collection, but the MP-standard
|
|
28
|
+
`page_view` is exactly what a server-side host must send.
|
|
29
|
+
|
|
30
|
+
## [1.2.0] - 2026-07-31
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- `ClientId::SignedToken` — first-party, HMAC-signed analytics token
|
|
34
|
+
(ActiveSupport::MessageVerifier, SHA256) as a cryptographic
|
|
35
|
+
proof-of-browser replacement for `_ga`-cookie sniffing. Carries
|
|
36
|
+
`client_id`, `session_id`, `session_started_at`, and `last_seen_at`;
|
|
37
|
+
`mint`/`read`/`from_request` reject tampered, forged, wrong-secret,
|
|
38
|
+
and expired (`ttl:`) values by returning `nil`.
|
|
39
|
+
- `TrackRelay::HandshakeEndpoint` — routable Rack endpoint
|
|
40
|
+
(`post "/analytics/handshakes", to: TrackRelay::HandshakeEndpoint`)
|
|
41
|
+
that mints/refreshes the signed token cookie (HttpOnly,
|
|
42
|
+
SameSite=Lax, secure on SSL). Preserves an existing token's
|
|
43
|
+
client_id, rotates the session after 30 minutes of inactivity
|
|
44
|
+
(`SESSION_INACTIVITY_TIMEOUT`), and raises `ConfigurationError`
|
|
45
|
+
when `signed_client_token_secret` is unset.
|
|
46
|
+
- Config: `signed_client_token_secret` (default `nil` — feature off)
|
|
47
|
+
and `signed_client_token_ttl` (default 30 days).
|
|
48
|
+
- `ConfigurationError` error class.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
- `Subscribers::Ga4MeasurementProtocol#prepare` now prefers the signed
|
|
52
|
+
first-party token — for both the browser-proof client_id and
|
|
53
|
+
page-context session enrichment — falling back to the `_ga` /
|
|
54
|
+
`_ga_<stream>` cookies so hosts can migrate incrementally. Behavior
|
|
55
|
+
is unchanged for hosts that don't configure a signed-token secret.
|
|
56
|
+
|
|
10
57
|
## [1.1.0] - 2026-07-07
|
|
11
58
|
|
|
12
59
|
### Added
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/message_verifier"
|
|
4
|
+
|
|
5
|
+
module TrackRelay
|
|
6
|
+
module ClientId
|
|
7
|
+
# First-party, HMAC-signed client token — the spoof-proof
|
|
8
|
+
# replacement for `_ga`-cookie browser proof.
|
|
9
|
+
#
|
|
10
|
+
# The cookie value is generated and verified with
|
|
11
|
+
# {ActiveSupport::MessageVerifier} (SHA256), so any tampering,
|
|
12
|
+
# truncation, or wrong-secret forgery verifies to `nil` rather
|
|
13
|
+
# than raising.
|
|
14
|
+
class SignedToken
|
|
15
|
+
COOKIE_NAME = "_tr_cid"
|
|
16
|
+
|
|
17
|
+
# Verified token contents. `session_started_at` is a Time (UTC);
|
|
18
|
+
# `session_id`/`client_id` are strings shaped for GA4 payloads.
|
|
19
|
+
Token = Struct.new(:client_id, :session_id, :session_started_at, :last_seen_at, keyword_init: true)
|
|
20
|
+
|
|
21
|
+
# Generate a signed cookie value embedding the client_id and
|
|
22
|
+
# optional session fields.
|
|
23
|
+
#
|
|
24
|
+
# @param secret [String] HMAC signing secret
|
|
25
|
+
# @param client_id [String] GA4-shaped client id to embed
|
|
26
|
+
# @param session_id [String, nil] GA4 session id
|
|
27
|
+
# @param session_started_at [Time, nil] start of the embedded session
|
|
28
|
+
# @param ttl [ActiveSupport::Duration, nil] validity window;
|
|
29
|
+
# `nil` mints a non-expiring token
|
|
30
|
+
# @return [String] opaque signed cookie value
|
|
31
|
+
def self.mint(secret:, client_id:, session_id: nil, session_started_at: nil, last_seen_at: nil, ttl: nil)
|
|
32
|
+
payload = {
|
|
33
|
+
"cid" => client_id,
|
|
34
|
+
"sid" => session_id,
|
|
35
|
+
"sst" => session_started_at&.to_i,
|
|
36
|
+
"lsa" => last_seen_at&.to_i
|
|
37
|
+
}.compact
|
|
38
|
+
verifier(secret).generate(payload, expires_in: ttl)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Read and verify the signed cookie into a {Token}.
|
|
42
|
+
#
|
|
43
|
+
# @param request [#cookies, nil] any object exposing
|
|
44
|
+
# `request.cookies[COOKIE_NAME]`
|
|
45
|
+
# @param secret [String] HMAC signing secret
|
|
46
|
+
# @return [Token, nil] verified token contents, or `nil` when the
|
|
47
|
+
# cookie is missing or unverifiable.
|
|
48
|
+
def self.read(request, secret:)
|
|
49
|
+
value = request&.cookies&.[](COOKIE_NAME)
|
|
50
|
+
return nil if value.nil? || value.empty?
|
|
51
|
+
|
|
52
|
+
payload = verifier(secret).verified(value)
|
|
53
|
+
return nil unless payload
|
|
54
|
+
|
|
55
|
+
Token.new(
|
|
56
|
+
client_id: payload["cid"],
|
|
57
|
+
session_id: payload["sid"],
|
|
58
|
+
session_started_at: payload["sst"] && Time.at(payload["sst"]).utc,
|
|
59
|
+
last_seen_at: payload["lsa"] && Time.at(payload["lsa"]).utc
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Convenience for gate/client-id use: just the verified client_id.
|
|
64
|
+
#
|
|
65
|
+
# @param request [#cookies, nil] any object exposing
|
|
66
|
+
# `request.cookies[COOKIE_NAME]`
|
|
67
|
+
# @param secret [String] HMAC signing secret
|
|
68
|
+
# @return [String, nil] the embedded client_id, or `nil` when the
|
|
69
|
+
# cookie is missing or unverifiable.
|
|
70
|
+
def self.from_request(request, secret:)
|
|
71
|
+
read(request, secret: secret)&.client_id
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.verifier(secret)
|
|
75
|
+
ActiveSupport::MessageVerifier.new(secret, digest: "SHA256", serializer: JSON)
|
|
76
|
+
end
|
|
77
|
+
private_class_method :verifier
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -85,7 +85,9 @@ module TrackRelay
|
|
|
85
85
|
:ga4_use_eu_endpoint,
|
|
86
86
|
:ga4_require_browser_client_id,
|
|
87
87
|
:ga4_enrich_page_context,
|
|
88
|
-
:track_gate
|
|
88
|
+
:track_gate,
|
|
89
|
+
:signed_client_token_secret,
|
|
90
|
+
:signed_client_token_ttl
|
|
89
91
|
|
|
90
92
|
# @return [Array] registered subscriber instances, in insertion order
|
|
91
93
|
attr_reader :subscribers
|
|
@@ -112,6 +114,8 @@ module TrackRelay
|
|
|
112
114
|
@ga4_require_browser_client_id = false
|
|
113
115
|
@ga4_enrich_page_context = false
|
|
114
116
|
@track_gate = nil
|
|
117
|
+
@signed_client_token_secret = nil
|
|
118
|
+
@signed_client_token_ttl = 30.days
|
|
115
119
|
end
|
|
116
120
|
|
|
117
121
|
# Append a subscriber to the registry.
|
data/lib/track_relay/errors.rb
CHANGED
|
@@ -32,6 +32,11 @@ module TrackRelay
|
|
|
32
32
|
# the catalog and untyped events are disabled.
|
|
33
33
|
class UnknownEventError < Error; end
|
|
34
34
|
|
|
35
|
+
# Raised when a feature is used without its required configuration —
|
|
36
|
+
# e.g. mounting {HandshakeEndpoint} without setting
|
|
37
|
+
# `config.signed_client_token_secret`.
|
|
38
|
+
class ConfigurationError < Error; end
|
|
39
|
+
|
|
35
40
|
# Raised by a subscriber's `#deliver` to signal a *transient* failure
|
|
36
41
|
# that the {DeliveryJob} should retry via ActiveJob's `retry_on`.
|
|
37
42
|
# Examples: HTTP 5xx response, `Net::OpenTimeout`, `Errno::ECONNREFUSED`,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module TrackRelay
|
|
6
|
+
# Routable Rack endpoint that mints the first-party signed analytics
|
|
7
|
+
# token ({ClientId::SignedToken}). Hosts mount it directly:
|
|
8
|
+
#
|
|
9
|
+
# post "/analytics/handshakes", to: TrackRelay::HandshakeEndpoint
|
|
10
|
+
#
|
|
11
|
+
# A small host-side JS module calls it from the browser; because only
|
|
12
|
+
# JS-executing clients ever do, possession of a valid token is
|
|
13
|
+
# proof-of-browser for the host's `track_gate`.
|
|
14
|
+
class HandshakeEndpoint
|
|
15
|
+
# GA4 semantics: a session ends after this much inactivity, however
|
|
16
|
+
# long it has been running. Activity = any handshake call.
|
|
17
|
+
SESSION_INACTIVITY_TIMEOUT = 30 * 60
|
|
18
|
+
|
|
19
|
+
def self.call(env)
|
|
20
|
+
new.call(env)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call(env)
|
|
24
|
+
request = ActionDispatch::Request.new(env)
|
|
25
|
+
config = TrackRelay.config
|
|
26
|
+
now = Time.now.utc
|
|
27
|
+
|
|
28
|
+
if config.signed_client_token_secret.nil? || config.signed_client_token_secret.empty?
|
|
29
|
+
raise ConfigurationError,
|
|
30
|
+
"TrackRelay::HandshakeEndpoint requires config.signed_client_token_secret to be set"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
existing = ClientId::SignedToken.read(request, secret: config.signed_client_token_secret)
|
|
34
|
+
session_alive = existing && !session_stale?(existing, now)
|
|
35
|
+
|
|
36
|
+
cookie_value = ClientId::SignedToken.mint(
|
|
37
|
+
secret: config.signed_client_token_secret,
|
|
38
|
+
client_id: existing&.client_id || fresh_client_id(now),
|
|
39
|
+
session_id: session_alive ? existing.session_id : now.to_i.to_s,
|
|
40
|
+
session_started_at: session_alive ? existing.session_started_at : now,
|
|
41
|
+
last_seen_at: now,
|
|
42
|
+
ttl: config.signed_client_token_ttl
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
[204, {"set-cookie" => set_cookie(cookie_value, request, now, config)}, []]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# A session with no recorded activity falls back to its start time,
|
|
51
|
+
# so pre-`last_seen_at` tokens still rotate sensibly.
|
|
52
|
+
def session_stale?(token, now)
|
|
53
|
+
reference = token.last_seen_at || token.session_started_at
|
|
54
|
+
reference.nil? || (now - reference) > SESSION_INACTIVITY_TIMEOUT
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# GA4-shaped client_id: `<random_int31>.<unix_ts>`.
|
|
58
|
+
def fresh_client_id(now)
|
|
59
|
+
"#{SecureRandom.random_number(0x7FFFFFFF)}.#{now.to_i}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def set_cookie(value, request, now, config)
|
|
63
|
+
attributes = {
|
|
64
|
+
value: value,
|
|
65
|
+
path: "/",
|
|
66
|
+
httponly: true,
|
|
67
|
+
same_site: :lax,
|
|
68
|
+
secure: request.ssl?
|
|
69
|
+
}
|
|
70
|
+
attributes[:expires] = now + config.signed_client_token_ttl if config.signed_client_token_ttl
|
|
71
|
+
|
|
72
|
+
Rack::Utils.set_cookie_header(ClientId::SignedToken::COOKIE_NAME, attributes)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module TrackRelay
|
|
6
|
+
# Opt-in automatic server-side `page_view` emission.
|
|
7
|
+
#
|
|
8
|
+
# Hosts that deliver GA4 purely via the Measurement Protocol (no
|
|
9
|
+
# client-side gtag) still need standard `page_view` events for GA4's
|
|
10
|
+
# page and engagement reports. Include this concern and call the
|
|
11
|
+
# macro:
|
|
12
|
+
#
|
|
13
|
+
# class ApplicationController < ActionController::Base
|
|
14
|
+
# include TrackRelay::PageViewTracking
|
|
15
|
+
# track_page_views
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# Events go through the normal {TrackRelay.track} path, so the host's
|
|
19
|
+
# `track_gate`, subscribers, and GA4 page-context enrichment apply
|
|
20
|
+
# unchanged.
|
|
21
|
+
#
|
|
22
|
+
# ## The builtin `page_view` catalog entry
|
|
23
|
+
#
|
|
24
|
+
# `page_view` sits in {TrackRelay::GA4_RESERVED_NAMES}, which guards
|
|
25
|
+
# *custom* catalog events from shadowing gtag's auto-collected ones.
|
|
26
|
+
# A server-side host has no auto-collection — the MP-standard
|
|
27
|
+
# `page_view` is exactly what it must send — so the concern registers
|
|
28
|
+
# the definition directly with the catalog, bypassing the DSL's
|
|
29
|
+
# reserved-name validation on purpose.
|
|
30
|
+
module PageViewTracking
|
|
31
|
+
extend ActiveSupport::Concern
|
|
32
|
+
|
|
33
|
+
include ControllerTracking
|
|
34
|
+
|
|
35
|
+
# Register the builtin `page_view` {EventDefinition} (no params —
|
|
36
|
+
# page context is attached by GA4 enrichment at delivery time).
|
|
37
|
+
# Idempotent so it can be re-run after test-suite `Catalog.clear!`.
|
|
38
|
+
#
|
|
39
|
+
# @return [void]
|
|
40
|
+
def self.register_builtin_event!
|
|
41
|
+
return if Catalog.defined?(:page_view)
|
|
42
|
+
|
|
43
|
+
Catalog.register(EventDefinition.new(name: :page_view))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
included do
|
|
47
|
+
class_attribute :_track_relay_page_view_condition,
|
|
48
|
+
instance_accessor: false, instance_predicate: false, default: nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class_methods do
|
|
52
|
+
# Enable automatic page_view emission for this controller (and
|
|
53
|
+
# subclasses).
|
|
54
|
+
#
|
|
55
|
+
# @param if [Symbol, Proc, nil] host-side suppression hook —
|
|
56
|
+
# a method name or proc evaluated in the controller instance
|
|
57
|
+
# per request; a falsy result suppresses the page_view.
|
|
58
|
+
# Tracking *policy* (opt-out, geo, bot checks) belongs to the
|
|
59
|
+
# host; this is where it plugs in.
|
|
60
|
+
# @return [void]
|
|
61
|
+
def track_page_views(if: nil)
|
|
62
|
+
self._track_relay_page_view_condition = binding.local_variable_get(:if)
|
|
63
|
+
PageViewTracking.register_builtin_event!
|
|
64
|
+
after_action :_track_relay_emit_page_view
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def _track_relay_emit_page_view
|
|
71
|
+
return unless _track_relay_page_view_response?
|
|
72
|
+
return unless _track_relay_page_view_allowed?
|
|
73
|
+
|
|
74
|
+
PageViewTracking.register_builtin_event!
|
|
75
|
+
track(:page_view)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def _track_relay_page_view_allowed?
|
|
79
|
+
condition = self.class._track_relay_page_view_condition
|
|
80
|
+
case condition
|
|
81
|
+
when nil then true
|
|
82
|
+
when Symbol then !!send(condition)
|
|
83
|
+
else !!instance_exec(&condition)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Exactly one page_view per full HTML page render:
|
|
88
|
+
#
|
|
89
|
+
# - GET only (a POST that re-renders a form is not a page view)
|
|
90
|
+
# - successful (2xx) only — redirects land on a page that fires its
|
|
91
|
+
# own page_view; error pages are noise
|
|
92
|
+
# - `text/html` only (excludes JSON and turbo-stream's
|
|
93
|
+
# `text/vnd.turbo-stream.html`)
|
|
94
|
+
# - not a turbo-frame fetch (partial navigation within a page)
|
|
95
|
+
def _track_relay_page_view_response?
|
|
96
|
+
request.get? &&
|
|
97
|
+
response.successful? &&
|
|
98
|
+
response.media_type == "text/html" &&
|
|
99
|
+
request.headers["Turbo-Frame"].nil?
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -121,12 +121,13 @@ module TrackRelay
|
|
|
121
121
|
return payload unless config.ga4_require_browser_client_id || config.ga4_enrich_page_context
|
|
122
122
|
|
|
123
123
|
request = Current.request
|
|
124
|
-
|
|
124
|
+
token = signed_token(request, config)
|
|
125
|
+
client_id = token&.client_id || ClientId::Ga.from_request(request)
|
|
125
126
|
return nil if config.ga4_require_browser_client_id && client_id.nil?
|
|
126
127
|
return payload if request.nil?
|
|
127
128
|
|
|
128
129
|
hash = payload.to_h
|
|
129
|
-
hash[:params] = hash[:params].merge(page_params(request)) if config.ga4_enrich_page_context
|
|
130
|
+
hash[:params] = hash[:params].merge(page_params(request, token)) if config.ga4_enrich_page_context
|
|
130
131
|
hash[:context] = hash[:context].merge(client_id: client_id) if client_id
|
|
131
132
|
EventPayload.from_h(hash)
|
|
132
133
|
end
|
|
@@ -158,13 +159,25 @@ module TrackRelay
|
|
|
158
159
|
|
|
159
160
|
private
|
|
160
161
|
|
|
162
|
+
# Verified first-party token, when the host has configured a
|
|
163
|
+
# signed-token secret. The token is the preferred (cryptographic)
|
|
164
|
+
# source for both client_id and session_id; the `_ga`/`_ga_<stream>`
|
|
165
|
+
# cookies remain as fallbacks so hosts can migrate incrementally.
|
|
166
|
+
#
|
|
167
|
+
# @return [ClientId::SignedToken::Token, nil]
|
|
168
|
+
def signed_token(request, config)
|
|
169
|
+
return nil unless config.signed_client_token_secret
|
|
170
|
+
|
|
171
|
+
ClientId::SignedToken.read(request, secret: config.signed_client_token_secret)
|
|
172
|
+
end
|
|
173
|
+
|
|
161
174
|
# Page-context params captured from the live request (only when
|
|
162
175
|
# `config.ga4_enrich_page_context` is enabled).
|
|
163
|
-
def page_params(request)
|
|
176
|
+
def page_params(request, token = nil)
|
|
164
177
|
params = {page_location: request.original_url}
|
|
165
178
|
params[:page_referrer] = request.referer if request.referer && !request.referer.empty?
|
|
166
179
|
|
|
167
|
-
if (session_id = ga_session_id(request))
|
|
180
|
+
if (session_id = token&.session_id || ga_session_id(request))
|
|
168
181
|
params[:session_id] = session_id
|
|
169
182
|
params[:engagement_time_msec] = ENGAGEMENT_TIME_MSEC
|
|
170
183
|
end
|
data/lib/track_relay/version.rb
CHANGED
data/lib/track_relay.rb
CHANGED
|
@@ -12,6 +12,8 @@ require "track_relay/current"
|
|
|
12
12
|
require "track_relay/client_id/ga"
|
|
13
13
|
require "track_relay/client_id/ahoy_visitor"
|
|
14
14
|
require "track_relay/client_id/session"
|
|
15
|
+
require "track_relay/client_id/signed_token"
|
|
16
|
+
require "track_relay/handshake_endpoint"
|
|
15
17
|
require "track_relay/configuration"
|
|
16
18
|
require "track_relay/instrumenter"
|
|
17
19
|
require "track_relay/subscribers/base"
|
|
@@ -22,6 +24,7 @@ require "track_relay/subscribers/ahoy"
|
|
|
22
24
|
require "track_relay/delivery_job"
|
|
23
25
|
require "track_relay/dispatcher"
|
|
24
26
|
require "track_relay/controller_tracking"
|
|
27
|
+
require "track_relay/page_view_tracking"
|
|
25
28
|
require "track_relay/job_tracking"
|
|
26
29
|
require "track_relay/linter"
|
|
27
30
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: track_relay
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dchuk
|
|
@@ -193,6 +193,7 @@ files:
|
|
|
193
193
|
- lib/track_relay/client_id/ahoy_visitor.rb
|
|
194
194
|
- lib/track_relay/client_id/ga.rb
|
|
195
195
|
- lib/track_relay/client_id/session.rb
|
|
196
|
+
- lib/track_relay/client_id/signed_token.rb
|
|
196
197
|
- lib/track_relay/configuration.rb
|
|
197
198
|
- lib/track_relay/controller_tracking.rb
|
|
198
199
|
- lib/track_relay/current.rb
|
|
@@ -203,10 +204,12 @@ files:
|
|
|
203
204
|
- lib/track_relay/errors.rb
|
|
204
205
|
- lib/track_relay/event_definition.rb
|
|
205
206
|
- lib/track_relay/event_payload.rb
|
|
207
|
+
- lib/track_relay/handshake_endpoint.rb
|
|
206
208
|
- lib/track_relay/instrumenter.rb
|
|
207
209
|
- lib/track_relay/job_tracking.rb
|
|
208
210
|
- lib/track_relay/linter.rb
|
|
209
211
|
- lib/track_relay/manifest.rb
|
|
212
|
+
- lib/track_relay/page_view_tracking.rb
|
|
210
213
|
- lib/track_relay/railtie.rb
|
|
211
214
|
- lib/track_relay/subscribers/ahoy.rb
|
|
212
215
|
- lib/track_relay/subscribers/base.rb
|