Rails中配置邮件定时发送:注册时及注册3个月后发件
Hey Mike, great question! Let's break this down into manageable steps to get that scheduled email up and running without disrupting your existing signup flow. I'll cover two common approaches for Rails scheduled tasks — pick the one that fits your setup best.
First: Prep the Email Template & Mailer Method
Since you already have signup emails working with the exchange gem, we just need to add a new email for the 3-month follow-up:
Open your user mailer (probably
app/mailers/user_mailer.rb) and add a new method:class UserMailer < ApplicationMailer # Your existing signup email method here def welcome_email(user) # ... your existing code end # New 3-month follow-up email def three_months_after_signup_email(user) @user = user mail(to: @user.email, subject: "We Missed You! Three Months Since You Joined") end endCreate the email templates in
app/views/user_mailer/:three_months_after_signup_email.html.erb(HTML version)three_months_after_signup_email.text.erb(plain text fallback)
Fill these with your desired follow-up content — keep it personal!
Option 1: Use the whenever Gem (Simple Cron Jobs)
If you don't have a background queue set up, whenever is a straightforward way to manage cron tasks through Ruby.
Steps:
Add the gem to your
Gemfile:gem 'whenever', require: falseRun
bundle installto install it.Generate the schedule config file:
wheneverize .This creates
config/schedule.rb.Configure the daily cron job in
config/schedule.rb:every :day, at: '2:00 am' do # Run daily at 2 AM (low traffic time) runner "User.send_three_month_followup_emails" endAdd the method to your
Usermodel to handle finding users and sending emails:class User < ApplicationRecord # Add a column to track if we've sent this email (prevents duplicates!) # Run this migration first: rails generate migration AddThreeMonthsEmailSentAtToUsers three_months_email_sent_at:datetime # Then run rails db:migrate def self.send_three_month_followup_emails # Find users who signed up exactly 3 months ago and haven't gotten the email yet three_months_ago_window = 3.months.ago.beginning_of_day..3.months.ago.end_of_day eligible_users = where(created_at: three_months_ago_window, three_months_email_sent_at: nil) eligible_users.each do |user| UserMailer.three_months_after_signup_email(user).deliver_now user.update!(three_months_email_sent_at: Time.current) end endDeploy the cron job:
# For production whenever --update-crontab # For development (if you want to test locally) whenever --update-crontab --set environment=development
Option 2: Use Sidekiq + Sidekiq-Cron (Background Queue, Recommended)
If you're already using Sidekiq for background jobs, this is the better approach — it keeps email sending off your main server and is more scalable.
Steps:
Add the gems to your
Gemfile:gem 'sidekiq' gem 'sidekiq-cron'Run
bundle install.Configure Sidekiq to load cron jobs on startup. Create/modify
config/initializers/sidekiq.rb:Sidekiq.configure_server do |config| config.on(:startup) do # Load cron jobs from the schedule.yml file Sidekiq::Cron::Job.load_from_hash YAML.load_file(Rails.root.join('config', 'schedule.yml')) end end Sidekiq.configure_client do |config| # Standard Sidekiq client config (if you already have this, leave it) endCreate the cron schedule file
config/schedule.yml:send_three_month_followup_emails: cron: "0 2 * * *" # Daily at 2 AM (cron syntax: minute hour day month weekday) class: "ThreeMonthsAfterSignupWorker" queue: defaultCreate the Sidekiq worker in
app/workers/three_months_after_signup_worker.rb:class ThreeMonthsAfterSignupWorker include Sidekiq::Worker sidekiq_options retry: 3 # Retry failed emails 3 times def perform three_months_ago_window = 3.months.ago.beginning_of_day..3.months.ago.end_of_day # Use find_each to handle large user lists efficiently User.where(created_at: three_months_ago_window, three_months_email_sent_at: nil).find_each do |user| UserMailer.three_months_after_signup_email(user).deliver_later user.update!(three_months_email_sent_at: Time.current) end end endStart Sidekiq (and make sure it runs in production):
bundle exec sidekiqFor deployment, use a process manager like systemd or supervisord to keep Sidekiq running.
Key Notes to Avoid Headaches
- Prevent duplicate emails: The
three_months_email_sent_atcolumn is critical — don't skip adding that migration. It ensures you never send the same email twice to a user. - Test locally: For development, you can tweak the cron schedule to run every minute (
* * * * *) to test quickly, or manually trigger the worker/method in the Rails console:# For whenever approach User.send_three_month_followup_emails # For Sidekiq approach ThreeMonthsAfterSignupWorker.perform_async - Use
deliver_later: If using Sidekiq, always usedeliver_laterinstead ofdeliver_nowto offload email sending to the background.
Let me know if you hit any snags — whether it's setting up the migration, tweaking the cron schedule, or troubleshooting email delivery!
内容的提问来源于stack exchange,提问作者Mike

