Second task of GSoC

Reading time ~5 minutes

Sending Email to Event participants

My second task in GSoC was to implement a feature to send emails to event participants. This feature included selecting the email recipients on the basis of state of their requests (request submitted by a user) i.e. submitted, approved, accepted, etc.

This feature is important because it provides TSP members a facility to easily notify the users participating in an event. Also the option of selecting users on the basis of their request state saves a lot of time of the members. This feature covers many requirements of the TSP members such as, sending deadline emails, information about changed date and timings, informing the participants about exciting rounds in an event,etc.

This feature was requested by KDE e.V. Travel Cost Reimbursement initiative .

How the Feature looks and its implementation

This feature provides three views: index, new and show. Lets see how the actions for these views are defined in the controller.

class EventEmailsController < InheritedResources::Base
  actions :all, except: [:edit, :update, :destroy]
  belongs_to :event

  def create
    @event_email.user = current_user
    create!(notice: 'Email Delivered')
    return unless @event_email.errors.empty?
    @event_email[:to].split(',').each do |e|
      user = User.find_by(email: e)
      EventMailer.notify_to(user, :event_info, @event_email)
    end
  end

  protected

  def permitted_params
    params.permit(event_email: [:to, :subject, :body])
  end
end

Great, you noticed that we have not defined the index, new and show action in the controller as we are inheriting from InheritedResources::Base which inherits all the restful actions for us. So why we have defined the create action? Because we want to call the Event mailer in it to send the mails. But lets just come back to this mailer part in a minute.

Lets have a look at the Email new view

This is the Email New view

As we can see a dropdown is provided to select the recipients for the To field with the states as its options as requested in the feature.

So how to implement this?

Creating a method in the Events helper to return email ids of the users.

def users_for_event(state)
  req = @event.requests
  req = req.where(state: state) if state != 'all'
  user_email = req.map { |e| e.user.email }.uniq
end

But can this method insert the values in the To field ? No Of course not, so lets write some javascript coffeescript for this.

states = (state) ->
  $('#state'+state).click ->
    users = $('#state'+state).data('users')
    $('#event_email_to').val(users)
    if users.length == 0
      $('#event_email_to').attr('placeholder', 'No recipients present for ' + state + ' state')

states state for state in ['All', 'Accepted', 'Incomplete', 'Submitted', 'Approved', 'Cancel']

You noticed that we are using data-attributes for the values here, you must be using sharingan ;) So why we have not written inline javascript here which might have been easy? Because the rails guide suggests to use Unobtrusive JavaScript, and we cannot go against the rails guide, can we ?

So what does the above code do: we have defined a function which adds a click event listener to the links of the dropdown and when a user clicks the link the data is fetched from the data-users attribute and filled in the To field and if their are no recipients present it adds a placeholder to indicate that.

Enough talk about the To field lets have a look at the body field, sending unformatted text in a mail might not look good so we have added a markdown functionality to the body field, cool right :) . You can easily do this with the help of the Redcarpet gem.

Before going to the mailer portion lets have a quick look at the other two views show and index.

This is the Email Show view

Show view provides all the details of an email sent.

This is the Email index view

Index view provides a list of all the emails sent with some attributes like name of the sender and email sent time.

Time to throw some light on the mailer

Creating a mailer in rails is very easy as it is pretty much like creating a controller. Lets have a look at the Event mailer used in this feature

class EventMailer < ApplicationMailer
  def event_info(to, email)
    @email = email
    mail(from: @email.user.email,
         to: to,
         subject: @email[:subject])
  end
end

We can use this event_info action to send our mails. But did you notice that above in the controller create action we are not calling this method instead calling some other method notify_to. Which method is this and why are we using this? We have defined this method in the ApplicationMailer for some extra features which you can have a look at here. But the important part of using it here is to use delayed jobs. As it will save us the time of waiting for the external mail service to send the mail.

We can’t finish a feature without tests, so lets write some tests for it.

Tests for the feature

To test the delivery of the mails we can track ActionMailer::Base.deliveries.size and check its value after we are finish sending the mails. Have a look at the test file for this feature. We are testing with the help of rspec. Using capybara, capybara-webkit etc.

Tests for the Event Mailer

require 'spec_helper'

describe Event, type: :mailer do
  fixtures :all
  describe 'Event Info' do
    let(:mail) { EventMailer.event_info(event_emails(:party_info).to, event_emails(:party_info)).deliver }

    it 'renders the headers' do
      expect(mail.subject).to eq('Testing mail')
      expect(mail.to).to eq(['test@example.com'])
    end

    it 'renders the body' do
      expect(mail.body.encoded).to match('This is a test mail')
    end
  end
end

In the above feature tests we checked for if we are able to create our mails and that they are getting delivered. In the mailer specs we check that if our mail is send to the correct person and that it has the correct subject and body. In the above test we are testing event_info method of the Event Mailer using fixtures. We can check the value of fields easily with the help of expect().to and eq. It is similar to writing the model specs.

Testing using Letter Opener

A fast and efficient option to test your email. Letter Opener helps you in previewing the email in the browser instead of sending it. Which means you don’t need to set up email delivery in your development environment.

Steps for using letter opener

  • Add the letter_opener gem in the Gemfile
    gem "letter_opener", :group => :development
    
  • Then set the delivery method in config/environments/development.rb
      config.action_mailer.delivery_method = :letter_opener
    

For the implementation of this feature you can have a look at the following PR’s:

Thanks to Björn Geuken and Christian Buckmayer for helping me in this task.

To know about Travel Support Program visit TSP

GSoC product submission

Details about new features added in TSP during GSoC Continue reading

GSoC Second evaluation

Published on July 05, 2017

Migrate to the latest version of Bootstrap

Published on June 05, 2017