Add scheduled email feature for reminders

This commit is contained in:
crstin 2020-02-12 14:19:27 +01:00
parent e4a317111c
commit 9ee37b42d8
13 changed files with 127 additions and 4 deletions

View File

@ -3,5 +3,43 @@
## Project Setup ## Project Setup
```sh ```sh
rails new -T -d sqlite3 scheduled_reminders rails db:create db:migrate
``` ```
## Running the tests
```sh
rspec
```
## Running app
```sh
rails s
```
## System dependencies
- sendmail
## Task
> In order to get some insight into your coding style and approach to problem solving, we would like to give you a small programming task for you to solve. You can either upload the project to Github or send it to us via mail. Its not supposed to win any beauty contest either, so it doesnt really matter how the views look, as long as the functionality is there.
>
> Ideally you create an initial commit after the invocation of`rails new`, so its easy to blank out the skeleton code.
>
> Here it goes:
> Sample Task: Scheduled Reminders
> Description: write an application that sends scheduled reminders to its users.
>
> Functionality:
> - a user can register with email and password
> - after signing in, they see a list of existing reminders and can set up new ones
> - once a month, on a configurable day and time, the application sends them an email with the reminder title and text
> - existing reminders can be deleted from the list
> Configuration of the reminder:
> - title
> - description / text
> - day and time of month. Should be any possible day of the month. I.e. “1st of month”, “2nd of month”, but also “last of month”, “2nd last month”
>
> Ideally, tests should be in place, too. If you have any questions, feel free to ask.

View File

@ -19,6 +19,7 @@ class RemindersController < ApplicationController
if @reminder.save if @reminder.save
redirect_to @reminder, notice: 'Reminder was successfully created.' redirect_to @reminder, notice: 'Reminder was successfully created.'
set_email_schedule
else else
render :new render :new
end end
@ -27,6 +28,7 @@ class RemindersController < ApplicationController
def update def update
if @reminder.update(reminder_params) if @reminder.update(reminder_params)
redirect_to @reminder, notice: 'Reminder was successfully updated.' redirect_to @reminder, notice: 'Reminder was successfully updated.'
set_email_schedule
else else
render :edit render :edit
end end
@ -48,4 +50,8 @@ class RemindersController < ApplicationController
def reminder_params def reminder_params
params.require(:reminder).permit(:title, :body, :date, :user_id) params.require(:reminder).permit(:title, :body, :date, :user_id)
end end
def set_email_schedule
ReminderMailer.schedule_email(@reminder, current_user.email).deliver_later!(wait_until: @reminder.date)
end
end end

View File

@ -0,0 +1,6 @@
class ReminderMailer < ApplicationMailer
def schedule_email(reminder, recipient)
@reminder = reminder
mail to: recipient, subject: 'Reminder'
end
end

View File

@ -11,10 +11,12 @@
<body> <body>
<%= yield %> <%= yield %>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<hr> <hr>
<% if user_signed_in? %> <% if user_signed_in? %>
<p><%= link_to('Logout', destroy_user_session_path) %></p> <p><%= link_to('Logout', destroy_user_session_path, method: 'delete') %></p>
<% end %> <% end %>
</body> </body>
</html> </html>

View File

@ -0,0 +1,2 @@
<h1><%= @reminder.title %></h1>
<p><%= @reminder.body %></p>

View File

@ -0,0 +1,3 @@
<%= @reminder.title %>
<%= @reminder.body %>

View File

@ -32,10 +32,14 @@ Rails.application.configure do
config.active_storage.service = :local config.active_storage.service = :local
# Don't care if the mailer can't send. # Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :sendmail
# Print deprecation notices to the Rails logger. # Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log config.active_support.deprecation = :log

View File

@ -40,6 +40,8 @@ Rails.application.configure do
# ActionMailer::Base.deliveries array. # ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# Print deprecation notices to the stderr. # Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr config.active_support.deprecation = :stderr

View File

@ -0,0 +1,13 @@
require 'rails_helper'
RSpec.describe "ResetPassword", type: :feature do
it "sends an email upon a password reset request" do
user = create(:user)
visit new_user_session_path
click_link 'password'
fill_in 'Email', with: user.email
click_button 'reset password'
expect(page).to have_content 'You will receive an email with instructions on how to reset your password in a few minutes.'
expect(last_email).to have_content user.email
end
end

View File

@ -0,0 +1,7 @@
# Preview all emails at http://localhost:3000/rails/mailers/reminder_mailer
class ReminderMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/reminder_mailer/schedule_email
def schedule_email
ReminderMailer.schedule_email(FactoryBot.build(:reminder), 'to@example.com')
end
end

View File

@ -0,0 +1,29 @@
require 'rails_helper'
RSpec.describe ReminderMailer, type: :mailer do
describe 'Email delivery' do
let(:reminder) { build :reminder }
let(:mail) { ReminderMailer.schedule_email reminder, 'to@example.org' }
it 'renders the headers' do
expect(mail.subject).to eq 'Reminder'
expect(mail.to).to eq ['to@example.org']
expect(mail.from).to eq ['from@example.com']
end
it 'renders the body' do
expect(mail.body.encoded).to have_content reminder.title
expect(mail.body.encoded).to have_content reminder.body
end
end
describe 'Scheduled email' do
before { clear_enqueued_jobs }
let(:reminder) { create :reminder }
it 'queues the reminder' do
expect { ReminderMailer.schedule_email(reminder, 'to@example.org').deliver_later(wait_until: reminder.date) }.to have_enqueued_job.on_queue('mailers')
expect { ReminderMailer.schedule_email(reminder, 'to@example.org').deliver_later(wait_until: reminder.date) }.to have_enqueued_job.at(reminder.date)
end
end
end

View File

@ -35,7 +35,7 @@ rescue ActiveRecord::PendingMigrationError => e
end end
RSpec.configure do |config| RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures" # config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your # If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false # examples within a transaction, remove the following line or assign false
@ -63,5 +63,7 @@ RSpec.configure do |config|
# config.filter_gems_from_backtrace("gem name") # config.filter_gems_from_backtrace("gem name")
config.include FactoryBot::Syntax::Methods config.include FactoryBot::Syntax::Methods
config.include(MailerMacros)
config.before(:each) { reset_email }
config.include Devise::Test::IntegrationHelpers, type: :request config.include Devise::Test::IntegrationHelpers, type: :request
end end

View File

@ -0,0 +1,9 @@
module MailerMacros
def last_email
ActionMailer::Base.deliveries.last
end
def reset_email
ActionMailer::Base.deliveries = []
end
end