where_is_waldo 0.1.10 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce4ac18944bec996440772eed972216ff64975a1be13b4f7f3d0230e5cc0807f
4
- data.tar.gz: 80e1f0ba820d24dc5f5d74f7ef4cc31e00ba3f183145d24de17dc24e5bd4b78b
3
+ metadata.gz: 5bd28c86d109c69fc976eaab788972106493a78b3fbd8e8e2370bae5dc936986
4
+ data.tar.gz: fd49e94bf3a08dd4350fdc1b8b413ec5d66916ef1ada8c74162044b3ccbdc428
5
5
  SHA512:
6
- metadata.gz: 877c8e0adf85cb7c8e75f992939c372690d1a928ab3ef4b6a4d64479ba9801ccd74820235db112d4efcab473a0589097165b62edf07824247a1a6be82d6ba32a
7
- data.tar.gz: d91ec5e623c3a8e8b7b149d5d9a80d6110ab5f4a8c81cc71bdaa73608afc593efe534bc2dc53c71df067837fb56c1cac9479df52a421f7364d1b545cc7657018
6
+ metadata.gz: 620c8ce6c899937a25ab39f2858e45c5fa6ac68dc3510cf0e47a5739b36a8f95c24bb3daf385974600ffa652ad32f176a79ac0389286520af7a291bc48cda1e7
7
+ data.tar.gz: 0ecf65ed538cbffc439428ed3f5acc690e178430ba62a9568fb1b4a1d4cb48f33303aaa9724979f8c1c0ebfc44da5eb35133665345a2dfc2d4a3a224373784d7
data/CHANGELOG.md CHANGED
@@ -3,6 +3,61 @@
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
+
6
61
  ## 0.1.10
7
62
 
8
63
  ### Added
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
@@ -148,6 +155,20 @@ WhereIsWaldo.configure do |config|
148
155
  end
149
156
  ```
150
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
+
151
172
  ### Querying Presence
152
173
 
153
174
  ```ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.10
1
+ 0.1.11
@@ -32,9 +32,17 @@ module WhereIsWaldo
32
32
  end
33
33
 
34
34
  def create_channels
35
- template "connection.rb.tt", "app/channels/application_cable/connection.rb"
36
- template "channel.rb.tt", "app/channels/application_cable/channel.rb"
37
- template "presence_channel.rb.tt", "app/channels/presence_channel.rb"
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
- add_index :presences, :<%= session_column %>, unique: true
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
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.10
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: 2026-08-07 00:00:00.000000000 Z
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,7 +215,6 @@ 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
@@ -228,7 +232,6 @@ metadata:
228
232
  changelog_uri: https://github.com/byscott-io/where_is_waldo/blob/master/CHANGELOG.md
229
233
  allowed_push_host: https://rubygems.org
230
234
  rubygems_mfa_required: 'true'
231
- post_install_message:
232
235
  rdoc_options: []
233
236
  require_paths:
234
237
  - lib
@@ -243,8 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
246
  - !ruby/object:Gem::Version
244
247
  version: '0'
245
248
  requirements: []
246
- rubygems_version: 3.5.22
247
- signing_key:
249
+ rubygems_version: 4.0.16
248
250
  specification_version: 4
249
251
  summary: Real-time presence tracking for Rails + React applications
250
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