Class: Stealth::Session

Inherits:
Object
  • Object
show all
Includes:
Redis
Defined in:
lib/stealth/session.rb

Constant Summary collapse

SLUG_SEPARATOR =
'->'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id: nil, type: :primary) ⇒ Session

Session types:

- :primary
- :previous
- :back_to


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/stealth/session.rb', line 18

def initialize(id: nil, type: :primary)
  @id = id
  @type = type

  if id.present?
    unless defined?(Stealth::RedisSupport) && Stealth::RedisSupport.connection_pool.present?
      raise(
        Stealth::Errors::RedisNotConfigured,
        "Please make sure STEALTH_REDIS_URL or REDIS_URL is configured before using sessions"
      )
    end

    get_session
  end

  self
end

Instance Attribute Details

#flowObject (readonly)

Returns the value of attribute flow.



11
12
13
# File 'lib/stealth/session.rb', line 11

def flow
  @flow
end

#idObject (readonly)

Returns the value of attribute id.



11
12
13
# File 'lib/stealth/session.rb', line 11

def id
  @id
end

#sessionObject

Returns the value of attribute session.



12
13
14
# File 'lib/stealth/session.rb', line 12

def session
  @session
end

#stateObject (readonly)

Returns the value of attribute state.



11
12
13
# File 'lib/stealth/session.rb', line 11

def state
  @state
end

#typeObject (readonly)

Returns the value of attribute type.



11
12
13
# File 'lib/stealth/session.rb', line 11

def type
  @type
end

Class Method Details

.canonical_session_slug(flow:, state:) ⇒ Object



141
142
143
# File 'lib/stealth/session.rb', line 141

def self.canonical_session_slug(flow:, state:)
  [flow, state].join(SLUG_SEPARATOR)
end

.flow_and_state_from_session_slug(slug:) ⇒ Object



36
37
38
39
40
41
# File 'lib/stealth/session.rb', line 36

def self.flow_and_state_from_session_slug(slug:)
  {
    flow: slug&.split(SLUG_SEPARATOR)&.first,
    state: slug&.split(SLUG_SEPARATOR)&.last
  }
end

.is_a_session_string?(string) ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
139
# File 'lib/stealth/session.rb', line 136

def self.is_a_session_string?(string)
  session_regex = /(.+)(#{SLUG_SEPARATOR})(.+)/
  !!string.match(session_regex)
end

.slugify(flow:, state:) ⇒ Object



43
44
45
46
47
48
49
# File 'lib/stealth/session.rb', line 43

def self.slugify(flow:, state:)
  unless flow.present? && state.present?
    raise(ArgumentError, 'A flow and state must be specified.')
  end

  [flow, state].join(SLUG_SEPARATOR)
end

Instance Method Details

#+(steps) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/stealth/session.rb', line 105

def +(steps)
  return nil if flow.blank?
  return self if steps.zero?

  new_state = self.state + steps
  new_session = Stealth::Session.new(id: self.id)
  new_session.session = self.class.canonical_session_slug(
    flow: self.flow_string,
    state: new_state
  )

  new_session
end

#-(steps) ⇒ Object



119
120
121
122
123
124
125
126
127
# File 'lib/stealth/session.rb', line 119

def -(steps)
  return nil if flow.blank?

  if steps < 0
    return self + steps.abs
  else
    return self + (-steps)
  end
end

#==(other_session) ⇒ Object



129
130
131
132
133
134
# File 'lib/stealth/session.rb', line 129

def ==(other_session)
  self.flow_string == other_session.flow_string &&
    self.state_string == other_session.state_string &&
    self.type == other_session.type &&
    self.id == other_session.id
end

#back_to_session?Boolean

Returns:

  • (Boolean)


164
165
166
# File 'lib/stealth/session.rb', line 164

def back_to_session?
  type == :back_to
end

#blank?Boolean

Returns:

  • (Boolean)


101
102
103
# File 'lib/stealth/session.rb', line 101

def blank?
  !present?
end

#clear_sessionObject



93
94
95
# File 'lib/stealth/session.rb', line 93

def clear_session
  Stealth::RedisSupport.with { |r| r.del(session_key) }
end

#flow_stringObject



61
62
63
# File 'lib/stealth/session.rb', line 61

def flow_string
  session&.split(SLUG_SEPARATOR)&.first
end

#get_sessionObject



69
70
71
# File 'lib/stealth/session.rb', line 69

def get_session
  @session ||= get_key(session_key)
end

#present?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/stealth/session.rb', line 97

def present?
  session.present?
end

#previous_session?Boolean

Returns:

  • (Boolean)


160
161
162
# File 'lib/stealth/session.rb', line 160

def previous_session?
  type == :previous
end

#primary_session?Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/stealth/session.rb', line 156

def primary_session?
  type == :primary
end

#session_keyObject



145
146
147
148
149
150
151
152
153
154
# File 'lib/stealth/session.rb', line 145

def session_key
  case type
  when :primary
    id
  when :previous
    previous_session_key
  when :back_to
    back_to_key
  end
end

#set_session(new_flow:, new_state:) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/stealth/session.rb', line 73

def set_session(new_flow:, new_state:)
  @flow = nil # override @flow's memoization
  existing_session = session # tmp backup for previous_session storage
  @session = self.class.canonical_session_slug(
    flow: new_flow,
    state: new_state
  )

  Stealth::Logger.l(
    topic: [type, 'session'].join('_'),
    message: "User #{id}: setting session to #{new_flow}->#{new_state}"
  )

  if primary_session?
    store_current_to_previous(existing_session: existing_session)
  end

  persist_key(key: session_key, value: session)
end

#state_stringObject



65
66
67
# File 'lib/stealth/session.rb', line 65

def state_string
  session&.split(SLUG_SEPARATOR)&.last
end

#to_sObject



168
169
170
# File 'lib/stealth/session.rb', line 168

def to_s
  [flow_string, state_string].join(SLUG_SEPARATOR)
end