From 9ee37b42d85ba979b495429f214654f5779a9efd Mon Sep 17 00:00:00 2001 From: crstin Date: Wed, 12 Feb 2020 14:19:27 +0100 Subject: [PATCH] Add scheduled email feature for reminders --- README.md | 40 ++++++++++++++++++- app/controllers/reminders_controller.rb | 6 +++ app/mailers/reminder_mailer.rb | 6 +++ app/views/layouts/application.html.erb | 4 +- .../reminder_mailer/schedule_email.html.erb | 2 + .../reminder_mailer/schedule_email.text.erb | 3 ++ config/environments/development.rb | 6 ++- config/environments/test.rb | 2 + spec/features/password_reset_spec.rb | 13 ++++++ .../previews/reminder_mailer_preview.rb | 7 ++++ spec/mailers/reminder_mailer_spec.rb | 29 ++++++++++++++ spec/rails_helper.rb | 4 +- spec/support/mailer_macros.rb | 9 +++++ 13 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 app/mailers/reminder_mailer.rb create mode 100644 app/views/reminder_mailer/schedule_email.html.erb create mode 100644 app/views/reminder_mailer/schedule_email.text.erb create mode 100644 spec/features/password_reset_spec.rb create mode 100644 spec/mailers/previews/reminder_mailer_preview.rb create mode 100644 spec/mailers/reminder_mailer_spec.rb create mode 100644 spec/support/mailer_macros.rb diff --git a/README.md b/README.md index 024a485..d3f209f 100644 --- a/README.md +++ b/README.md @@ -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. It’s not supposed to win any beauty contest either, so it doesn’t 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 it’s 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. diff --git a/app/controllers/reminders_controller.rb b/app/controllers/reminders_controller.rb index 4aa60e5..35ce2ac 100644 --- a/app/controllers/reminders_controller.rb +++ b/app/controllers/reminders_controller.rb @@ -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 diff --git a/app/mailers/reminder_mailer.rb b/app/mailers/reminder_mailer.rb new file mode 100644 index 0000000..f9b342d --- /dev/null +++ b/app/mailers/reminder_mailer.rb @@ -0,0 +1,6 @@ +class ReminderMailer < ApplicationMailer + def schedule_email(reminder, recipient) + @reminder = reminder + mail to: recipient, subject: 'Reminder' + end +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index cd695e2..b1060e0 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,10 +11,12 @@ <%= yield %> +

<%= notice %>

+

<%= alert %>


<% if user_signed_in? %> -

<%= link_to('Logout', destroy_user_session_path) %>

+

<%= link_to('Logout', destroy_user_session_path, method: 'delete') %>

<% end %> diff --git a/app/views/reminder_mailer/schedule_email.html.erb b/app/views/reminder_mailer/schedule_email.html.erb new file mode 100644 index 0000000..29a3d49 --- /dev/null +++ b/app/views/reminder_mailer/schedule_email.html.erb @@ -0,0 +1,2 @@ +

<%= @reminder.title %>

+

<%= @reminder.body %>

diff --git a/app/views/reminder_mailer/schedule_email.text.erb b/app/views/reminder_mailer/schedule_email.text.erb new file mode 100644 index 0000000..d4c3c80 --- /dev/null +++ b/app/views/reminder_mailer/schedule_email.text.erb @@ -0,0 +1,3 @@ +<%= @reminder.title %> + +<%= @reminder.body %> diff --git a/config/environments/development.rb b/config/environments/development.rb index 66df51f..a6e058d 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -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 diff --git a/config/environments/test.rb b/config/environments/test.rb index 1d62e91..1124034 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -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 diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb new file mode 100644 index 0000000..e97b7dc --- /dev/null +++ b/spec/features/password_reset_spec.rb @@ -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 diff --git a/spec/mailers/previews/reminder_mailer_preview.rb b/spec/mailers/previews/reminder_mailer_preview.rb new file mode 100644 index 0000000..6aca293 --- /dev/null +++ b/spec/mailers/previews/reminder_mailer_preview.rb @@ -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 diff --git a/spec/mailers/reminder_mailer_spec.rb b/spec/mailers/reminder_mailer_spec.rb new file mode 100644 index 0000000..272e181 --- /dev/null +++ b/spec/mailers/reminder_mailer_spec.rb @@ -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 diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 58bf2ac..1ad29ed 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -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 diff --git a/spec/support/mailer_macros.rb b/spec/support/mailer_macros.rb new file mode 100644 index 0000000..effba21 --- /dev/null +++ b/spec/support/mailer_macros.rb @@ -0,0 +1,9 @@ +module MailerMacros + def last_email + ActionMailer::Base.deliveries.last + end + + def reset_email + ActionMailer::Base.deliveries = [] + end +end