where_is_waldo 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -0
- data/README.md +29 -0
- data/VERSION +1 -1
- data/app/channels/where_is_waldo/presence_channel.rb +13 -2
- data/lib/generators/where_is_waldo/install/install_generator.rb +35 -3
- data/lib/generators/where_is_waldo/install/templates/migration.rb.tt +6 -1
- data/lib/where_is_waldo/apm.rb +73 -0
- data/lib/where_is_waldo/configuration.rb +12 -0
- data/lib/where_is_waldo.rb +1 -0
- metadata +12 -9
- data/lib/generators/where_is_waldo/install/templates/create_presences.rb.tt +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5bd28c86d109c69fc976eaab788972106493a78b3fbd8e8e2370bae5dc936986
|
|
4
|
+
data.tar.gz: fd49e94bf3a08dd4350fdc1b8b413ec5d66916ef1ada8c74162044b3ccbdc428
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 620c8ce6c899937a25ab39f2858e45c5fa6ac68dc3510cf0e47a5739b36a8f95c24bb3daf385974600ffa652ad32f176a79ac0389286520af7a291bc48cda1e7
|
|
7
|
+
data.tar.gz: 0ecf65ed538cbffc439428ed3f5acc690e178430ba62a9568fb1b4a1d4cb48f33303aaa9724979f8c1c0ebfc44da5eb35133665345a2dfc2d4a3a224373784d7
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,93 @@
|
|
|
3
3
|
Notable changes to where_is_waldo. Format loosely follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
## 0.1.11 - 2026-10-27
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **The install generator emitted the pre-0.1.5 schema.** It wrote a unique
|
|
13
|
+
index on the session column alone, while 0.1.5 moved presence rows to being
|
|
14
|
+
keyed by `(subject, session)` precisely so two authenticated subjects who
|
|
15
|
+
collide on a session id cannot upsert onto each other's row — session ids are
|
|
16
|
+
client-supplied in some setups, and a reused JWT `jti` is enough. The adapter
|
|
17
|
+
has scoped every session lookup by both columns since; only the generator
|
|
18
|
+
lagged, so every app installed after 0.1.5 got a schema the library had
|
|
19
|
+
already moved away from and needed a follow-up migration to correct. An
|
|
20
|
+
unused second migration template carrying the right index (and a hardcoded
|
|
21
|
+
`Migration[7.0]`) has been removed rather than left as a second answer.
|
|
22
|
+
|
|
23
|
+
- **The install generator overwrote an existing ActionCable connection.**
|
|
24
|
+
Installing into an app that already authenticates its own cable connection
|
|
25
|
+
replaced that file with the turnkey one, silently dropping whatever the app
|
|
26
|
+
did beyond what this library needs — container scoping and token bookkeeping
|
|
27
|
+
in the cases seen. Existing channel files are now kept, and the post-install
|
|
28
|
+
message names them and states what the channel requires of a connection
|
|
29
|
+
(`current_user` and `session_id`).
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **The Redis adapter is now tested against a real server, on both redis-rb 5
|
|
34
|
+
and 6.** Its existing spec runs against `MockRedis`, a pure-Ruby fake with no
|
|
35
|
+
protocol layer, so it could not see wire-format changes at all. redis-rb 6
|
|
36
|
+
defaults to RESP3, which types several replies differently from RESP2 —
|
|
37
|
+
`SMEMBERS` among them, and this adapter feeds that result straight into
|
|
38
|
+
`filter_map` and `all?`. Nothing in the adapter's command set behaves
|
|
39
|
+
differently between the two, but that was previously unverified rather than
|
|
40
|
+
known.
|
|
41
|
+
|
|
42
|
+
- New integration spec drives the adapter against a live Redis. It skips when
|
|
43
|
+
no server is reachable, so contributors are not required to run one.
|
|
44
|
+
- CI adds an `rspec-redis` job with a Redis service, run as a matrix over
|
|
45
|
+
redis-rb 5.x and 6.x, and fails rather than skips if the service is missing
|
|
46
|
+
— a silent skip would read as a pass.
|
|
47
|
+
- Development dependency widened from `~> 5.0` to `>= 5.0, < 7`. The gem has
|
|
48
|
+
no runtime dependency on `redis` and never has, so this constrains nothing
|
|
49
|
+
for consumers; it only lets the library test what consumers actually use.
|
|
50
|
+
- README documents supported client versions, and that `protocol: 2` is not
|
|
51
|
+
needed.
|
|
52
|
+
|
|
53
|
+
The committed lockfile stays on redis 5.x, which keeps `mock_redis` at
|
|
54
|
+
0.53.0 for every existing spec. `mock_redis` 0.53.0 depends on `redis ~> 5`,
|
|
55
|
+
so resolving against redis 6 forces it down to 0.48.1 — that happens only
|
|
56
|
+
inside the redis 6 CI leg, which runs the real-server spec alone and never
|
|
57
|
+
touches MockRedis. No mock-based spec changed the double it runs against.
|
|
58
|
+
|
|
59
|
+
No runtime code changed.
|
|
60
|
+
|
|
61
|
+
## 0.1.10
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **Presence heartbeats are kept out of APM as their own transactions.**
|
|
66
|
+
`WhereIsWaldo::PresenceChannel#heartbeat` now runs inside
|
|
67
|
+
`WhereIsWaldo::Apm.ignoring_transaction`, so when a host app runs New Relic
|
|
68
|
+
the per-tab-per-interval heartbeat — the highest-volume, least-interesting
|
|
69
|
+
cable action — no longer surfaces as an APM transaction. Left unhandled it
|
|
70
|
+
inflates throughput and flatters average response time (heartbeats are
|
|
71
|
+
uniformly fast) while telling operators nothing the presence store can't
|
|
72
|
+
already answer.
|
|
73
|
+
|
|
74
|
+
- Guarded, no new dependency: `Apm` no-ops unless a supported agent
|
|
75
|
+
(New Relic today) is loaded, so consumers without APM pay nothing.
|
|
76
|
+
- Failures stay visible: an ignored New Relic transaction skips `commit!`,
|
|
77
|
+
where exceptions are normally harvested, so the wrapper hands any heartbeat
|
|
78
|
+
exception straight to the error collector and re-raises. Ignoring the
|
|
79
|
+
routine transaction never hides a heartbeat failure.
|
|
80
|
+
- Opt out with `config.ignore_heartbeat_apm = false` to report heartbeats
|
|
81
|
+
like any other action.
|
|
82
|
+
- Scope: New Relic only. The underlying exposure is agent-general — any APM
|
|
83
|
+
that opens a transaction per `perform_action` sees the same heartbeat
|
|
84
|
+
volume — but New Relic is the only agent seen in production so far.
|
|
85
|
+
Detection is isolated in `Apm.agent` so another vendor can be added there
|
|
86
|
+
without touching callers.
|
|
87
|
+
|
|
88
|
+
Note: the companion New Relic issue — the agent pinning the WebSocket upgrade
|
|
89
|
+
transaction onto pooled ActionCable worker threads — is a general
|
|
90
|
+
newrelic_rpm × ActionCable defect and stays a host-app shim; it is not
|
|
91
|
+
something the gem patches.
|
|
92
|
+
|
|
6
93
|
## 0.1.9
|
|
7
94
|
|
|
8
95
|
Security/maintenance release — dependency bumps only, no library code changes.
|
data/README.md
CHANGED
|
@@ -41,6 +41,13 @@ rails generate where_is_waldo:install --adapter=redis --subject_column=user_id
|
|
|
41
41
|
npm install @byscott-io/where-is-waldo @rails/actioncable
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
Installing into an app that already has `app/channels/application_cable/`?
|
|
45
|
+
Those files are kept, never overwritten — the generator only adds what is
|
|
46
|
+
missing, and says which it left alone. The presence channel needs the
|
|
47
|
+
connection to identify **`current_user`** and **`session_id`**; add them to
|
|
48
|
+
your own connection, or `include WhereIsWaldo::JwtConnection` (corebyscott
|
|
49
|
+
apps) to get both from the JWT.
|
|
50
|
+
|
|
44
51
|
### 2. Configure
|
|
45
52
|
|
|
46
53
|
```ruby
|
|
@@ -113,6 +120,14 @@ WhereIsWaldo.configure do |config|
|
|
|
113
120
|
config.timeout = 90 # seconds until offline
|
|
114
121
|
config.heartbeat_interval = 30
|
|
115
122
|
|
|
123
|
+
# Observability: when true (default) and the host app runs a supported APM
|
|
124
|
+
# agent (New Relic today), presence heartbeats are kept out of APM as their
|
|
125
|
+
# own transactions. Heartbeats are the highest-volume, least-interesting cable
|
|
126
|
+
# action, so reporting each one inflates throughput and flatters average
|
|
127
|
+
# response time without adding signal. Failures are still reported. Set false
|
|
128
|
+
# to report heartbeats like any other action.
|
|
129
|
+
config.ignore_heartbeat_apm = true
|
|
130
|
+
|
|
116
131
|
# Optional: custom subject data in presence hash. NOTE: with the roster
|
|
117
132
|
# enabled, these fields are broadcast to every member of the org — see
|
|
118
133
|
# Security.
|
|
@@ -140,6 +155,20 @@ WhereIsWaldo.configure do |config|
|
|
|
140
155
|
end
|
|
141
156
|
```
|
|
142
157
|
|
|
158
|
+
#### Redis adapter and client versions
|
|
159
|
+
|
|
160
|
+
The gem does not depend on `redis`. Choosing `:redis` means supplying your own
|
|
161
|
+
client, so you pick the version.
|
|
162
|
+
|
|
163
|
+
`redis-rb` 5.x and 6.x are both supported and both run in CI against a real
|
|
164
|
+
server. Version 6 switched to RESP3 by default, which changes reply types for
|
|
165
|
+
some commands; none of the commands this adapter uses behave differently, and
|
|
166
|
+
the integration spec exists to keep that true. Nothing here needs `protocol: 2`.
|
|
167
|
+
|
|
168
|
+
The adapter stores presence under keys prefixed by `config.redis_prefix`
|
|
169
|
+
(default `where_is_waldo`) and sets a TTL on session keys, so it does not need a
|
|
170
|
+
cleanup job the way the database adapter does.
|
|
171
|
+
|
|
143
172
|
### Querying Presence
|
|
144
173
|
|
|
145
174
|
```ruby
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.11
|
|
@@ -32,7 +32,20 @@ module WhereIsWaldo
|
|
|
32
32
|
publish_roster_change
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# Kept out of APM as its own transaction by default — heartbeats are the
|
|
36
|
+
# highest-volume, least-interesting cable action, so reporting each one
|
|
37
|
+
# distorts throughput and average response time without adding signal. The
|
|
38
|
+
# wrapper no-ops unless the host runs a supported agent; see
|
|
39
|
+
# WhereIsWaldo::Apm and config.ignore_heartbeat_apm. The real work lives in
|
|
40
|
+
# perform_heartbeat, which is private so a client can't invoke it directly
|
|
41
|
+
# as a channel action (only public methods are processable actions).
|
|
35
42
|
def heartbeat(data)
|
|
43
|
+
WhereIsWaldo::Apm.ignoring_transaction { perform_heartbeat(data) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def perform_heartbeat(data)
|
|
36
49
|
return if presence_suppressed?
|
|
37
50
|
|
|
38
51
|
data = data.with_indifferent_access
|
|
@@ -58,8 +71,6 @@ module WhereIsWaldo
|
|
|
58
71
|
publish_roster_change
|
|
59
72
|
end
|
|
60
73
|
|
|
61
|
-
private
|
|
62
|
-
|
|
63
74
|
# Memoized per-subscription. Host apps configure suppress_presence_proc
|
|
64
75
|
# to gate WHICH subscriptions register presence — e.g. a support-user
|
|
65
76
|
# impersonation tab is a legitimate WS consumer but shouldn't count as
|
|
@@ -32,9 +32,17 @@ module WhereIsWaldo
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def create_channels
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
# NEVER overwrite an app's existing cable connection or channel base.
|
|
36
|
+
# A host app that already authenticates ActionCable has its auth,
|
|
37
|
+
# container scoping and session identity in those files, and the
|
|
38
|
+
# turnkey versions here provide only what this library needs -- so
|
|
39
|
+
# replacing them silently drops whatever else they did. Installing
|
|
40
|
+
# into an existing app is the common case, not the exception.
|
|
41
|
+
@kept_channels = []
|
|
42
|
+
|
|
43
|
+
copy_channel "connection.rb.tt", "app/channels/application_cable/connection.rb"
|
|
44
|
+
copy_channel "channel.rb.tt", "app/channels/application_cable/channel.rb"
|
|
45
|
+
copy_channel "presence_channel.rb.tt", "app/channels/presence_channel.rb"
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
def show_post_install_message
|
|
@@ -54,10 +62,34 @@ module WhereIsWaldo
|
|
|
54
62
|
say " <App />"
|
|
55
63
|
say " </PresenceProvider>"
|
|
56
64
|
say ""
|
|
65
|
+
report_kept_channels
|
|
57
66
|
end
|
|
58
67
|
|
|
59
68
|
private
|
|
60
69
|
|
|
70
|
+
# Say plainly what was left alone. A silent skip is as confusing as a
|
|
71
|
+
# silent overwrite: the install looks complete and presence then fails
|
|
72
|
+
# because the app's own connection exposes no session identity.
|
|
73
|
+
def report_kept_channels
|
|
74
|
+
return if @kept_channels.blank?
|
|
75
|
+
|
|
76
|
+
say "Kept your existing files (not overwritten):", :yellow
|
|
77
|
+
@kept_channels.each { |f| say " - #{f}" }
|
|
78
|
+
say ""
|
|
79
|
+
say " WhereIsWaldo's channel reads connection.current_user and"
|
|
80
|
+
say " connection.session_id. If your connection does not identify"
|
|
81
|
+
say " both, add them -- or include WhereIsWaldo::JwtConnection."
|
|
82
|
+
say ""
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def copy_channel(source, destination)
|
|
86
|
+
if File.exist?(File.join(destination_root, destination))
|
|
87
|
+
@kept_channels << destination
|
|
88
|
+
else
|
|
89
|
+
template source, destination
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
61
93
|
def migration_version
|
|
62
94
|
"[#{ActiveRecord::VERSION::STRING.to_f}]"
|
|
63
95
|
end
|
|
@@ -14,7 +14,12 @@ class CreatePresences < ActiveRecord::Migration<%= migration_version %>
|
|
|
14
14
|
t.timestamps
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
# (subject, session) must be unique TOGETHER, never session alone. Session
|
|
18
|
+
# ids are not globally unique -- they are client-supplied in some setups,
|
|
19
|
+
# and a reused JWT jti is enough -- so keying on the session alone lets two
|
|
20
|
+
# authenticated subjects upsert onto each other's presence row. This is the
|
|
21
|
+
# keying the adapter uses: every session lookup scopes by both columns.
|
|
22
|
+
add_index :presences, [ :<%= subject_column %>, :<%= session_column %> ], unique: true
|
|
18
23
|
add_index :presences, :last_heartbeat
|
|
19
24
|
end
|
|
20
25
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WhereIsWaldo
|
|
4
|
+
# APM (New Relic) integration seam.
|
|
5
|
+
#
|
|
6
|
+
# The gem takes NO hard dependency on newrelic_rpm. Every method here no-ops
|
|
7
|
+
# unless a supported agent is loaded in the host app, so a consumer without
|
|
8
|
+
# New Relic pays nothing and nothing breaks.
|
|
9
|
+
#
|
|
10
|
+
# Why this exists: ActionCable dispatches each client action through New
|
|
11
|
+
# Relic's ActionCableSubscriber, which starts one APM transaction per
|
|
12
|
+
# `perform_action`. Presence heartbeats are the highest-volume, least
|
|
13
|
+
# interesting action Waldo has — one per open tab per heartbeat interval,
|
|
14
|
+
# uniformly fast — so left alone they dominate transaction throughput and
|
|
15
|
+
# flatten the host app's average response time while telling operators nothing
|
|
16
|
+
# the presence store can't already answer. `ignoring_transaction` keeps them
|
|
17
|
+
# out of APM's headline numbers without hiding failures.
|
|
18
|
+
#
|
|
19
|
+
# (The companion half of this problem — the agent pinning the WebSocket
|
|
20
|
+
# upgrade transaction onto pooled ActionCable worker threads — is a general
|
|
21
|
+
# newrelic_rpm x ActionCable defect, not Waldo's to fix here. Host apps carry
|
|
22
|
+
# that shim until the agent resets pooled-thread state itself.)
|
|
23
|
+
#
|
|
24
|
+
# Scope: this handles New Relic ONLY. The underlying exposure is agent-general
|
|
25
|
+
# — any APM/tracer that opens a transaction per `perform_action` (Datadog,
|
|
26
|
+
# Scout, AppSignal, Skylight, Sentry, OpenTelemetry) sees the same heartbeat
|
|
27
|
+
# flood, and several share the same pooled-thread context-leak. Waldo doesn't
|
|
28
|
+
# cause that (it spawns no threads and patches nothing global); its heartbeat
|
|
29
|
+
# volume amplifies whatever the host's agent already does. Detection is
|
|
30
|
+
# isolated in `agent` so another vendor can be added there without touching
|
|
31
|
+
# callers.
|
|
32
|
+
module Apm
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# The New Relic agent module, or nil when newrelic_rpm isn't loaded (or is
|
|
36
|
+
# too old to expose the API we use). Isolated behind one method so specs can
|
|
37
|
+
# drive both the present and absent paths without defining a global
|
|
38
|
+
# ::NewRelic constant.
|
|
39
|
+
def agent
|
|
40
|
+
return nil unless defined?(::NewRelic::Agent)
|
|
41
|
+
return nil unless ::NewRelic::Agent.respond_to?(:ignore_transaction)
|
|
42
|
+
|
|
43
|
+
::NewRelic::Agent
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Run a unit of work without letting it surface as its own APM transaction,
|
|
47
|
+
# and return the block's value.
|
|
48
|
+
#
|
|
49
|
+
# A plain `yield` when there's no agent, or when the host has opted out via
|
|
50
|
+
# `config.ignore_heartbeat_apm = false` — in either case the transaction is
|
|
51
|
+
# reported normally.
|
|
52
|
+
#
|
|
53
|
+
# New Relic's `Transaction#ignore!` makes `#finish` skip `commit!`, and
|
|
54
|
+
# `commit!` is where transaction-attached exceptions are harvested
|
|
55
|
+
# (`record_exceptions`). So ignoring on its own would ALSO swallow a
|
|
56
|
+
# heartbeat failure. We hand any exception straight to the error collector —
|
|
57
|
+
# which reports immediately rather than at commit — and re-raise, so the
|
|
58
|
+
# caller's own error handling is unchanged.
|
|
59
|
+
def ignoring_transaction
|
|
60
|
+
nr = agent
|
|
61
|
+
return yield unless nr && WhereIsWaldo.config.ignore_heartbeat_apm
|
|
62
|
+
|
|
63
|
+
nr.ignore_transaction
|
|
64
|
+
|
|
65
|
+
begin
|
|
66
|
+
yield
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
nr.instance&.error_collector&.notice_error(e)
|
|
69
|
+
raise
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -27,6 +27,15 @@ module WhereIsWaldo
|
|
|
27
27
|
# :heartbeat_interval - expected heartbeat frequency (default: 30)
|
|
28
28
|
attr_accessor :timeout, :heartbeat_interval
|
|
29
29
|
|
|
30
|
+
# Observability
|
|
31
|
+
# :ignore_heartbeat_apm - when true (default) and a supported APM agent
|
|
32
|
+
# (New Relic) is loaded in the host app, presence heartbeats are kept out
|
|
33
|
+
# of APM as their own transactions. Heartbeats are the highest-volume,
|
|
34
|
+
# least-interesting cable action, so reporting each one distorts
|
|
35
|
+
# throughput and average response time without adding signal. Set false
|
|
36
|
+
# to report them like any other action. See WhereIsWaldo::Apm.
|
|
37
|
+
attr_accessor :ignore_heartbeat_apm
|
|
38
|
+
|
|
30
39
|
# ActionCable settings
|
|
31
40
|
# :channel_name - defaults to 'WhereIsWaldo::PresenceChannel'
|
|
32
41
|
# :authenticate_proc - proc to authenticate connection, receives request
|
|
@@ -120,6 +129,9 @@ module WhereIsWaldo
|
|
|
120
129
|
@timeout = 90
|
|
121
130
|
@heartbeat_interval = 30
|
|
122
131
|
|
|
132
|
+
# Observability defaults
|
|
133
|
+
@ignore_heartbeat_apm = true
|
|
134
|
+
|
|
123
135
|
# ActionCable defaults
|
|
124
136
|
@channel_name = "WhereIsWaldo::PresenceChannel"
|
|
125
137
|
@authenticate_proc = nil
|
data/lib/where_is_waldo.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: where_is_waldo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Scott Gibson
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -84,16 +83,22 @@ dependencies:
|
|
|
84
83
|
name: redis
|
|
85
84
|
requirement: !ruby/object:Gem::Requirement
|
|
86
85
|
requirements:
|
|
87
|
-
- - "
|
|
86
|
+
- - ">="
|
|
88
87
|
- !ruby/object:Gem::Version
|
|
89
88
|
version: '5.0'
|
|
89
|
+
- - "<"
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '7'
|
|
90
92
|
type: :development
|
|
91
93
|
prerelease: false
|
|
92
94
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
95
|
requirements:
|
|
94
|
-
- - "
|
|
96
|
+
- - ">="
|
|
95
97
|
- !ruby/object:Gem::Version
|
|
96
98
|
version: '5.0'
|
|
99
|
+
- - "<"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '7'
|
|
97
102
|
- !ruby/object:Gem::Dependency
|
|
98
103
|
name: rspec-rails
|
|
99
104
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -210,11 +215,11 @@ files:
|
|
|
210
215
|
- lib/generators/where_is_waldo/install/install_generator.rb
|
|
211
216
|
- lib/generators/where_is_waldo/install/templates/channel.rb.tt
|
|
212
217
|
- lib/generators/where_is_waldo/install/templates/connection.rb.tt
|
|
213
|
-
- lib/generators/where_is_waldo/install/templates/create_presences.rb.tt
|
|
214
218
|
- lib/generators/where_is_waldo/install/templates/initializer.rb.tt
|
|
215
219
|
- lib/generators/where_is_waldo/install/templates/migration.rb.tt
|
|
216
220
|
- lib/generators/where_is_waldo/install/templates/presence_channel.rb.tt
|
|
217
221
|
- lib/where_is_waldo.rb
|
|
222
|
+
- lib/where_is_waldo/apm.rb
|
|
218
223
|
- lib/where_is_waldo/configuration.rb
|
|
219
224
|
- lib/where_is_waldo/engine.rb
|
|
220
225
|
- lib/where_is_waldo/version.rb
|
|
@@ -227,7 +232,6 @@ metadata:
|
|
|
227
232
|
changelog_uri: https://github.com/byscott-io/where_is_waldo/blob/master/CHANGELOG.md
|
|
228
233
|
allowed_push_host: https://rubygems.org
|
|
229
234
|
rubygems_mfa_required: 'true'
|
|
230
|
-
post_install_message:
|
|
231
235
|
rdoc_options: []
|
|
232
236
|
require_paths:
|
|
233
237
|
- lib
|
|
@@ -242,8 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
242
246
|
- !ruby/object:Gem::Version
|
|
243
247
|
version: '0'
|
|
244
248
|
requirements: []
|
|
245
|
-
rubygems_version:
|
|
246
|
-
signing_key:
|
|
249
|
+
rubygems_version: 4.0.16
|
|
247
250
|
specification_version: 4
|
|
248
251
|
summary: Real-time presence tracking for Rails + React applications
|
|
249
252
|
test_files: []
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Create<%= table_name.camelize %> < ActiveRecord::Migration[7.0]
|
|
4
|
-
def change
|
|
5
|
-
create_table :<%= table_name %> do |t|
|
|
6
|
-
# Identifiers - column names are configurable
|
|
7
|
-
t.string :<%= session_column %>, null: false # Unique session/connection identifier
|
|
8
|
-
t.bigint :<%= subject_column %>, null: false # Who (user, member, student, etc.)
|
|
9
|
-
|
|
10
|
-
# Timestamps
|
|
11
|
-
t.datetime :connected_at, null: false
|
|
12
|
-
t.datetime :last_heartbeat, null: false
|
|
13
|
-
t.datetime :last_activity
|
|
14
|
-
|
|
15
|
-
# Activity state
|
|
16
|
-
t.boolean :tab_visible, default: true, null: false
|
|
17
|
-
t.boolean :subject_active, default: true, null: false
|
|
18
|
-
|
|
19
|
-
# Extensible metadata
|
|
20
|
-
t.<%= json_column_type %> :metadata
|
|
21
|
-
|
|
22
|
-
t.timestamps
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# (subject, session) must be unique together. Keying by session alone
|
|
26
|
-
# would let two authenticated subjects that happen to collide on a
|
|
27
|
-
# session id (client-supplied values, JWT jti reuse, etc.) upsert onto
|
|
28
|
-
# each other's row.
|
|
29
|
-
add_index :<%= table_name %>, [:<%= subject_column %>, :<%= session_column %>], unique: true
|
|
30
|
-
|
|
31
|
-
# Session lookups (heartbeat, disconnect, session_status) always scope
|
|
32
|
-
# by (subject, session); the composite unique index above serves those.
|
|
33
|
-
# Query by subject alone
|
|
34
|
-
add_index :<%= table_name %>, :<%= subject_column %>
|
|
35
|
-
|
|
36
|
-
# Cleanup old records
|
|
37
|
-
add_index :<%= table_name %>, :last_heartbeat
|
|
38
|
-
|
|
39
|
-
<% if subject_table.present? -%>
|
|
40
|
-
# Foreign key to subject table
|
|
41
|
-
add_foreign_key :<%= table_name %>, :<%= subject_table %>, column: :<%= subject_column %>
|
|
42
|
-
<% end -%>
|
|
43
|
-
end
|
|
44
|
-
end
|