Class: Lucerna::Identity

Inherits:
Object
  • Object
show all
Defined in:
lib/lucerna/identity.rb,
lib/lucerna/identity/client.rb

Overview

The shared "who is the user" primitive — one immutable value, built once per request, that feeds both products: gates reads take it positionally, Lucerna.identity.identify sends it to People. Mirrors GatesIdentity / identity.current() in the JS SDKs.

PII rules: user_id is a pseudonymous app id ("u_42"), never an email. email and name are first-class fields — never traits — and are only ever transmitted by identify, which routes them to People's encrypted columns. Traits are plaintext targeting data.

Defined Under Namespace

Classes: Client

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user_id:, email: nil, name: nil, traits: {}) ⇒ Identity

Returns a new instance of Identity.



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/lucerna/identity.rb', line 18

def initialize(user_id:, email: nil, name: nil, traits: {})
  unless user_id.is_a?(String) && !user_id.empty?
    raise ArgumentError, "user_id must be a non-empty String (got #{user_id.inspect})"
  end

  @user_id = -user_id
  @email = email&.to_s
  @name = name&.to_s
  @traits = Traits.normalize!(traits).freeze
  freeze
end

Instance Attribute Details

#emailObject (readonly)

Returns the value of attribute email.



16
17
18
# File 'lib/lucerna/identity.rb', line 16

def email
  @email
end

#nameObject (readonly)

Returns the value of attribute name.



16
17
18
# File 'lib/lucerna/identity.rb', line 16

def name
  @name
end

#traitsObject (readonly)

Returns the value of attribute traits.



16
17
18
# File 'lib/lucerna/identity.rb', line 16

def traits
  @traits
end

#user_idObject (readonly)

Returns the value of attribute user_id.



16
17
18
# File 'lib/lucerna/identity.rb', line 16

def user_id
  @user_id
end

Class Method Details

.wrap(value) ⇒ Object

Coerces the accepted identity forms — Identity, Hash, nil — into an Identity (or nil for anonymous). Strict: bad input is a programming error. Gates reads have their own lenient path and never raise.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/lucerna/identity.rb', line 33

def self.wrap(value)
  case value
  when nil then nil
  when Identity then value
  when Hash
    new(
      user_id: value[:user_id] || value["user_id"],
      email: value[:email] || value["email"],
      name: value[:name] || value["name"],
      traits: value[:traits] || value["traits"] || {},
    )
  else
    raise ArgumentError, "expected a Lucerna::Identity, Hash, or nil (got #{value.class})"
  end
end

Instance Method Details

#for_gatesObject

Projection for gates evaluation: user id + traits, with the first-class email merged in as a trait fallback so the engine's email→domain derivation makes domain overrides work. Purely local — gates evaluation transmits nothing; only identify sends PII.



53
54
55
56
57
# File 'lib/lucerna/identity.rb', line 53

def for_gates
  traits = @traits
  traits = traits.merge("email" => @email) if @email && !traits.key?("email")
  { user_id: @user_id, traits: traits }
end

#to_payloadObject

Wire payload for POST /sdk/v1/people/identify. Note the rename: local user_id -> wire appUserId. email/name keys are omitted entirely when absent; traits are always present.



62
63
64
65
66
67
68
# File 'lib/lucerna/identity.rb', line 62

def to_payload
  payload = { "appUserId" => @user_id }
  payload["email"] = @email if @email
  payload["name"] = @name if @name
  payload["traits"] = @traits
  payload
end