Skip to content

Instantly share code, notes, and snippets.

@spencerldixon
Last active August 19, 2019 20:14
Show Gist options
  • Save spencerldixon/8fcf317aca9c72114de19ae8ad7d6d60 to your computer and use it in GitHub Desktop.
Save spencerldixon/8fcf317aca9c72114de19ae8ad7d6d60 to your computer and use it in GitHub Desktop.
Robust CSV Importer
class CsvImporterJob < ApplicationJob
queue_as :default
# Handle any cleanup after perform
after_perform { |job| clean_up_csv_upload(job) }
def perform(file, user)
# Use begin/rescue - raise StandardError in importer if something goes wrong
# Rescue here with email notification to user
begin
CsvImporter.import(file, user)
NotificationMailer.csv_import_successful(user).deliver_later
rescue StandardError => error
NotificationMailer.csv_import_failed(user, error.message).deliver_later
end
end
def clean_up_csv_upload(job)
# Clean up the file from ActiveStorage
csv_upload = job.arguments.first
csv_upload.destroy
end
end
@spencerldixon
Copy link
Author

spencerldixon commented May 16, 2019

Ensure csv upload model purges on destroy...

class CsvUpload < ApplicationRecord
  belongs_to :user
  has_one_attached :file

  before_destroy :purge_attachment

  def purge_attachment
    self.file.purge
  end
end

@spencerldixon
Copy link
Author

Tests for this Job...

require 'rails_helper'

RSpec.describe CsvImporterJob, type: :job do
  include ActiveJob::TestHelper

  let!(:user)             { FactoryBot.create(:user) }
  let!(:csv_upload)  { FactoryBot.create(:csv_upload, user: user) }

  subject(:job) { described_class.perform_later(csv_upload) }

  describe '.perform' do
    it 'calls CsvImporter.import' do
      expect(CsvImporter).to receive(:import).with(csv_upload)
      perform_enqueued_jobs { job }
    end

    context 'when successful' do
      it 'sends csv_import_successful mailer' do
        expect_any_instance_of(NotificationMailer).to receive(:csv_import_successful).with(user)
        allow(CsvImporter).to receive(:import).with(csv_upload)
        perform_enqueued_jobs { job }
      end
    end

    context 'when failed' do
      it 'sends csv_import_failed mailer' do
        expect_any_instance_of(NotificationMailer).to receive(:csv_import_failed).with(user, 'oh no!')
        allow(CsvImporter).to receive(:import).with(csv_upload).and_raise(StandardError, 'oh no!')
        perform_enqueued_jobs { job }
      end
    end
  end

  describe 'callbacks' do
    describe 'after_perform' do
      it 'deletes the CsvUpload' do
        expect {
          allow(CsvImporter).to receive(:import).with(csv_upload)
          perform_enqueued_jobs { job }
        }.to change(CsvUpload, :count).by(-1)
      end
    end
  end

  after do
    clear_enqueued_jobs
    clear_performed_jobs
  end
end

@spencerldixon
Copy link
Author

And for mailers...

require 'rails_helper'

RSpec.describe NotificationMailer, type: :mailer do
  let(:user) { FactoryBot.create(:user) }

  describe '#csv_import_successful' do
    let(:mail) { NotificationMailer.csv_import_successful(user) }

    it 'renders the headers' do
      expect(mail.subject).to eq('Import successful')
      expect(mail.to).to eq([user.email])
    end

    it 'renders the body' do
      expect(mail.body.encoded).to match('Your csv has been imported!')
    end
  end

  describe '#csv_import_failed' do
    let(:mail) { NotificationMailer.csv_import_failed(user, 'error message') }

    it 'renders the headers' do
      expect(mail.subject).to eq('Import failed')
      expect(mail.to).to eq([user.email])
    end

    it 'renders the body' do
      expect(mail.body.encoded).to match('Something went wrong')
    end
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment