Class: WhereIsWaldo::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/where_is_waldo/configuration.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/where_is_waldo/configuration.rb', line 87

def initialize
  # Storage defaults
  @adapter = :database
  @table_name = "presences"
  @redis_client = nil
  @redis_prefix = "where_is_waldo"

  # Column defaults
  @session_column = :session_id
  @subject_column = :subject_id

  # Subject model (required)
  @subject_class = nil
  @subject_data_proc = nil

  # Timing defaults
  @timeout = 90
  @heartbeat_interval = 30

  # ActionCable defaults
  @channel_name = "WhereIsWaldo::PresenceChannel"
  @authenticate_proc = nil

  # Broadcastable default audience (set per app)
  @broadcast_audience = nil

  # Presence roster (set per app to enable)
  @roster_org = nil
  @roster_members = nil
  @roster_members_association = nil
  @roster_visible_to = nil
  @roster_viewers_of = nil
  @roster_mode = :poll
  @roster_poll_interval = 15
  @roster_cache_ttl = 90
  @roster_nudge_jitter = 0.5
end

Instance Attribute Details

#adapterObject

Storage settings :adapter - :database or :redis :table_name - defaults to 'presences' :redis_client - custom Redis instance (optional) :redis_prefix - key prefix for Redis (for multi-app setups)



10
11
12
# File 'lib/where_is_waldo/configuration.rb', line 10

def adapter
  @adapter
end

#authenticate_procObject

ActionCable settings :channel_name - defaults to 'WhereIsWaldo::PresenceChannel' :authenticate_proc - proc to authenticate connection, receives request



33
34
35
# File 'lib/where_is_waldo/configuration.rb', line 33

def authenticate_proc
  @authenticate_proc
end

#broadcast_audienceObject

