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
```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
redirect_to @reminder, notice: 'Reminder was successfully created.'
set_email_schedule
else
render :new
end
@ -27,6 +28,7 @@ class RemindersController < ApplicationController
def update
if @reminder.update(reminder_params)
redirect_to @reminder, notice: 'Reminder was successfully updated.'
set_email_schedule
else
render :edit
end
@ -48,4 +50,8 @@ class RemindersController < ApplicationController
def reminder_params
params.require(:reminder).permit(:title, :body, :date, :user_id)
end
def set_email_schedule
ReminderMailer.schedule_email(@reminder, current_user.email).deliver_later!(wait_until: @reminder.date)
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>
<%= yield %>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<hr>
<% 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 %>
</body>
</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
# 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.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :sendmail
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log

View File

@ -40,6 +40,8 @@ Rails.application.configure do
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# Print deprecation notices to the 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
RSpec.configure do |config|
# 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
# 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.include FactoryBot::Syntax::Methods
config.include(MailerMacros)
config.before(:each) { reset_email }
config.include Devise::Test::IntegrationHelpers, type: :request
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