rails_claude_skills 0.1.0
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 +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
- data/.github/ISSUE_TEMPLATE/config.yml +11 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
- data/.github/ISSUE_TEMPLATE/question.yml +90 -0
- data/.github/dependabot.yml +19 -0
- data/.github/workflows/ci.yml +77 -0
- data/.github/workflows/release.yml +66 -0
- data/.rubocop.yml +52 -0
- data/CHANGELOG.md +94 -0
- data/CLAUDE.md +332 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +580 -0
- data/LICENSE.txt +21 -0
- data/README.md +544 -0
- data/Rakefile +8 -0
- data/lib/generators/claude/agent/agent_generator.rb +71 -0
- data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
- data/lib/generators/claude/command/command_generator.rb +50 -0
- data/lib/generators/claude/command/templates/command.md.tt +28 -0
- data/lib/generators/claude/commands_library/create-pr.md +27 -0
- data/lib/generators/claude/commands_library/dbchange.md +19 -0
- data/lib/generators/claude/commands_library/quality.md +20 -0
- data/lib/generators/claude/commands_library/stimulus.md +19 -0
- data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
- data/lib/generators/claude/install/install_generator.rb +211 -0
- data/lib/generators/claude/install/templates/README.md.tt +59 -0
- data/lib/generators/claude/install/templates/USAGE +28 -0
- data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
- data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
- data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
- data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
- data/lib/generators/claude/rule/rule_generator.rb +175 -0
- data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
- data/lib/generators/claude/rules_library/code-style.md +37 -0
- data/lib/generators/claude/rules_library/database.md +47 -0
- data/lib/generators/claude/rules_library/hotwire.md +56 -0
- data/lib/generators/claude/rules_library/security.md +54 -0
- data/lib/generators/claude/rules_library/testing.md +47 -0
- data/lib/generators/claude/skill/skill_generator.rb +196 -0
- data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
- data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
- data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
- data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
- data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
- data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
- data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
- data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
- data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
- data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
- data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
- data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
- data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
- data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
- data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
- data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
- data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
- data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
- data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
- data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
- data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
- data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
- data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
- data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
- data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
- data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
- data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
- data/lib/generators/claude/views/views_generator.rb +113 -0
- data/lib/rails_claude_skills/railtie.rb +16 -0
- data/lib/rails_claude_skills/version.rb +5 -0
- data/lib/rails_claude_skills.rb +27 -0
- data/sig/rails_claude_skills.rbs +4 -0
- metadata +199 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-mailers
|
|
3
|
+
description: Use when sending emails - ActionMailer with async delivery via SolidQueue, templates, previews, and testing
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Email with ActionMailer
|
|
7
|
+
|
|
8
|
+
Send transactional and notification emails using ActionMailer, integrated with SolidQueue for async delivery. Create HTML and text templates, preview emails in development, and test thoroughly.
|
|
9
|
+
|
|
10
|
+
<when-to-use>
|
|
11
|
+
- Sending transactional emails (password resets, confirmations, receipts)
|
|
12
|
+
- Sending notification emails (updates, alerts, digests)
|
|
13
|
+
- Delivering emails asynchronously via background jobs
|
|
14
|
+
- Creating email templates with HTML and text versions
|
|
15
|
+
- Testing email delivery and content
|
|
16
|
+
</when-to-use>
|
|
17
|
+
|
|
18
|
+
<benefits>
|
|
19
|
+
- **Async Delivery** - ActionMailer integrates with SolidQueue for non-blocking email sending
|
|
20
|
+
- **Template Support** - ERB templates for HTML and text email versions
|
|
21
|
+
- **Preview in Development** - See emails without sending via /rails/mailers
|
|
22
|
+
- **Testing Support** - Full test suite for delivery and content
|
|
23
|
+
- **Layouts** - Shared layouts for consistent email branding
|
|
24
|
+
- **Attachments** - Send files (PDFs, images) with emails
|
|
25
|
+
</benefits>
|
|
26
|
+
|
|
27
|
+
<verification-checklist>
|
|
28
|
+
Before completing mailer work:
|
|
29
|
+
- ✅ Async delivery used (deliver_later, not deliver_now)
|
|
30
|
+
- ✅ Both HTML and text templates provided
|
|
31
|
+
- ✅ URL helpers used (not path helpers)
|
|
32
|
+
- ✅ Email previews created for development
|
|
33
|
+
- ✅ Mailer tests passing (delivery and content)
|
|
34
|
+
- ✅ SolidQueue configured for background delivery
|
|
35
|
+
</verification-checklist>
|
|
36
|
+
|
|
37
|
+
<standards>
|
|
38
|
+
- ALWAYS deliver emails asynchronously with deliver_later (NOT deliver_now)
|
|
39
|
+
- Provide both HTML and text email templates
|
|
40
|
+
- Use *_url helpers (NOT *_path) for links in emails
|
|
41
|
+
- Set default 'from' address in ApplicationMailer
|
|
42
|
+
- Create email previews for development (/rails/mailers)
|
|
43
|
+
- Configure default_url_options for each environment
|
|
44
|
+
- Use inline CSS for email styling (email clients strip external styles)
|
|
45
|
+
- Test email delivery and content
|
|
46
|
+
- Use parameterized mailers (.with()) for cleaner syntax
|
|
47
|
+
</standards>
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ActionMailer Setup
|
|
52
|
+
|
|
53
|
+
<pattern name="actionmailer-basic-setup">
|
|
54
|
+
<description>Configure ActionMailer for email delivery</description>
|
|
55
|
+
|
|
56
|
+
**Mailer Class:**
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
# app/mailers/application_mailer.rb
|
|
60
|
+
class ApplicationMailer < ActionMailer::Base
|
|
61
|
+
default from: "noreply@example.com"
|
|
62
|
+
layout "mailer"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# app/mailers/notification_mailer.rb
|
|
66
|
+
class NotificationMailer < ApplicationMailer
|
|
67
|
+
def welcome_email(user)
|
|
68
|
+
@user = user
|
|
69
|
+
@login_url = login_url
|
|
70
|
+
mail(to: user.email, subject: "Welcome to Our App")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def password_reset(user)
|
|
74
|
+
@user = user
|
|
75
|
+
@reset_url = password_reset_url(user.reset_token)
|
|
76
|
+
mail(to: user.email, subject: "Password Reset Instructions")
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**HTML Template:**
|
|
82
|
+
|
|
83
|
+
```erb
|
|
84
|
+
<%# app/views/notification_mailer/welcome_email.html.erb %>
|
|
85
|
+
<h1>Welcome, <%= @user.name %>!</h1>
|
|
86
|
+
<p>Thanks for signing up. Get started by logging in:</p>
|
|
87
|
+
<%= link_to "Login Now", @login_url, class: "button" %>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Text Template:**
|
|
91
|
+
|
|
92
|
+
```erb
|
|
93
|
+
<%# app/views/notification_mailer/welcome_email.text.erb %>
|
|
94
|
+
Welcome, <%= @user.name %>!
|
|
95
|
+
|
|
96
|
+
Thanks for signing up. Get started by logging in:
|
|
97
|
+
<%= @login_url %>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Usage (Async with SolidQueue):**
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
# In controller or service
|
|
104
|
+
NotificationMailer.welcome_email(@user).deliver_later
|
|
105
|
+
NotificationMailer.password_reset(@user).deliver_later(queue: :mailers)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Why:** ActionMailer integrates seamlessly with SolidQueue for async delivery. Always use deliver_later to avoid blocking requests. Provide both HTML and text versions for compatibility.
|
|
109
|
+
</pattern>
|
|
110
|
+
|
|
111
|
+
<antipattern>
|
|
112
|
+
<description>Using deliver_now in production (blocks HTTP request)</description>
|
|
113
|
+
<bad-example>
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
# ❌ WRONG - Blocks HTTP request thread
|
|
117
|
+
def create
|
|
118
|
+
@user = User.create!(user_params)
|
|
119
|
+
NotificationMailer.welcome_email(@user).deliver_now # Blocks!
|
|
120
|
+
redirect_to @user
|
|
121
|
+
end
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
</bad-example>
|
|
125
|
+
<good-example>
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# ✅ CORRECT - Async delivery via SolidQueue
|
|
129
|
+
def create
|
|
130
|
+
@user = User.create!(user_params)
|
|
131
|
+
NotificationMailer.welcome_email(@user).deliver_later # Non-blocking
|
|
132
|
+
redirect_to @user
|
|
133
|
+
end
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
</good-example>
|
|
137
|
+
|
|
138
|
+
**Why bad:** deliver_now blocks the HTTP request until SMTP completes, creating slow response times and poor user experience. deliver_later uses SolidQueue to send email in background.
|
|
139
|
+
</antipattern>
|
|
140
|
+
|
|
141
|
+
<pattern name="parameterized-mailers">
|
|
142
|
+
<description>Use .with() to pass parameters cleanly to mailers</description>
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
class NotificationMailer < ApplicationMailer
|
|
146
|
+
def custom_notification
|
|
147
|
+
@user = params[:user]
|
|
148
|
+
@message = params[:message]
|
|
149
|
+
mail(to: @user.email, subject: params[:subject])
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Usage
|
|
154
|
+
NotificationMailer.with(
|
|
155
|
+
user: user,
|
|
156
|
+
message: "Update available",
|
|
157
|
+
subject: "System Alert"
|
|
158
|
+
).custom_notification.deliver_later
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Why:** Cleaner syntax, easier to read and modify, and works seamlessly with background jobs.
|
|
162
|
+
</pattern>
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Email Templates
|
|
167
|
+
|
|
168
|
+
<pattern name="email-layouts">
|
|
169
|
+
<description>Shared layouts for consistent email branding</description>
|
|
170
|
+
|
|
171
|
+
**HTML Layout:**
|
|
172
|
+
|
|
173
|
+
```erb
|
|
174
|
+
<%# app/views/layouts/mailer.html.erb %>
|
|
175
|
+
<!DOCTYPE html>
|
|
176
|
+
<html>
|
|
177
|
+
<head>
|
|
178
|
+
<meta charset="utf-8">
|
|
179
|
+
<style>
|
|
180
|
+
body {
|
|
181
|
+
font-family: Arial, sans-serif;
|
|
182
|
+
max-width: 600px;
|
|
183
|
+
margin: 0 auto;
|
|
184
|
+
color: #333;
|
|
185
|
+
}
|
|
186
|
+
.header {
|
|
187
|
+
background-color: #4F46E5;
|
|
188
|
+
color: white;
|
|
189
|
+
padding: 20px;
|
|
190
|
+
text-align: center;
|
|
191
|
+
}
|
|
192
|
+
.content {
|
|
193
|
+
padding: 20px;
|
|
194
|
+
}
|
|
195
|
+
.button {
|
|
196
|
+
display: inline-block;
|
|
197
|
+
padding: 12px 24px;
|
|
198
|
+
background-color: #4F46E5;
|
|
199
|
+
color: white;
|
|
200
|
+
text-decoration: none;
|
|
201
|
+
border-radius: 4px;
|
|
202
|
+
}
|
|
203
|
+
.footer {
|
|
204
|
+
padding: 20px;
|
|
205
|
+
text-align: center;
|
|
206
|
+
font-size: 12px;
|
|
207
|
+
color: #666;
|
|
208
|
+
}
|
|
209
|
+
</style>
|
|
210
|
+
</head>
|
|
211
|
+
<body>
|
|
212
|
+
<div class="header">
|
|
213
|
+
<h1>Your App</h1>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="content">
|
|
216
|
+
<%= yield %>
|
|
217
|
+
</div>
|
|
218
|
+
<div class="footer">
|
|
219
|
+
<p>© 2025 Your Company. All rights reserved.</p>
|
|
220
|
+
</div>
|
|
221
|
+
</body>
|
|
222
|
+
</html>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Text Layout:**
|
|
226
|
+
|
|
227
|
+
```erb
|
|
228
|
+
<%# app/views/layouts/mailer.text.erb %>
|
|
229
|
+
================================================================================
|
|
230
|
+
YOUR APP
|
|
231
|
+
================================================================================
|
|
232
|
+
|
|
233
|
+
<%= yield %>
|
|
234
|
+
|
|
235
|
+
--------------------------------------------------------------------------------
|
|
236
|
+
© 2025 Your Company. All rights reserved.
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Why:** Consistent branding across all emails. Inline CSS ensures styling works across email clients.
|
|
240
|
+
</pattern>
|
|
241
|
+
|
|
242
|
+
<pattern name="email-attachments">
|
|
243
|
+
<description>Attach files to emails (PDFs, CSVs, images)</description>
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
class ReportMailer < ApplicationMailer
|
|
247
|
+
def monthly_report(user, data)
|
|
248
|
+
@user = user
|
|
249
|
+
|
|
250
|
+
# Regular attachment
|
|
251
|
+
attachments["report.pdf"] = {
|
|
252
|
+
mime_type: "application/pdf",
|
|
253
|
+
content: generate_pdf(data)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
# Inline attachment (for embedding in email body)
|
|
257
|
+
attachments.inline["logo.png"] = File.read(
|
|
258
|
+
Rails.root.join("app/assets/images/logo.png")
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
mail(to: user.email, subject: "Monthly Report")
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**In template:**
|
|
267
|
+
|
|
268
|
+
```erb
|
|
269
|
+
<%# Reference inline attachment %>
|
|
270
|
+
<%= image_tag attachments["logo.png"].url %>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Why:** Attach reports, exports, or inline images. Inline attachments can be referenced in email body with image_tag.
|
|
274
|
+
</pattern>
|
|
275
|
+
|
|
276
|
+
<antipattern>
|
|
277
|
+
<description>Using *_path helpers instead of *_url in emails (broken links)</description>
|
|
278
|
+
<bad-example>
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
# ❌ WRONG - Relative path doesn't work in emails
|
|
282
|
+
def welcome_email(user)
|
|
283
|
+
@user = user
|
|
284
|
+
@login_url = login_path # => "/login" (relative path)
|
|
285
|
+
mail(to: user.email, subject: "Welcome")
|
|
286
|
+
end
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
</bad-example>
|
|
290
|
+
<good-example>
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
# ✅ CORRECT - Full URL works in emails
|
|
294
|
+
def welcome_email(user)
|
|
295
|
+
@user = user
|
|
296
|
+
@login_url = login_url # => "https://example.com/login" (absolute URL)
|
|
297
|
+
mail(to: user.email, subject: "Welcome")
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Required configuration
|
|
301
|
+
# config/environments/production.rb
|
|
302
|
+
config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
</good-example>
|
|
306
|
+
|
|
307
|
+
**Why bad:** Emails are viewed outside your application context, so relative paths don't work. Always use *_url helpers to generate absolute URLs.
|
|
308
|
+
</antipattern>
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Email Testing
|
|
313
|
+
|
|
314
|
+
<pattern name="letter-opener-setup">
|
|
315
|
+
<description>Preview emails in browser during development without sending</description>
|
|
316
|
+
|
|
317
|
+
**Configuration:**
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
# Gemfile
|
|
321
|
+
group :development do
|
|
322
|
+
gem "letter_opener"
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# config/environments/development.rb
|
|
326
|
+
config.action_mailer.delivery_method = :letter_opener
|
|
327
|
+
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
|
|
328
|
+
|
|
329
|
+
# config/environments/production.rb
|
|
330
|
+
config.action_mailer.delivery_method = :smtp
|
|
331
|
+
config.action_mailer.smtp_settings = {
|
|
332
|
+
address: "smtp.sendgrid.net",
|
|
333
|
+
port: 587,
|
|
334
|
+
user_name: Rails.application.credentials.dig(:smtp, :username),
|
|
335
|
+
password: Rails.application.credentials.dig(:smtp, :password),
|
|
336
|
+
authentication: :plain,
|
|
337
|
+
enable_starttls_auto: true
|
|
338
|
+
}
|
|
339
|
+
config.action_mailer.default_url_options = { host: "example.com", protocol: "https" }
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**Why:** letter_opener opens emails in browser during development - no SMTP setup needed. Test email appearance without actually sending.
|
|
343
|
+
</pattern>
|
|
344
|
+
|
|
345
|
+
<pattern name="mailer-previews">
|
|
346
|
+
<description>Preview all email variations at /rails/mailers</description>
|
|
347
|
+
|
|
348
|
+
```ruby
|
|
349
|
+
# test/mailers/previews/notification_mailer_preview.rb
|
|
350
|
+
class NotificationMailerPreview < ActionMailer::Preview
|
|
351
|
+
# Preview at http://localhost:3000/rails/mailers/notification_mailer/welcome_email
|
|
352
|
+
def welcome_email
|
|
353
|
+
user = User.first || User.new(name: "Test User", email: "test@example.com")
|
|
354
|
+
NotificationMailer.welcome_email(user)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def password_reset
|
|
358
|
+
user = User.first || User.new(name: "Test User", email: "test@example.com")
|
|
359
|
+
user.reset_token = "sample_token_123"
|
|
360
|
+
NotificationMailer.password_reset(user)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Preview with different data
|
|
364
|
+
def welcome_email_long_name
|
|
365
|
+
user = User.new(name: "Christopher Alexander Montgomery III", email: "long@example.com")
|
|
366
|
+
NotificationMailer.welcome_email(user)
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Why:** Mailer previews at /rails/mailers let you see all email variations without sending. Test different edge cases (long names, missing data, etc.).
|
|
372
|
+
</pattern>
|
|
373
|
+
|
|
374
|
+
<pattern name="mailer-testing">
|
|
375
|
+
<description>Test email delivery and content with ActionMailer::TestCase</description>
|
|
376
|
+
|
|
377
|
+
```ruby
|
|
378
|
+
# test/mailers/notification_mailer_test.rb
|
|
379
|
+
class NotificationMailerTest < ActionMailer::TestCase
|
|
380
|
+
test "welcome_email sends with correct attributes" do
|
|
381
|
+
user = users(:alice)
|
|
382
|
+
email = NotificationMailer.welcome_email(user)
|
|
383
|
+
|
|
384
|
+
# Test delivery
|
|
385
|
+
assert_emails 1 do
|
|
386
|
+
email.deliver_now
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Test attributes
|
|
390
|
+
assert_equal [user.email], email.to
|
|
391
|
+
assert_equal ["noreply@example.com"], email.from
|
|
392
|
+
assert_equal "Welcome to Our App", email.subject
|
|
393
|
+
|
|
394
|
+
# Test content
|
|
395
|
+
assert_includes email.html_part.body.to_s, user.name
|
|
396
|
+
assert_includes email.text_part.body.to_s, user.name
|
|
397
|
+
assert_includes email.html_part.body.to_s, "Login Now"
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
test "delivers via background job" do
|
|
401
|
+
user = users(:alice)
|
|
402
|
+
|
|
403
|
+
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
|
|
404
|
+
NotificationMailer.welcome_email(user).deliver_later(queue: :mailers)
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
test "password_reset includes reset link" do
|
|
409
|
+
user = users(:alice)
|
|
410
|
+
user.update!(reset_token: "test_token_123")
|
|
411
|
+
email = NotificationMailer.password_reset(user)
|
|
412
|
+
|
|
413
|
+
assert_includes email.html_part.body.to_s, "test_token_123"
|
|
414
|
+
assert_includes email.html_part.body.to_s, "password_reset"
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Why:** Test email delivery, content, and background job enqueuing. Verify recipients, subjects, and that emails are queued properly.
|
|
420
|
+
</pattern>
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## Email Configuration
|
|
425
|
+
|
|
426
|
+
<pattern name="environment-configuration">
|
|
427
|
+
<description>Configure ActionMailer for each environment</description>
|
|
428
|
+
|
|
429
|
+
**Development:**
|
|
430
|
+
|
|
431
|
+
```ruby
|
|
432
|
+
# config/environments/development.rb
|
|
433
|
+
config.action_mailer.delivery_method = :letter_opener
|
|
434
|
+
config.action_mailer.perform_deliveries = true
|
|
435
|
+
config.action_mailer.raise_delivery_errors = true
|
|
436
|
+
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Test:**
|
|
440
|
+
|
|
441
|
+
```ruby
|
|
442
|
+
# config/environments/test.rb
|
|
443
|
+
config.action_mailer.delivery_method = :test
|
|
444
|
+
config.action_mailer.default_url_options = { host: "example.com" }
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Production:**
|
|
448
|
+
|
|
449
|
+
```ruby
|
|
450
|
+
# config/environments/production.rb
|
|
451
|
+
config.action_mailer.delivery_method = :smtp
|
|
452
|
+
config.action_mailer.perform_deliveries = true
|
|
453
|
+
config.action_mailer.raise_delivery_errors = false
|
|
454
|
+
config.action_mailer.default_url_options = {
|
|
455
|
+
host: ENV["APP_HOST"],
|
|
456
|
+
protocol: "https"
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
config.action_mailer.smtp_settings = {
|
|
460
|
+
address: ENV["SMTP_ADDRESS"],
|
|
461
|
+
port: ENV["SMTP_PORT"],
|
|
462
|
+
user_name: Rails.application.credentials.dig(:smtp, :username),
|
|
463
|
+
password: Rails.application.credentials.dig(:smtp, :password),
|
|
464
|
+
authentication: :plain,
|
|
465
|
+
enable_starttls_auto: true
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Why:** Different configurations per environment. Development previews in browser, test stores emails in memory, production sends via SMTP.
|
|
470
|
+
</pattern>
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
<testing>
|
|
475
|
+
|
|
476
|
+
```ruby
|
|
477
|
+
# test/mailers/notification_mailer_test.rb
|
|
478
|
+
class NotificationMailerTest < ActionMailer::TestCase
|
|
479
|
+
setup do
|
|
480
|
+
@user = users(:alice)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
test "welcome_email" do
|
|
484
|
+
email = NotificationMailer.welcome_email(@user)
|
|
485
|
+
|
|
486
|
+
assert_emails 1 { email.deliver_now }
|
|
487
|
+
assert_equal [@user.email], email.to
|
|
488
|
+
assert_equal ["noreply@example.com"], email.from
|
|
489
|
+
assert_match @user.name, email.html_part.body.to_s
|
|
490
|
+
assert_match @user.name, email.text_part.body.to_s
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
test "enqueues for async delivery" do
|
|
494
|
+
assert_enqueued_with(job: ActionMailer::MailDeliveryJob) do
|
|
495
|
+
NotificationMailer.welcome_email(@user).deliver_later
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
test "uses correct queue" do
|
|
500
|
+
assert_enqueued_with(job: ActionMailer::MailDeliveryJob, queue: "mailers") do
|
|
501
|
+
NotificationMailer.welcome_email(@user).deliver_later(queue: :mailers)
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# test/system/email_delivery_test.rb
|
|
507
|
+
class EmailDeliveryTest < ApplicationSystemTestCase
|
|
508
|
+
test "sends welcome email after signup" do
|
|
509
|
+
visit signup_path
|
|
510
|
+
fill_in "Email", with: "new@example.com"
|
|
511
|
+
fill_in "Password", with: "password"
|
|
512
|
+
click_button "Sign Up"
|
|
513
|
+
|
|
514
|
+
assert_enqueued_emails 1
|
|
515
|
+
perform_enqueued_jobs
|
|
516
|
+
|
|
517
|
+
email = ActionMailer::Base.deliveries.last
|
|
518
|
+
assert_equal ["new@example.com"], email.to
|
|
519
|
+
assert_match "Welcome", email.subject
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
</testing>
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
<related-skills>
|
|
529
|
+
- rails-ai:jobs - Background job processing with SolidQueue
|
|
530
|
+
- rails-ai:views - Email templates and layouts
|
|
531
|
+
- rails-ai:testing - Testing email delivery
|
|
532
|
+
- rails-ai:project-setup - Environment-specific email configuration
|
|
533
|
+
</related-skills>
|
|
534
|
+
|
|
535
|
+
<resources>
|
|
536
|
+
|
|
537
|
+
**Official Documentation:**
|
|
538
|
+
- [Rails Guides - Action Mailer Basics](https://guides.rubyonrails.org/action_mailer_basics.html)
|
|
539
|
+
|
|
540
|
+
**Gems & Libraries:**
|
|
541
|
+
- [letter_opener](https://github.com/ryanb/letter_opener) - Preview emails in browser during development
|
|
542
|
+
|
|
543
|
+
**Tools:**
|
|
544
|
+
- [Email on Acid](https://www.emailonacid.com/) - Email testing across clients
|
|
545
|
+
|
|
546
|
+
**Email Service Providers:**
|
|
547
|
+
- [SendGrid Rails Guide](https://docs.sendgrid.com/for-developers/sending-email/rubyonrails)
|
|
548
|
+
|
|
549
|
+
</resources>
|