Class: TrackRelay::EventPayload
- Inherits:
-
Object
- Object
- TrackRelay::EventPayload
- Defined in:
- lib/track_relay/event_payload.rb
Overview
Runtime data for a single event in flight.
An EventPayload pairs a EventDefinition (the schema) with the raw values supplied at the call site (‘TrackRelay.track(:foo, …)`), plus the request-derived context (user, visitor_token, client_id, etc.) and a timestamp. It is the value passed through the `track_relay.event` ActiveSupport::Notifications instrumentation.
Two constructors:
-
‘EventPayload.new(definition:, params:, …)` — typed payload; #validate! coerces and enforces the definition’s schema.
-
‘EventPayload.untyped(name:, params:, …)` — untyped payload (no matching definition); #validate! is a no-op so consumers can still ship the raw event for the untyped-events linter (see Plan 04).
‘validate!` is destructive: it replaces `@params` with the coerced hash so subscribers see post-coercion values. Errors raise ValidationError naming the offending key.
Constant Summary collapse
- BOOLEAN_TRUE_VALUES =
Sentinel values that the strict boolean coercion accepts. Anything else raises ValidationError.
[true, "true", 1].freeze
- BOOLEAN_FALSE_VALUES =
[false, "false", 0].freeze
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#definition ⇒ Object
readonly
Returns the value of attribute definition.
-
#params ⇒ Object
readonly
Returns the value of attribute params.
-
#timestamp ⇒ Object
readonly
Returns the value of attribute timestamp.
Class Method Summary collapse
-
.from_h(hash) ⇒ EventPayload
Reconstruct an EventPayload from a serialized #to_h form.
-
.untyped(name:, params:, context: {}, timestamp: Time.now) ⇒ EventPayload
Build an untyped payload — no definition, no schema enforcement.
Instance Method Summary collapse
-
#initialize(definition:, params:, context: {}, timestamp: Time.now) ⇒ EventPayload
constructor
A new instance of EventPayload.
-
#name ⇒ Symbol
Event name (from definition or untyped store).
-
#to_h ⇒ Hash
Serialize to a Hash suitable for ActiveJob arguments / JSON encoding.
-
#untyped? ⇒ Boolean
Whether this payload was built without a matching catalog definition.
-
#validate! ⇒ Hash{Symbol => Object}
Coerce and validate ‘@params` against `@definition.params`.
Constructor Details
#initialize(definition:, params:, context: {}, timestamp: Time.now) ⇒ EventPayload
Returns a new instance of EventPayload.
39 40 41 42 43 44 45 |
# File 'lib/track_relay/event_payload.rb', line 39 def initialize(definition:, params:, context: {}, timestamp: Time.now) @definition = definition @params = params @context = context @timestamp = @untyped_name = nil end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
32 33 34 |
# File 'lib/track_relay/event_payload.rb', line 32 def context @context end |
#definition ⇒ Object (readonly)
Returns the value of attribute definition.
32 33 34 |
# File 'lib/track_relay/event_payload.rb', line 32 def definition @definition end |
#params ⇒ Object (readonly)
Returns the value of attribute params.
32 33 34 |
# File 'lib/track_relay/event_payload.rb', line 32 def params @params end |
#timestamp ⇒ Object (readonly)
Returns the value of attribute timestamp.
32 33 34 |
# File 'lib/track_relay/event_payload.rb', line 32 def @timestamp end |
Class Method Details
.from_h(hash) ⇒ EventPayload
Reconstruct an EventPayload from a serialized #to_h form. Used by Plan 05’s DeliveryJob to rehydrate a payload after ActiveJob has serialized arguments through the queue adapter.
The reconstructed payload is always untyped (‘definition: nil`): validation happened at track time on the calling thread, so the async delivery path doesn’t need the schema. ActiveJob’s argument serialization round-trips Symbols as Strings under the standard adapter, so ‘String#to_sym` is applied defensively to the name.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/track_relay/event_payload.rb', line 75 def self.from_h(hash) payload = allocate payload.instance_variable_set(:@definition, nil) payload.instance_variable_set(:@params, hash[:params] || hash["params"] || {}) payload.instance_variable_set(:@context, hash[:context] || hash["context"] || {}) payload.instance_variable_set( :@timestamp, Time.iso8601(hash[:timestamp] || hash["timestamp"]) ) payload.instance_variable_set( :@untyped_name, (hash[:name] || hash["name"]).to_sym ) payload end |
.untyped(name:, params:, context: {}, timestamp: Time.now) ⇒ EventPayload
Build an untyped payload — no definition, no schema enforcement. Used when a host application calls ‘TrackRelay.track(:unknown, …)` while `untyped_events_allowed = true`.
57 58 59 60 61 |
# File 'lib/track_relay/event_payload.rb', line 57 def self.untyped(name:, params:, context: {}, timestamp: Time.now) payload = new(definition: nil, params: params, context: context, timestamp: ) payload.instance_variable_set(:@untyped_name, name) payload end |
Instance Method Details
#name ⇒ Symbol
Returns event name (from definition or untyped store).
92 93 94 |
# File 'lib/track_relay/event_payload.rb', line 92 def name @definition ? @definition.name : @untyped_name end |
#to_h ⇒ Hash
Serialize to a Hash suitable for ActiveJob arguments / JSON encoding. Used by the DeliveryJob in Plan 05.
157 158 159 160 161 162 163 164 |
# File 'lib/track_relay/event_payload.rb', line 157 def to_h { name: name, params: @params, context: @context, timestamp: @timestamp.respond_to?(:iso8601) ? @timestamp.iso8601 : @timestamp.to_s } end |
#untyped? ⇒ Boolean
Returns whether this payload was built without a matching catalog definition.
98 99 100 |
# File 'lib/track_relay/event_payload.rb', line 98 def untyped? @definition.nil? end |
#validate! ⇒ Hash{Symbol => Object}
Coerce and validate ‘@params` against `@definition.params`. Mutates `@params` to the coerced hash.
For each schema entry the order of operations is:
-
Apply ‘sanitize` callable if present (raw -> sanitized).
-
Check ‘required` against post-sanitize value.
-
Coerce to ‘type`.
-
Apply ‘max` (length for strings, value for numbers).
-
Apply ‘in` inclusion list.
-
Apply ‘format` regex (strings only).
After per-key processing, any incoming param not declared in the schema raises ValidationError.
No-op for untyped payloads.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/track_relay/event_payload.rb', line 120 def validate! return @params if untyped? coerced = {} @definition.params.each do |key, schema| raw_value = @params[key] raw_value = schema.sanitize.call(raw_value) if schema.sanitize&.respond_to?(:call) && @params.key?(key) if raw_value.nil? if schema.required raise ValidationError, "Param #{key.inspect} is required but was not provided" end next end coerced_value = coerce(key, schema.type, raw_value) check_max!(key, schema.max, coerced_value) if schema.max check_in!(key, schema.in, coerced_value) if schema.in check_format!(key, schema.format, coerced_value) if schema.format coerced[key] = coerced_value end extras = @params.keys - @definition.params.keys unless extras.empty? raise ValidationError, "Unexpected param(s) #{extras.map(&:inspect).join(", ")} not declared on event #{@definition.name.inspect}" end @params = coerced end |