agent-notes 2.0.4__py3-none-any.whl
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.
- agent_notes/VERSION +1 -0
- agent_notes/__init__.py +1 -0
- agent_notes/__main__.py +4 -0
- agent_notes/cli.py +348 -0
- agent_notes/commands/__init__.py +27 -0
- agent_notes/commands/_install_helpers.py +262 -0
- agent_notes/commands/build.py +170 -0
- agent_notes/commands/doctor.py +112 -0
- agent_notes/commands/info.py +95 -0
- agent_notes/commands/install.py +99 -0
- agent_notes/commands/list.py +169 -0
- agent_notes/commands/memory.py +430 -0
- agent_notes/commands/regenerate.py +152 -0
- agent_notes/commands/set_role.py +143 -0
- agent_notes/commands/uninstall.py +26 -0
- agent_notes/commands/update.py +169 -0
- agent_notes/commands/validate.py +199 -0
- agent_notes/commands/wizard.py +720 -0
- agent_notes/config.py +154 -0
- agent_notes/data/agents/agents.yaml +352 -0
- agent_notes/data/agents/analyst.md +45 -0
- agent_notes/data/agents/api-reviewer.md +47 -0
- agent_notes/data/agents/architect.md +46 -0
- agent_notes/data/agents/coder.md +28 -0
- agent_notes/data/agents/database-specialist.md +45 -0
- agent_notes/data/agents/debugger.md +47 -0
- agent_notes/data/agents/devil.md +47 -0
- agent_notes/data/agents/devops.md +38 -0
- agent_notes/data/agents/explorer.md +23 -0
- agent_notes/data/agents/integrations.md +44 -0
- agent_notes/data/agents/lead.md +216 -0
- agent_notes/data/agents/performance-profiler.md +44 -0
- agent_notes/data/agents/refactorer.md +48 -0
- agent_notes/data/agents/reviewer.md +44 -0
- agent_notes/data/agents/security-auditor.md +44 -0
- agent_notes/data/agents/system-auditor.md +38 -0
- agent_notes/data/agents/tech-writer.md +32 -0
- agent_notes/data/agents/test-runner.md +36 -0
- agent_notes/data/agents/test-writer.md +39 -0
- agent_notes/data/cli/claude.yaml +25 -0
- agent_notes/data/cli/copilot.yaml +18 -0
- agent_notes/data/cli/opencode.yaml +22 -0
- agent_notes/data/commands/brainstorm.md +8 -0
- agent_notes/data/commands/debug.md +9 -0
- agent_notes/data/commands/review.md +10 -0
- agent_notes/data/global-claude.md +290 -0
- agent_notes/data/global-copilot.md +27 -0
- agent_notes/data/global-opencode.md +40 -0
- agent_notes/data/hooks/session-context.md.tpl +19 -0
- agent_notes/data/models/claude-haiku-4-5.yaml +15 -0
- agent_notes/data/models/claude-opus-4-1.yaml +16 -0
- agent_notes/data/models/claude-opus-4-5.yaml +16 -0
- agent_notes/data/models/claude-opus-4-6.yaml +16 -0
- agent_notes/data/models/claude-opus-4-7.yaml +15 -0
- agent_notes/data/models/claude-sonnet-4-5.yaml +16 -0
- agent_notes/data/models/claude-sonnet-4-6.yaml +15 -0
- agent_notes/data/models/claude-sonnet-4.yaml +16 -0
- agent_notes/data/pricing.yaml +33 -0
- agent_notes/data/roles/orchestrator.yaml +5 -0
- agent_notes/data/roles/reasoner.yaml +5 -0
- agent_notes/data/roles/scout.yaml +5 -0
- agent_notes/data/roles/worker.yaml +5 -0
- agent_notes/data/rules/code-quality.md +9 -0
- agent_notes/data/rules/safety.md +10 -0
- agent_notes/data/scripts/cost-report +211 -0
- agent_notes/data/skills/brainstorming/SKILL.md +57 -0
- agent_notes/data/skills/code-review/SKILL.md +64 -0
- agent_notes/data/skills/debugging-protocol/SKILL.md +51 -0
- agent_notes/data/skills/docker-compose/SKILL.md +318 -0
- agent_notes/data/skills/docker-compose-advanced/SKILL.md +575 -0
- agent_notes/data/skills/docker-dockerfile/SKILL.md +385 -0
- agent_notes/data/skills/docker-dockerfile-languages/SKILL.md +293 -0
- agent_notes/data/skills/git/SKILL.md +87 -0
- agent_notes/data/skills/rails-active-storage/SKILL.md +321 -0
- agent_notes/data/skills/rails-broadcasting/SKILL.md +374 -0
- agent_notes/data/skills/rails-concerns/SKILL.md +806 -0
- agent_notes/data/skills/rails-controllers/SKILL.md +510 -0
- agent_notes/data/skills/rails-controllers-advanced/SKILL.md +441 -0
- agent_notes/data/skills/rails-helpers/SKILL.md +677 -0
- agent_notes/data/skills/rails-initializers/SKILL.md +79 -0
- agent_notes/data/skills/rails-javascript/SKILL.md +567 -0
- agent_notes/data/skills/rails-jobs/SKILL.md +700 -0
- agent_notes/data/skills/rails-kamal/SKILL.md +483 -0
- agent_notes/data/skills/rails-lib/SKILL.md +101 -0
- agent_notes/data/skills/rails-mailers/SKILL.md +321 -0
- agent_notes/data/skills/rails-migrations/SKILL.md +268 -0
- agent_notes/data/skills/rails-models/SKILL.md +459 -0
- agent_notes/data/skills/rails-models-advanced/SKILL.md +398 -0
- agent_notes/data/skills/rails-routes/SKILL.md +804 -0
- agent_notes/data/skills/rails-style/SKILL.md +538 -0
- agent_notes/data/skills/rails-testing-controllers/SKILL.md +343 -0
- agent_notes/data/skills/rails-testing-models/SKILL.md +296 -0
- agent_notes/data/skills/rails-testing-system/SKILL.md +375 -0
- agent_notes/data/skills/rails-validations/SKILL.md +108 -0
- agent_notes/data/skills/rails-view-components/SKILL.md +511 -0
- agent_notes/data/skills/rails-view-components-advanced/SKILL.md +376 -0
- agent_notes/data/skills/rails-views/SKILL.md +413 -0
- agent_notes/data/skills/rails-views-advanced/SKILL.md +450 -0
- agent_notes/data/skills/refactoring-protocol/SKILL.md +64 -0
- agent_notes/data/skills/tdd/SKILL.md +57 -0
- agent_notes/data/templates/__init__.py +1 -0
- agent_notes/data/templates/__pycache__/__init__.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__init__.py +1 -0
- agent_notes/data/templates/frontmatter/__pycache__/__init__.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/claude.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/cursor.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/__pycache__/opencode.cpython-314.pyc +0 -0
- agent_notes/data/templates/frontmatter/claude.py +44 -0
- agent_notes/data/templates/frontmatter/opencode.py +104 -0
- agent_notes/doctor_checks.py +189 -0
- agent_notes/domain/__init__.py +17 -0
- agent_notes/domain/agent.py +34 -0
- agent_notes/domain/cli_backend.py +40 -0
- agent_notes/domain/diagnostics.py +29 -0
- agent_notes/domain/diff.py +44 -0
- agent_notes/domain/model.py +27 -0
- agent_notes/domain/role.py +13 -0
- agent_notes/domain/rule.py +13 -0
- agent_notes/domain/skill.py +15 -0
- agent_notes/domain/state.py +46 -0
- agent_notes/install_state.py +11 -0
- agent_notes/registries/__init__.py +16 -0
- agent_notes/registries/_base.py +46 -0
- agent_notes/registries/agent_registry.py +107 -0
- agent_notes/registries/cli_registry.py +89 -0
- agent_notes/registries/model_registry.py +85 -0
- agent_notes/registries/role_registry.py +64 -0
- agent_notes/registries/rule_registry.py +80 -0
- agent_notes/registries/skill_registry.py +141 -0
- agent_notes/services/__init__.py +8 -0
- agent_notes/services/diagnostics/__init__.py +47 -0
- agent_notes/services/diagnostics/_checks.py +272 -0
- agent_notes/services/diagnostics/_display.py +346 -0
- agent_notes/services/diagnostics/_fix.py +169 -0
- agent_notes/services/diff.py +349 -0
- agent_notes/services/fs.py +195 -0
- agent_notes/services/install_state_builder.py +210 -0
- agent_notes/services/installer.py +293 -0
- agent_notes/services/memory_backend.py +155 -0
- agent_notes/services/rendering.py +329 -0
- agent_notes/services/session_context.py +23 -0
- agent_notes/services/settings_writer.py +79 -0
- agent_notes/services/state_store.py +249 -0
- agent_notes/services/ui.py +419 -0
- agent_notes/services/user_config.py +62 -0
- agent_notes/services/validation.py +67 -0
- agent_notes/state.py +21 -0
- agent_notes-2.0.4.dist-info/METADATA +14 -0
- agent_notes-2.0.4.dist-info/RECORD +162 -0
- agent_notes-2.0.4.dist-info/WHEEL +5 -0
- agent_notes-2.0.4.dist-info/entry_points.txt +2 -0
- agent_notes-2.0.4.dist-info/licenses/LICENSE +21 -0
- agent_notes-2.0.4.dist-info/top_level.txt +2 -0
- tests/conftest.py +20 -0
- tests/functional/__init__.py +0 -0
- tests/functional/test_build_commands.py +88 -0
- tests/functional/test_registries.py +128 -0
- tests/integration/__init__.py +0 -0
- tests/integration/test_build_output.py +129 -0
- tests/plugins/__init__.py +0 -0
- tests/plugins/test_agents.py +93 -0
- tests/plugins/test_skills.py +77 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-broadcasting
|
|
3
|
+
description: "Turbo Streams broadcasting: real-time updates, morphing, targeting, and Action Cable"
|
|
4
|
+
group: rails
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Broadcasting (Turbo Streams)
|
|
8
|
+
|
|
9
|
+
Real-time updates with Turbo Streams and Action Cable.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Model Broadcasting
|
|
14
|
+
|
|
15
|
+
### broadcasts_refreshes
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
class Card < ApplicationRecord
|
|
19
|
+
# Automatically broadcasts refresh on changes
|
|
20
|
+
broadcasts_refreshes
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Custom Broadcasting
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
class Card < ApplicationRecord
|
|
28
|
+
after_create_commit -> { broadcast_prepend_to "cards", partial: "cards/card", target: "cards-list" }
|
|
29
|
+
after_update_commit -> { broadcast_replace_to "cards", partial: "cards/card" }
|
|
30
|
+
after_destroy_commit -> { broadcast_remove_to "cards" }
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Conditional Broadcasting
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
module Card::Broadcastable
|
|
38
|
+
extend ActiveSupport::Concern
|
|
39
|
+
|
|
40
|
+
included do
|
|
41
|
+
broadcasts_refreshes
|
|
42
|
+
|
|
43
|
+
before_update :remember_if_preview_changed
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
def remember_if_preview_changed
|
|
48
|
+
@preview_changed ||= title_changed? || column_id_changed?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def preview_changed?
|
|
52
|
+
@preview_changed
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Manual Broadcasting
|
|
60
|
+
|
|
61
|
+
### From Controllers
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
def create
|
|
65
|
+
@card = Card.create!(card_params)
|
|
66
|
+
|
|
67
|
+
@card.broadcast_prepend_to "cards",
|
|
68
|
+
partial: "cards/card",
|
|
69
|
+
target: "cards-list"
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### From Jobs
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
class NotificationJob < ApplicationJob
|
|
77
|
+
def perform(user, message)
|
|
78
|
+
Turbo::StreamsChannel.broadcast_append_to(
|
|
79
|
+
"user:#{user.id}:notifications",
|
|
80
|
+
target: "notifications",
|
|
81
|
+
partial: "notifications/notification",
|
|
82
|
+
locals: { message: message }
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Subscribing to Streams
|
|
91
|
+
|
|
92
|
+
### In Views
|
|
93
|
+
|
|
94
|
+
```erb
|
|
95
|
+
<%= turbo_stream_from "cards" %>
|
|
96
|
+
|
|
97
|
+
<div id="cards-list">
|
|
98
|
+
<%= render @cards %>
|
|
99
|
+
</div>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### User-Specific Streams
|
|
103
|
+
|
|
104
|
+
```erb
|
|
105
|
+
<%= turbo_stream_from "user:#{current_user.id}:notifications" %>
|
|
106
|
+
|
|
107
|
+
<div id="notifications">
|
|
108
|
+
<!-- Notifications will appear here -->
|
|
109
|
+
</div>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Turbo Stream Methods
|
|
115
|
+
|
|
116
|
+
### All Broadcast Methods
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
# Append to target
|
|
120
|
+
broadcast_append_to "stream", target: "id", partial: "path"
|
|
121
|
+
|
|
122
|
+
# Prepend to target
|
|
123
|
+
broadcast_prepend_to "stream", target: "id", partial: "path"
|
|
124
|
+
|
|
125
|
+
# Replace target
|
|
126
|
+
broadcast_replace_to "stream", target: "id", partial: "path"
|
|
127
|
+
|
|
128
|
+
# Update target (replaces innerHTML only)
|
|
129
|
+
broadcast_update_to "stream", target: "id", partial: "path"
|
|
130
|
+
|
|
131
|
+
# Remove target
|
|
132
|
+
broadcast_remove_to "stream", target: "id"
|
|
133
|
+
|
|
134
|
+
# Before target
|
|
135
|
+
broadcast_before_to "stream", target: "id", partial: "path"
|
|
136
|
+
|
|
137
|
+
# After target
|
|
138
|
+
broadcast_after_to "stream", target: "id", partial: "path"
|
|
139
|
+
|
|
140
|
+
# Morph (Turbo 8+) - Preserves form state, focus, scroll position
|
|
141
|
+
broadcast_morph_to "stream", target: "id", partial: "path"
|
|
142
|
+
|
|
143
|
+
# Refresh entire page
|
|
144
|
+
broadcast_refresh_to "stream"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Morphing (Turbo 8+)
|
|
150
|
+
|
|
151
|
+
Morphing updates the DOM while preserving form state, focus, and scroll position. Use for complex updates where you want smooth transitions without losing user context.
|
|
152
|
+
|
|
153
|
+
### In Controllers
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
def update
|
|
157
|
+
@card = Card.find(params[:id])
|
|
158
|
+
@card.update!(card_params)
|
|
159
|
+
|
|
160
|
+
# Morph instead of replace - preserves form state
|
|
161
|
+
@card.broadcast_morph_to "cards",
|
|
162
|
+
partial: "cards/card",
|
|
163
|
+
target: dom_id(@card)
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### In Views (Manual Turbo Streams)
|
|
168
|
+
|
|
169
|
+
```erb
|
|
170
|
+
<%# app/views/cards/update.turbo_stream.erb %>
|
|
171
|
+
<%= turbo_stream.morph dom_id(@card) do %>
|
|
172
|
+
<%= render @card %>
|
|
173
|
+
<% end %>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### When to Use Morph vs Replace
|
|
177
|
+
|
|
178
|
+
**Use morph when:**
|
|
179
|
+
- Updating forms with user input
|
|
180
|
+
- Preserving scroll position
|
|
181
|
+
- Maintaining focus state
|
|
182
|
+
- Complex UI with nested interactive elements
|
|
183
|
+
|
|
184
|
+
**Use replace when:**
|
|
185
|
+
- Simple content updates
|
|
186
|
+
- No user interaction in the element
|
|
187
|
+
- Complete state refresh needed
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Stream Targeting Strategies
|
|
192
|
+
|
|
193
|
+
### Instance-Based (Single Record)
|
|
194
|
+
|
|
195
|
+
```erb
|
|
196
|
+
<%# Subscribe to specific card updates %>
|
|
197
|
+
<%= turbo_stream_from @card %>
|
|
198
|
+
|
|
199
|
+
<%# In model %>
|
|
200
|
+
class Card < ApplicationRecord
|
|
201
|
+
after_update_commit -> {
|
|
202
|
+
broadcast_replace_to self, partial: "cards/card"
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Collection-Based (All Records)
|
|
208
|
+
|
|
209
|
+
```erb
|
|
210
|
+
<%# Subscribe to all cards %>
|
|
211
|
+
<%= turbo_stream_from "cards" %>
|
|
212
|
+
|
|
213
|
+
<%# In model %>
|
|
214
|
+
class Card < ApplicationRecord
|
|
215
|
+
after_create_commit -> {
|
|
216
|
+
broadcast_prepend_to "cards", target: "cards-list"
|
|
217
|
+
}
|
|
218
|
+
end
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Nested/Scoped Streams
|
|
222
|
+
|
|
223
|
+
```erb
|
|
224
|
+
<%# Subscribe to cards within a board %>
|
|
225
|
+
<%= turbo_stream_from @board, "cards" %>
|
|
226
|
+
<%= turbo_stream_from [@board, @card] %>
|
|
227
|
+
|
|
228
|
+
<%# In model %>
|
|
229
|
+
class Card < ApplicationRecord
|
|
230
|
+
after_update_commit -> {
|
|
231
|
+
broadcast_replace_to [board, "cards"], partial: "cards/card"
|
|
232
|
+
}
|
|
233
|
+
end
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### User-Specific Streams
|
|
237
|
+
|
|
238
|
+
```erb
|
|
239
|
+
<%# Subscribe to current user's notifications %>
|
|
240
|
+
<%= turbo_stream_from current_user, "notifications" %>
|
|
241
|
+
<%= turbo_stream_from "user:#{current_user.id}:notifications" %>
|
|
242
|
+
|
|
243
|
+
<%# In job/model %>
|
|
244
|
+
Turbo::StreamsChannel.broadcast_append_to(
|
|
245
|
+
[current_user, "notifications"],
|
|
246
|
+
target: "notifications",
|
|
247
|
+
partial: "notifications/notification"
|
|
248
|
+
)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Multiple Targets
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
# Broadcast to multiple streams
|
|
255
|
+
def after_update_commit
|
|
256
|
+
# Update the card itself
|
|
257
|
+
broadcast_replace_to self
|
|
258
|
+
|
|
259
|
+
# Update the board's card list
|
|
260
|
+
broadcast_replace_to [board, "cards"],
|
|
261
|
+
target: "cards-list",
|
|
262
|
+
partial: "boards/cards_list"
|
|
263
|
+
|
|
264
|
+
# Update user's activity feed
|
|
265
|
+
broadcast_prepend_to [creator, "activity"],
|
|
266
|
+
target: "activity-feed",
|
|
267
|
+
partial: "activity/card_updated"
|
|
268
|
+
end
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Performance Considerations
|
|
274
|
+
|
|
275
|
+
### When to Use Broadcasting
|
|
276
|
+
|
|
277
|
+
**✅ Good use cases:**
|
|
278
|
+
- Collaborative editing (multiple users)
|
|
279
|
+
- Real-time notifications
|
|
280
|
+
- Live dashboards
|
|
281
|
+
- Chat/messaging
|
|
282
|
+
- Status updates
|
|
283
|
+
|
|
284
|
+
**⚠️ Avoid when:**
|
|
285
|
+
- Single-user forms (use Turbo Frames instead)
|
|
286
|
+
- High-frequency updates (> 10/sec per user)
|
|
287
|
+
- Large data sets (paginate or batch)
|
|
288
|
+
- Non-interactive content
|
|
289
|
+
|
|
290
|
+
### Optimization Tips
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
# ❌ Bad - Broadcasts on every save
|
|
294
|
+
class Card < ApplicationRecord
|
|
295
|
+
broadcasts_refreshes
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# ✅ Good - Conditional broadcasting
|
|
299
|
+
class Card < ApplicationRecord
|
|
300
|
+
after_update_commit :broadcast_if_changed, if: :saved_changes?
|
|
301
|
+
|
|
302
|
+
private
|
|
303
|
+
def broadcast_if_changed
|
|
304
|
+
return unless saved_change_to_title? || saved_change_to_status?
|
|
305
|
+
broadcast_replace_to "cards"
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# ✅ Better - Debounce rapid updates
|
|
310
|
+
class Card < ApplicationRecord
|
|
311
|
+
after_update_commit :broadcast_later
|
|
312
|
+
|
|
313
|
+
private
|
|
314
|
+
def broadcast_later
|
|
315
|
+
BroadcastCardJob.set(wait: 100.milliseconds).perform_later(self)
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Troubleshooting
|
|
323
|
+
|
|
324
|
+
### Broadcasts Not Appearing
|
|
325
|
+
|
|
326
|
+
**Check WebSocket connection:**
|
|
327
|
+
```javascript
|
|
328
|
+
// In browser console
|
|
329
|
+
Turbo.StreamActions
|
|
330
|
+
Turbo.session.adapter.visitStarted
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Verify stream subscription:**
|
|
334
|
+
```erb
|
|
335
|
+
<%# Make sure turbo_stream_from matches broadcast target %>
|
|
336
|
+
<%= turbo_stream_from "cards" %> <%# Must match broadcast_*_to "cards" %>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**Check Action Cable:**
|
|
340
|
+
```ruby
|
|
341
|
+
# config/cable.yml should have correct adapter
|
|
342
|
+
development:
|
|
343
|
+
adapter: async
|
|
344
|
+
|
|
345
|
+
production:
|
|
346
|
+
adapter: redis
|
|
347
|
+
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Performance Issues
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
# Limit broadcast frequency
|
|
354
|
+
class Card < ApplicationRecord
|
|
355
|
+
# Throttle broadcasts
|
|
356
|
+
after_update_commit :broadcast_changes, if: -> {
|
|
357
|
+
(saved_changes.keys & %w[title status]).any?
|
|
358
|
+
}
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Or use background jobs
|
|
362
|
+
after_update_commit -> { BroadcastJob.perform_later(self) }
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Summary
|
|
368
|
+
|
|
369
|
+
- **broadcasts_refreshes**: Automatic model broadcasting (simple)
|
|
370
|
+
- **broadcast_morph_to**: Preserves form state and scroll (Turbo 8+)
|
|
371
|
+
- **Targeting**: Instance, collection, nested, user-specific
|
|
372
|
+
- **Methods**: append, prepend, replace, update, remove, morph
|
|
373
|
+
- **Performance**: Conditional broadcasting, debouncing, background jobs
|
|
374
|
+
- **Action Cable**: WebSocket connection for real-time updates
|