Default audience resolver for the Broadcastable concern. A lambda that, given a record, returns the AR scope to broadcast to (e.g. that record's account members). Set once per app to match its container, e.g.:

config.broadcast_audience = ->(rec) { rec..users }

Models may override per-model via broadcasts_realtime(scope: ...).



40
41
42
# File 'lib/where_is_waldo/configuration.rb', line 40

def broadcast_audience
  @broadcast_audience
end

#channel_nameObject

ActionCable settings :channel_name - defaults to 'WhereIsWaldo::PresenceChannel' :authenticate_proc - proc to authenticate connection, receives request



33
34
35
# File 'lib/where_is_waldo/configuration.rb', line 33

def channel_name
  @channel_name
end

#heartbeat_intervalObject

Timing :timeout - seconds until considered offline (default: 90) :heartbeat_interval - expected heartbeat frequency (default: 30)



28
29
30
# File 'lib/where_is_waldo/configuration.rb', line 28

def heartbeat_interval
  @heartbeat_interval
end

#redis_clientObject

Storage settings :adapter - :database or :redis :table_name - defaults to 'presences' :redis_client - custom Redis instance (optional) :redis_prefix - key prefix for Redis (for multi-app setups)



10
11
12
# File 'lib/where_is_waldo/configuration.rb', line 10

def redis_client
  @redis_client
end

#redis_prefixObject

Storage settings :adapter - :database or :redis :table_name - defaults to 'presences' :redis_client - custom Redis instance (optional) :redis_prefix - key prefix for Redis (for multi-app setups)



10
11
12
# File 'lib/where_is_waldo/configuration.rb', line 10

def redis_prefix
  @redis_prefix
end

#roster_cache_ttlObject

Tuning (pull/nudge). roster_nudge_jitter (seconds) spreads clients' re-poll after a nudge so a change doesn't stampede every viewer at once.



85
86
87
# File 'lib/where_is_waldo/configuration.rb', line 85

def roster_cache_ttl
  @roster_cache_ttl
end

#roster_membersObject

Live presence roster ("who's around in my org/account") ===

roster_org: given a subject, return the org/container record that defines the roster boundary. Its class + id key the single shared roster ActionCable stream, so every member of the same org subscribes to ONE stream and a presence change is a single O(1) broadcast (not per-member fan-out). Required to enable the roster feature — nil leaves it inert. config.roster_org = ->(subject) { subject.account }

roster_members: given that org, return the AR scope of member subjects shown in the roster. Optional — defaults to org.public_send() inferred from subject_class (User => :users). Provide it to scope the list, e.g. only active members: config.roster_members = ->(org) { org.users.active }

roster_members_association: association used to build the default roster from the org when roster_members is unset (defaults to the pluralized subject_class, e.g. :users).



60
61
62
# File 'lib/where_is_waldo/configuration.rb', line 60

def roster_members
  @roster_members
end

#roster_members_associationObject

Live presence roster ("who's around in my org/account") ===

roster_org: given a subject, return the org/container record that defines the roster boundary. Its class + id key the single shared roster ActionCable stream, so every member of the same org subscribes to ONE stream and a presence change is a single O(1) broadcast (not per-member fan-out). Required to enable the roster feature — nil leaves it inert. config.roster_org = ->(subject) { subject.account }

roster_members: given that org, return the AR scope of member subjects shown in the roster. Optional — defaults to org.public_send() inferred from subject_class (User => :users). Provide it to scope the list, e.g. only active members: config.roster_members = ->(org) { org.users.active }

roster_members_association: association used to build the default roster from the org when roster_members is unset (defaults to the pluralized subject_class, e.g. :users).



60
61
62
# File 'lib/where_is_waldo/configuration.rb', line 60

def roster_members_association
  @roster_members_association
end

#roster_modeObject

roster_mode: delivery strategy, per account. A symbol, or a callable resolving an account -> mode. MUST be a function of the account (uniform for all its members). Default :poll (safe: server-side filtered). :poll - heartbeat/poll, server-filtered, ~interval latency :broadcast - instant shared-stream push, NO filtering (open account) :nudge - :poll + content-free trigger (Phase 2) :fanout - per-viewer push (Phase 3) config.roster_mode = ->(account) { account.everyone_admin? ? :broadcast : :poll }



81
82
83
# File 'lib/where_is_waldo/configuration.rb', line 81

def roster_mode
  @roster_mode
end

#roster_nudge_jitterObject

Tuning (pull/nudge). roster_nudge_jitter (seconds) spreads clients' re-poll after a nudge so a change doesn't stampede every viewer at once.



85
86
87
# File 'lib/where_is_waldo/configuration.rb', line 85

def roster_nudge_jitter
  @roster_nudge_jitter
end

#roster_orgObject

Live presence roster ("who's around in my org/account") ===

roster_org: given a subject, return the org/container record that defines the roster boundary. Its class + id key the single shared roster ActionCable stream, so every member of the same org subscribes to ONE stream and a presence change is a single O(1) broadcast (not per-member fan-out). Required to enable the roster feature — nil leaves it inert. config.roster_org = ->(subject) { subject.account }

roster_members: given that org, return the AR scope of member subjects shown in the roster. Optional — defaults to org.public_send() inferred from subject_class (User => :users). Provide it to scope the list, e.g. only active members: config.roster_members = ->(org) { org.users.active }

roster_members_association: association used to build the default roster from the org when roster_members is unset (defaults to the pluralized subject_class, e.g. :users).



60
61
62
# File 'lib/where_is_waldo/configuration.rb', line 60

def roster_org
  @roster_org
end

#roster_poll_intervalObject

Tuning (pull/nudge). roster_nudge_jitter (seconds) spreads clients' re-poll after a nudge so a change doesn't stampede every viewer at once.



85
86
87
# File 'lib/where_is_waldo/configuration.rb', line 85

def roster_poll_interval
  @roster_poll_interval
end

#roster_viewers_ofObject

roster_visible_to: given a VIEWER, return the AR scope of subjects that viewer may see (for :poll/:nudge snapshots + diffs). Handles any visibility rule server-side. Defaults to the viewer's whole org roster (everyone-sees-everyone) when unset. config.roster_visible_to = ->(viewer) { viewer.visible_users }

roster_viewers_of: given a SUBJECT, return the AR scope of viewers allowed to see it (ONLY used by :fanout, Phase 3). Must be the exact inverse of roster_visible_to.



71
72
73
# File 'lib/where_is_waldo/configuration.rb', line 71

def roster_viewers_of
  @roster_viewers_of
end

#roster_visible_toObject

roster_visible_to: given a VIEWER, return the AR scope of subjects that viewer may see (for :poll/:nudge snapshots + diffs). Handles any visibility rule server-side. Defaults to the viewer's whole org roster (everyone-sees-everyone) when unset. config.roster_visible_to = ->(viewer) { viewer.visible_users }

roster_viewers_of: given a SUBJECT, return the AR scope of viewers allowed to see it (ONLY used by :fanout, Phase 3). Must be the exact inverse of roster_visible_to.



71
72
73
# File 'lib/where_is_waldo/configuration.rb', line 71

def roster_visible_to
  @roster_visible_to
end

#session_columnObject

Column names - fully configurable :session_column - unique identifier per connection/tab (e.g., :session_id, :jti) :subject_column - who is present (e.g., :user_id, :member_id, :student_id)



15
16
17
# File 'lib/where_is_waldo/configuration.rb', line 15

def session_column
  @session_column
end

#subject_classObject

Subject model (e.g., 'User', 'Member', 'Student') Required for scope-based broadcasting and queries



19
20
21
# File 'lib/where_is_waldo/configuration.rb', line 19

def subject_class
  @subject_class
end

#subject_columnObject

Column names - fully configurable :session_column - unique identifier per connection/tab (e.g., :session_id, :jti) :subject_column - who is present (e.g., :user_id, :member_id, :student_id)



15
16
17
# File 'lib/where_is_waldo/configuration.rb', line 15

def subject_column
  @subject_column
end

#subject_data_procObject

Optional: proc that returns hash of subject info for presence data Called with the subject record to build presence hash



23
24
25
# File 'lib/where_is_waldo/configuration.rb', line 23

def subject_data_proc
  @subject_data_proc
end

#table_nameObject

Storage settings :adapter - :database or :redis :table_name - defaults to 'presences' :redis_client - custom Redis instance (optional) :redis_prefix - key prefix for Redis (for multi-app setups)



10
11
12
# File 'lib/where_is_waldo/configuration.rb', line 10

def table_name
  @table_name
end

#timeoutObject

Timing :timeout - seconds until considered offline (default: 90) :heartbeat_interval - expected heartbeat frequency (default: 30)



28
29
30
# File 'lib/where_is_waldo/configuration.rb', line 28

def timeout
  @timeout
end

Instance Method Details

#build_subject_data(subject) ⇒ Object

Build subject data hash from a subject record



197
198
199
200
201
202
203
204
205
206
207
# File 'lib/where_is_waldo/configuration.rb', line 197

def build_subject_data(subject)
  return {} unless subject

  if subject_data_proc
    subject_data_proc.call(subject)
  elsif subject.respond_to?(:id)
    { id: subject.id }
  else
    {}
  end
end

#members_associationObject

Association name used to derive the default roster from an org when roster_members is not configured (e.g. subject_class "User" => :users).



146
147
148
149
150
151
# File 'lib/where_is_waldo/configuration.rb', line 146

def members_association
  return roster_members_association if roster_members_association
  return nil if subject_class.blank?

  subject_class.to_s.demodulize.underscore.pluralize.to_sym
end

#resolve_members(org) ⇒ Object

Resolve the AR scope of member subjects for an org (nil if not resolvable).



154
155
156
157
158
159
160
161
162
# File 'lib/where_is_waldo/configuration.rb', line 154

def resolve_members(org)
  return nil unless org

  if roster_members
    roster_members.call(org)
  elsif (assoc = members_association) && org.respond_to?(assoc)
    org.public_send(assoc)
  end
end

#resolve_mode(account) ⇒ Object

Resolve the delivery mode for an account. Callable roster_mode is invoked with the account; a bare symbol is returned as-is. Defaults to :poll.



190
191
192
193
194
# File 'lib/where_is_waldo/configuration.rb', line 190

def resolve_mode()
  mode = roster_mode
  mode = mode.call() if mode.respond_to?(:call)
  (mode || :poll).to_sym
end

#resolve_roster_org(subject) ⇒ Object

Resolve the org/container record for a subject (nil if unset/absent).



138
139
140
141
142
# File 'lib/where_is_waldo/configuration.rb', line 138

def resolve_roster_org(subject)
  return nil unless roster_org && subject

  roster_org.call(subject)
end

#resolve_viewers_of(subject) ⇒ Object

Resolve the AR scope of viewers allowed to see a SUBJECT (:fanout only).



182
183
184
185
186
# File 'lib/where_is_waldo/configuration.rb', line 182

def resolve_viewers_of(subject)
  return nil unless roster_viewers_of && subject

  roster_viewers_of.call(subject)
end

#resolve_visible_to(viewer) ⇒ Object

Resolve the AR scope of subjects a VIEWER may see. Defaults to the viewer's whole org roster (everyone-sees-everyone) when unset.



171
172
173
174
175
176
177
178
179
# File 'lib/where_is_waldo/configuration.rb', line 171

def resolve_visible_to(viewer)
  return nil unless viewer

  if roster_visible_to
    roster_visible_to.call(viewer)
  else
    resolve_members(resolve_roster_org(viewer))
  end
end

#roster_enabled?Boolean

True when the live-presence roster feature is configured.

Returns:

  • (Boolean)


165
166
167
# File 'lib/where_is_waldo/configuration.rb', line 165

def roster_enabled?
  !roster_org.nil?
end

#subject_class_constantObject

Helper to get subject class constant



131
132
133
134
135
# File 'lib/where_is_waldo/configuration.rb', line 131

def subject_class_constant
  return nil if subject_class.blank?

  subject_class.is_a?(String) ? subject_class.safe_constantize : subject_class
end

#timeout_durationObject

Helper to get timeout as duration



126
127
128
# File 'lib/where_is_waldo/configuration.rb', line 126

def timeout_duration
  timeout.is_a?(ActiveSupport::Duration) ? timeout : timeout.seconds
end