Class: WhereIsWaldo::Adapters::DatabaseAdapter
- Inherits:
-
BaseAdapter
- Object
- BaseAdapter
- WhereIsWaldo::Adapters::DatabaseAdapter
- Defined in:
- app/services/where_is_waldo/adapters/database_adapter.rb
Instance Method Summary collapse
- #cleanup(timeout: nil) ⇒ Object
- #connect(session_id:, subject_id:, metadata: {}) ⇒ Object
- #disconnect(session_id: nil, subject_id: nil) ⇒ Object
-
#heartbeat(session_id:, subject_id:, tab_visible: true, subject_active: true, last_activity_at: nil, metadata: {}) ⇒ Object
rubocop:disable Metrics/ParameterLists, Layout/LineLength.
- #online_subject_ids(timeout: nil) ⇒ Object
- #session_status(session_id, subject_id) ⇒ Object
- #sessions_for_subject(subject_id) ⇒ Object
- #sessions_for_subjects(subject_ids, timeout: nil) ⇒ Object
Instance Method Details
#cleanup(timeout: nil) ⇒ Object
109 110 111 112 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 109 def cleanup(timeout: nil) threshold = (timeout || default_timeout).seconds.ago Presence.where(last_heartbeat: ...threshold).delete_all end |
#connect(session_id:, subject_id:, metadata: {}) ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 6 def connect(session_id:, subject_id:, metadata: {}) now = Time.current attrs = { session_column => session_id, subject_column => subject_id, connected_at: now, last_heartbeat: now, tab_visible: true, subject_active: true, last_activity: now, metadata: , created_at: now, updated_at: now } # (subject, session) is the unique key — see the install migration # template. Keying on session alone would let a caller-supplied # session_id colliding across two subjects upsert onto each other's # row. # rubocop:disable Rails/SkipsModelValidations -- intentional for performance Presence.upsert(attrs, unique_by: [subject_column, session_column]) # rubocop:enable Rails/SkipsModelValidations true rescue StandardError => e Rails.logger.error "[WhereIsWaldo] Connect failed: #{e.}" false end |
#disconnect(session_id: nil, subject_id: nil) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 35 def disconnect(session_id: nil, subject_id: nil) raise ArgumentError, "disconnect(session_id:) requires subject_id:" if session_id && !subject_id scope = build_lookup_scope(session_id: session_id, subject_id: subject_id) scope.delete_all true rescue ArgumentError raise rescue StandardError => e Rails.logger.error "[WhereIsWaldo] Disconnect failed: #{e.}" false end |
#heartbeat(session_id:, subject_id:, tab_visible: true, subject_active: true, last_activity_at: nil, metadata: {}) ⇒ Object
rubocop:disable Metrics/ParameterLists, Layout/LineLength
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 49 def heartbeat(session_id:, subject_id:, tab_visible: true, subject_active: true, last_activity_at: nil, metadata: {}) # rubocop:enable Metrics/ParameterLists, Layout/LineLength now = Time.current updates = { last_heartbeat: now, tab_visible: tab_visible, subject_active: subject_active, updated_at: now } # Update last_activity: use JS timestamp if provided, otherwise use current time when active if last_activity_at updates[:last_activity] = Time.zone.at(last_activity_at / 1000.0) elsif subject_active updates[:last_activity] = now end updates[:metadata] = if .present? # Scope by both — a caller-supplied session_id shouldn't be able to # heartbeat another subject's row. scope = Presence.where(session_column => session_id, subject_column => subject_id) # rubocop:disable Rails/SkipsModelValidations -- intentional for performance scope.update_all(updates).positive? # rubocop:enable Rails/SkipsModelValidations rescue StandardError => e Rails.logger.error "[WhereIsWaldo] Heartbeat failed: #{e.}" false end |
#online_subject_ids(timeout: nil) ⇒ Object
78 79 80 81 82 83 84 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 78 def online_subject_ids(timeout: nil) threshold = (timeout || default_timeout).seconds.ago Presence.where("last_heartbeat > ?", threshold) .distinct .pluck(subject_column) end |
#session_status(session_id, subject_id) ⇒ Object
103 104 105 106 107 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 103 def session_status(session_id, subject_id) scope = Presence.where(session_column => session_id, subject_column => subject_id) scope = scope.includes(:subject) if config.subject_class_constant scope.first&.as_presence_hash end |
#sessions_for_subject(subject_id) ⇒ Object
86 87 88 89 90 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 86 def sessions_for_subject(subject_id) scope = Presence.where(subject_column => subject_id) scope = scope.includes(:subject) if config.subject_class_constant scope.map(&:as_presence_hash) end |
#sessions_for_subjects(subject_ids, timeout: nil) ⇒ Object
92 93 94 95 96 97 98 99 100 101 |
# File 'app/services/where_is_waldo/adapters/database_adapter.rb', line 92 def sessions_for_subjects(subject_ids, timeout: nil) ids = Array(subject_ids).compact.uniq return {} if ids.empty? threshold = (timeout || default_timeout).seconds.ago scope = Presence.where(subject_column => ids).where("last_heartbeat > ?", threshold) scope = scope.includes(:subject) if config.subject_class_constant scope.group_by { |row| row[subject_column] } .transform_values { |rows| rows.map(&:as_presence_hash) } end |