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,376 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-view-components-advanced
|
|
3
|
+
description: "ViewComponent advanced: best practices, organization, lifecycle, and common patterns"
|
|
4
|
+
group: rails
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ViewComponent (Advanced)
|
|
8
|
+
|
|
9
|
+
## Best Practices
|
|
10
|
+
|
|
11
|
+
### DO
|
|
12
|
+
|
|
13
|
+
1. **Use composition instead of inheritance**
|
|
14
|
+
```ruby
|
|
15
|
+
# GOOD - Composition
|
|
16
|
+
class PanelComponent < ViewComponent::Base
|
|
17
|
+
renders_one :card, CardComponent
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# BAD - Inheritance
|
|
21
|
+
class PanelComponent < CardComponent
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. **Extract components after proving pattern across multiple uses**
|
|
26
|
+
```ruby
|
|
27
|
+
# Good frameworks are extracted, not invented
|
|
28
|
+
# Develop single-use components first, extract when pattern repeats 3+ times
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
3. **Pass global state explicitly as arguments**
|
|
32
|
+
```ruby
|
|
33
|
+
# GOOD
|
|
34
|
+
render UserCardComponent.new(user: current_user, signed_in: user_signed_in?)
|
|
35
|
+
|
|
36
|
+
# BAD - Accessing global state
|
|
37
|
+
class UserCardComponent < ViewComponent::Base
|
|
38
|
+
def call
|
|
39
|
+
if user_signed_in? # Don't access global state
|
|
40
|
+
...
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
4. **Use instance methods instead of inline Ruby in templates**
|
|
47
|
+
```ruby
|
|
48
|
+
# GOOD
|
|
49
|
+
class ButtonComponent < ViewComponent::Base
|
|
50
|
+
private
|
|
51
|
+
def button_classes
|
|
52
|
+
["btn", "btn-#{type}", size_class].compact.join(" ")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```erb
|
|
58
|
+
<button class="<%= button_classes %>">
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
5. **Prefer slots for providing markup to components**
|
|
62
|
+
```ruby
|
|
63
|
+
# GOOD - Using slots
|
|
64
|
+
card.with_header do
|
|
65
|
+
content_tag :h3, "Title"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# BAD - Passing HTML as argument
|
|
69
|
+
card.header = "<h3>Title</h3>".html_safe
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
6. **Test against rendered content**
|
|
73
|
+
```ruby
|
|
74
|
+
# GOOD
|
|
75
|
+
def test_renders_button
|
|
76
|
+
render_inline ButtonComponent.new(type: :primary)
|
|
77
|
+
assert_selector "button.btn-primary"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# BAD - Testing only instance methods
|
|
81
|
+
def test_button_classes
|
|
82
|
+
component = ButtonComponent.new(type: :primary)
|
|
83
|
+
assert_equal "btn btn-primary", component.send(:button_classes)
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
7. **Make most instance methods private**
|
|
88
|
+
```ruby
|
|
89
|
+
class ButtonComponent < ViewComponent::Base
|
|
90
|
+
def initialize(type:)
|
|
91
|
+
@type = type
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Public interface is minimal
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
attr_reader :type
|
|
98
|
+
|
|
99
|
+
# Helper methods are private but accessible in templates
|
|
100
|
+
def button_classes
|
|
101
|
+
"btn btn-#{type}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
8. **Replace partials and HTML-generating helpers**
|
|
107
|
+
```ruby
|
|
108
|
+
# GOOD - ViewComponent
|
|
109
|
+
render ButtonComponent.new(type: :primary, url: user_path(@user))
|
|
110
|
+
|
|
111
|
+
# OLD - Partial
|
|
112
|
+
render "shared/button", type: :primary, url: user_path(@user)
|
|
113
|
+
|
|
114
|
+
# OLD - Helper
|
|
115
|
+
button_tag type: :primary, url: user_path(@user)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### DON'T
|
|
119
|
+
|
|
120
|
+
1. **Don't use component inheritance with separate templates**
|
|
121
|
+
```ruby
|
|
122
|
+
# BAD - Confusing inheritance
|
|
123
|
+
class PanelComponent < CardComponent
|
|
124
|
+
# Has its own template - which one renders?
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
2. **Don't write inline Ruby in templates**
|
|
129
|
+
```erb
|
|
130
|
+
<%# BAD %>
|
|
131
|
+
<button class="btn <%= type == :primary ? 'btn-primary' : 'btn-secondary' %>">
|
|
132
|
+
|
|
133
|
+
<%# GOOD - Use instance method %>
|
|
134
|
+
<button class="<%= button_classes %>">
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
3. **Don't pass HTML-safe markup as arguments**
|
|
138
|
+
```ruby
|
|
139
|
+
# BAD - Security risk
|
|
140
|
+
render CardComponent.new(title: "<h3>#{user_input}</h3>".html_safe)
|
|
141
|
+
|
|
142
|
+
# GOOD - Use slots
|
|
143
|
+
render CardComponent.new do |card|
|
|
144
|
+
card.with_title { content_tag :h3, user_input }
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
4. **Don't rely on global state**
|
|
149
|
+
```ruby
|
|
150
|
+
# BAD
|
|
151
|
+
class UserComponent < ViewComponent::Base
|
|
152
|
+
def call
|
|
153
|
+
current_user # Accessing global state
|
|
154
|
+
params[:id] # Accessing request params
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# GOOD - Explicit dependencies
|
|
159
|
+
class UserComponent < ViewComponent::Base
|
|
160
|
+
def initialize(user:, id:)
|
|
161
|
+
@user = user
|
|
162
|
+
@id = id
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Source**: [ViewComponent Best Practices](https://viewcomponent.org/best_practices.html)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Component Organization
|
|
172
|
+
|
|
173
|
+
### Two Component Types
|
|
174
|
+
|
|
175
|
+
1. **General-purpose components** - Common UI patterns
|
|
176
|
+
- Examples: buttons, forms, modals, alerts
|
|
177
|
+
- Like Primer ViewComponents
|
|
178
|
+
- Highly reusable across applications
|
|
179
|
+
|
|
180
|
+
2. **Application-specific components** - Domain-driven
|
|
181
|
+
- Examples: UserCard, ProductListing, InvoiceHeader
|
|
182
|
+
- Convert domain objects into general-purpose components
|
|
183
|
+
- Encapsulate business logic presentation
|
|
184
|
+
|
|
185
|
+
### Naming Conventions
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
# Use -Component suffix (Rails conventions)
|
|
189
|
+
ButtonComponent
|
|
190
|
+
UserCardComponent
|
|
191
|
+
NavigationComponent
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Extraction Strategy
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
1. Develop single-use components first
|
|
198
|
+
2. Extract to reusable component once pattern appears 3+ times
|
|
199
|
+
3. Consolidate similar patterns (DRY)
|
|
200
|
+
4. Minimize single-use view code
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Source**: [ViewComponent Best Practices](https://viewcomponent.org/best_practices.html)
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Lifecycle Methods
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
class Component < ViewComponent::Base
|
|
211
|
+
def initialize(*args)
|
|
212
|
+
# Called when component is instantiated
|
|
213
|
+
super
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def before_render
|
|
217
|
+
# Called before rendering
|
|
218
|
+
# Access to slots here
|
|
219
|
+
@computed_value = expensive_calculation if header?
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def call
|
|
223
|
+
# Optional: custom render logic
|
|
224
|
+
# By default renders the template
|
|
225
|
+
content_tag :div, class: "wrapper" do
|
|
226
|
+
super
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Source**: [ViewComponent Lifecycle Guide](https://viewcomponent.org/guide/lifecycle.html)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Advanced Patterns
|
|
237
|
+
|
|
238
|
+
### Collections
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
class UserComponent < ViewComponent::Base
|
|
242
|
+
def initialize(user:)
|
|
243
|
+
@user = user
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Enable collection rendering
|
|
247
|
+
with_collection_parameter :user
|
|
248
|
+
end
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Usage:**
|
|
252
|
+
```erb
|
|
253
|
+
<%# Renders UserComponent for each user %>
|
|
254
|
+
<%= render UserComponent.with_collection(@users) %>
|
|
255
|
+
|
|
256
|
+
<%# With counter %>
|
|
257
|
+
<%= render UserComponent.with_collection(@users, :user_counter) %>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Conditional Rendering
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
class Component < ViewComponent::Base
|
|
264
|
+
def render?
|
|
265
|
+
# Return false to skip rendering entirely
|
|
266
|
+
user.present? && user.active?
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Helpers
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
class Component < ViewComponent::Base
|
|
275
|
+
# Access Rails helpers
|
|
276
|
+
def formatted_date
|
|
277
|
+
helpers.time_ago_in_words(created_at)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Or delegate
|
|
281
|
+
delegate :link_to, :content_tag, to: :helpers
|
|
282
|
+
end
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Common Patterns
|
|
288
|
+
|
|
289
|
+
### Form Component
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
class FormComponent < ViewComponent::Base
|
|
293
|
+
renders_one :submit_button, ButtonComponent
|
|
294
|
+
|
|
295
|
+
def initialize(url:, method: :post, **options)
|
|
296
|
+
@url = url
|
|
297
|
+
@method = method
|
|
298
|
+
@options = options
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
private
|
|
302
|
+
attr_reader :url, :method, :options
|
|
303
|
+
end
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
```erb
|
|
307
|
+
<%= form_with url: url, method: method, **options do |f| %>
|
|
308
|
+
<%= content %>
|
|
309
|
+
|
|
310
|
+
<% if submit_button? %>
|
|
311
|
+
<%= submit_button %>
|
|
312
|
+
<% else %>
|
|
313
|
+
<%= f.submit "Submit", class: "btn btn-primary" %>
|
|
314
|
+
<% end %>
|
|
315
|
+
<% end %>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Modal Component
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
class ModalComponent < ViewComponent::Base
|
|
322
|
+
renders_one :title
|
|
323
|
+
renders_one :body
|
|
324
|
+
renders_many :actions, ActionComponent
|
|
325
|
+
|
|
326
|
+
def initialize(id:, size: :medium)
|
|
327
|
+
@id = id
|
|
328
|
+
@size = size
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
private
|
|
332
|
+
attr_reader :id, :size
|
|
333
|
+
end
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Table Component
|
|
337
|
+
|
|
338
|
+
```ruby
|
|
339
|
+
class TableComponent < ViewComponent::Base
|
|
340
|
+
renders_many :columns, ColumnComponent
|
|
341
|
+
renders_many :rows, RowComponent
|
|
342
|
+
|
|
343
|
+
def initialize(data:, **options)
|
|
344
|
+
@data = data
|
|
345
|
+
@options = options
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Summary
|
|
353
|
+
|
|
354
|
+
- **ViewComponent** = Reusable, testable, encapsulated view components
|
|
355
|
+
- **100x faster** tests than controller tests
|
|
356
|
+
- **Slots** = `renders_one` (single) and `renders_many` (multiple)
|
|
357
|
+
- **Testing** = Use `render_inline` with Capybara matchers
|
|
358
|
+
- **Previews** = Visualize components at `/rails/view_components`
|
|
359
|
+
- **Best Practices** = Composition over inheritance, explicit dependencies, slots over HTML args
|
|
360
|
+
- **Organization** = General-purpose vs application-specific components
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## References and Sources
|
|
365
|
+
|
|
366
|
+
This guide is based on official ViewComponent documentation:
|
|
367
|
+
|
|
368
|
+
- [ViewComponent Official Documentation](https://viewcomponent.org/)
|
|
369
|
+
- [ViewComponent GitHub Repository](https://github.com/ViewComponent/view_component)
|
|
370
|
+
- [ViewComponent Guide](https://viewcomponent.org/guide/)
|
|
371
|
+
- [ViewComponent Slots](https://viewcomponent.org/guide/slots.html)
|
|
372
|
+
- [ViewComponent Testing](https://viewcomponent.org/guide/testing.html)
|
|
373
|
+
- [ViewComponent Previews](https://viewcomponent.org/guide/previews.html)
|
|
374
|
+
- [ViewComponent Best Practices](https://viewcomponent.org/best_practices.html)
|
|
375
|
+
|
|
376
|
+
Last updated: December 2025
|