smailer 0.7.8 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/CHANGELOG.md +202 -0
- data/Gemfile +4 -3
- data/Gemfile.lock +16 -12
- data/README.md +107 -73
- data/generators/smailer/templates/migration.rb.erb +65 -47
- data/lib/generators/smailer/templates/migration.rb.erb +65 -47
- data/lib/smailer/models.rb +3 -2
- data/lib/smailer/models/{mail_campaign_attachment.rb → mail_attachment.rb} +3 -5
- data/lib/smailer/models/mail_campaign.rb +15 -4
- data/lib/smailer/models/mail_template.rb +13 -0
- data/lib/smailer/models/queued_mail.rb +51 -7
- data/lib/smailer/tasks/send.rb +2 -2
- data/lib/smailer/version.rb +2 -2
- data/setup-test-db +14 -0
- data/spec/factories/common.rb +4 -0
- data/spec/factories/models.rb +26 -0
- data/spec/features/issuing_a_newsletter_spec.rb +52 -0
- data/spec/features/sending_oneoff_emails.rb +42 -0
- data/spec/models/mail_campaign_spec.rb +28 -0
- data/spec/models/queued_mail_spec.rb +114 -0
- data/spec/spec_helper.rb +22 -4
- data/upgrading/migrations/smailer_v0_7_3_to_v0_8_0.rb +106 -0
- metadata +13 -6
- data/Guardfile +0 -14
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/models/mail_key_spec.rb +0 -18
data/lib/smailer/tasks/send.rb
CHANGED
@@ -50,7 +50,7 @@ module Smailer
|
|
50
50
|
from queue_item.from
|
51
51
|
to queue_item.to
|
52
52
|
subject queue_item.subject
|
53
|
-
queue_item.
|
53
|
+
queue_item.attachments.each do |attachment|
|
54
54
|
cached_attachments[attachment.id] ||= attachment.body
|
55
55
|
add_file :filename => attachment.filename,
|
56
56
|
:content => cached_attachments[attachment.id]
|
@@ -114,4 +114,4 @@ module Smailer
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
|
-
end
|
117
|
+
end
|
data/lib/smailer/version.rb
CHANGED
data/setup-test-db
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :mailing_list, class: Smailer::Models::MailingList do
|
3
|
+
name { "Mailing List #{generate(:name)}" }
|
4
|
+
end
|
5
|
+
|
6
|
+
factory :mail_template, class: Smailer::Models::MailTemplate do
|
7
|
+
from { generate(:email) }
|
8
|
+
subject 'Hi there'
|
9
|
+
body_html '<h1>Hi there</h1>'
|
10
|
+
body_text 'Hi there'
|
11
|
+
end
|
12
|
+
|
13
|
+
factory :mail_campaign, class: Smailer::Models::MailCampaign do
|
14
|
+
association :mailing_list
|
15
|
+
|
16
|
+
after(:build) do |mail_campaign|
|
17
|
+
mail_campaign.mail_template ||= FactoryGirl.build(:mail_template)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
factory :queued_mail, class: Smailer::Models::QueuedMail do
|
22
|
+
association :mail_campaign
|
23
|
+
|
24
|
+
to { generate(:email) }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Issuing a newsletter' do
|
4
|
+
describe 'the example from the readme' do
|
5
|
+
before do
|
6
|
+
# Required setup not from the readme
|
7
|
+
FactoryGirl.create(:mailing_list)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'works as expected' do
|
11
|
+
# locate the mailing list we'll be sending to
|
12
|
+
list = Smailer::Models::MailingList.first
|
13
|
+
|
14
|
+
# create a corresponding mail campaign
|
15
|
+
campaign_params = {
|
16
|
+
:from => 'noreply@example.org',
|
17
|
+
:subject => 'My First Campaign!',
|
18
|
+
:body_html => '<h1>Hello</h1><p>World</p>',
|
19
|
+
:body_text => 'Hello, world!',
|
20
|
+
:mailing_list_id => list.id,
|
21
|
+
}
|
22
|
+
campaign = Smailer::Models::MailCampaign.new campaign_params
|
23
|
+
campaign.add_unsubscribe_method :all
|
24
|
+
|
25
|
+
# Add attachments
|
26
|
+
campaign.add_attachment 'attachment.pdf', 'url_or_file_path_to_attachment'
|
27
|
+
|
28
|
+
campaign.save!
|
29
|
+
|
30
|
+
# enqueue mails to be sent out
|
31
|
+
subscribers = %w[
|
32
|
+
subscriber@domain.com
|
33
|
+
office@company.com
|
34
|
+
contact@store.com
|
35
|
+
]
|
36
|
+
subscribers.each do |subscriber|
|
37
|
+
campaign.queued_mails.create! :to => subscriber
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(Smailer::Models::MailCampaign.count).to eq(1)
|
41
|
+
expect(Smailer::Models::MailTemplate.count).to eq(1)
|
42
|
+
expect(Smailer::Models::MailAttachment.count).to eq(1)
|
43
|
+
expect(Smailer::Models::QueuedMail.count).to eq(3)
|
44
|
+
|
45
|
+
expect(campaign.from).to eq(campaign_params[:from])
|
46
|
+
expect(campaign.subject).to eq(campaign_params[:subject])
|
47
|
+
expect(campaign.body_html).to eq(campaign_params[:body_html])
|
48
|
+
expect(campaign.body_text).to eq(campaign_params[:body_text])
|
49
|
+
expect(campaign.mailing_list_id).to eq(campaign_params[:mailing_list_id])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Sending one-off emails' do
|
4
|
+
describe 'the example from the readme' do
|
5
|
+
before do
|
6
|
+
# Required setup not from the readme
|
7
|
+
FactoryGirl.create(:mail_campaign)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'works as expected' do
|
11
|
+
campaign = Smailer::Models::MailCampaign.first
|
12
|
+
|
13
|
+
# The mail template is copied from the campaign and then you make you changes
|
14
|
+
# e.g. here the subject and from are copied from the campaign
|
15
|
+
campaign.queued_mails.create! :to => 'subscriber@domain.com', :body_html => '<h1>my custom body</h1>', :body_text => 'my custom body'
|
16
|
+
|
17
|
+
# if you change the campaign now it won't change the one-off queued_mails
|
18
|
+
|
19
|
+
# sending two mails to the same person
|
20
|
+
campaign.queued_mails.create! :to => 'subscriber@domain.com', :body_html => '<h1>second custom body</h1>', :body_text => 'second custom body', require_uniqueness => false
|
21
|
+
|
22
|
+
expect(Smailer::Models::MailCampaign.count).to eq(1)
|
23
|
+
expect(Smailer::Models::MailTemplate.count).to eq(3) # 1 for the campaign and 2 for the one-off emails
|
24
|
+
expect(Smailer::Models::MailAttachment.count).to eq(0)
|
25
|
+
expect(Smailer::Models::QueuedMail.count).to eq(2)
|
26
|
+
|
27
|
+
first_mail = campaign.queued_mails.first
|
28
|
+
|
29
|
+
expect(first_mail.from).to eq(campaign.from)
|
30
|
+
expect(first_mail.subject).to eq(campaign.subject)
|
31
|
+
expect(first_mail.body_html).to eq('<h1>my custom body</h1>')
|
32
|
+
expect(first_mail.body_text).to eq('my custom body')
|
33
|
+
|
34
|
+
second_mail = campaign.queued_mails.last
|
35
|
+
|
36
|
+
expect(second_mail.from).to eq(campaign_params.from)
|
37
|
+
expect(second_mail.subject).to eq(campaign_params.subject)
|
38
|
+
expect(second_mail.body_html).to eq('<h1>second custom body</h1>')
|
39
|
+
expect(second_mail.body_text).to eq('second custom body')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Smailer::Models::MailCampaign do
|
4
|
+
describe '#save' do
|
5
|
+
it 'works as expected' do
|
6
|
+
mail_campaign = Smailer::Models::MailCampaign.new
|
7
|
+
|
8
|
+
mail_campaign.mailing_list = FactoryGirl.create(:mailing_list)
|
9
|
+
|
10
|
+
mail_campaign.from = '"test" <test@example.com>'
|
11
|
+
mail_campaign.subject = 'This is my test'
|
12
|
+
mail_campaign.body_html = 'Hello html'
|
13
|
+
mail_campaign.body_text = 'Hello text'
|
14
|
+
|
15
|
+
mail_campaign.add_attachment 'foo.pdf', '/tmp/foo.pdf'
|
16
|
+
|
17
|
+
expect(Smailer::Models::MailCampaign.count).to eq(0)
|
18
|
+
expect(Smailer::Models::MailTemplate.count).to eq(0)
|
19
|
+
expect(Smailer::Models::MailAttachment.count).to eq(0)
|
20
|
+
|
21
|
+
mail_campaign.save!
|
22
|
+
|
23
|
+
expect(Smailer::Models::MailCampaign.count).to eq(1)
|
24
|
+
expect(Smailer::Models::MailTemplate.count).to eq(1)
|
25
|
+
expect(Smailer::Models::MailAttachment.count).to eq(1)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Smailer::Models::QueuedMail do
|
4
|
+
describe '#save' do
|
5
|
+
it 'could be made only with mail_campaign and to (recipient)' do
|
6
|
+
mail_campaign = FactoryGirl.create(:mail_campaign)
|
7
|
+
|
8
|
+
queued_mail = Smailer::Models::QueuedMail.new
|
9
|
+
|
10
|
+
queued_mail.mail_campaign = mail_campaign
|
11
|
+
queued_mail.to = 'test@example.com'
|
12
|
+
|
13
|
+
expect(Smailer::Models::MailCampaign.count).to eq(1)
|
14
|
+
expect(Smailer::Models::MailTemplate.count).to eq(1)
|
15
|
+
expect(Smailer::Models::QueuedMail.count).to eq(0)
|
16
|
+
|
17
|
+
queued_mail.save!
|
18
|
+
|
19
|
+
expect(Smailer::Models::MailCampaign.count).to eq(1)
|
20
|
+
expect(Smailer::Models::MailTemplate.count).to eq(1)
|
21
|
+
expect(Smailer::Models::QueuedMail.count).to eq(1)
|
22
|
+
|
23
|
+
queued_mail.reload
|
24
|
+
|
25
|
+
expect(queued_mail.to).to eq('test@example.com')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'would create its own template if you change any of the template attributes and will copy the mail_campaign template' do
|
29
|
+
mail_campaign = FactoryGirl.create(:mail_campaign)
|
30
|
+
mail_campaign.add_attachment 'foo.pdf', '/tmp/foo.pdf'
|
31
|
+
mail_campaign.save!
|
32
|
+
|
33
|
+
queued_mail = Smailer::Models::QueuedMail.new
|
34
|
+
|
35
|
+
queued_mail.mail_campaign = mail_campaign
|
36
|
+
queued_mail.to = 'text@example.com'
|
37
|
+
|
38
|
+
queued_mail.from = 'sender@example.com'
|
39
|
+
|
40
|
+
queued_mail.save!
|
41
|
+
|
42
|
+
mail_campaign.reload
|
43
|
+
queued_mail.reload
|
44
|
+
|
45
|
+
expect(queued_mail.from).to eq('sender@example.com')
|
46
|
+
expect(queued_mail.body_html).to eq(mail_campaign.body_html)
|
47
|
+
expect(queued_mail.body_text).to eq(mail_campaign.body_text)
|
48
|
+
expect(queued_mail.subject).to eq(mail_campaign.subject)
|
49
|
+
|
50
|
+
expect(queued_mail.attachments).to be_present
|
51
|
+
expect(mail_campaign.attachments).to be_present
|
52
|
+
expect(queued_mail.attachments.first.path).to eq(mail_campaign.attachments.first.path)
|
53
|
+
expect(queued_mail.attachments.first.filename).to eq(mail_campaign.attachments.first.filename)
|
54
|
+
|
55
|
+
expect(queued_mail.mail_template).to_not eq(mail_campaign.mail_template)
|
56
|
+
expect(queued_mail.attachments.first).to_not eq(mail_campaign.attachments.first)
|
57
|
+
|
58
|
+
expect(Smailer::Models::MailCampaign.count).to eq(1)
|
59
|
+
expect(Smailer::Models::MailTemplate.count).to eq(2)
|
60
|
+
expect(Smailer::Models::MailAttachment.count).to eq(2)
|
61
|
+
expect(Smailer::Models::QueuedMail.count).to eq(1)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#key' do
|
66
|
+
it 'is unique per email' do
|
67
|
+
mail_campaign = FactoryGirl.create(:mail_campaign)
|
68
|
+
|
69
|
+
queued_mail_1 = Smailer::Models::QueuedMail.new
|
70
|
+
queued_mail_1.mail_campaign = mail_campaign
|
71
|
+
queued_mail_1.to = 'text@example.com'
|
72
|
+
|
73
|
+
queued_mail_2 = Smailer::Models::QueuedMail.new
|
74
|
+
queued_mail_2.mail_campaign = mail_campaign
|
75
|
+
queued_mail_2.to = 'text@example.com'
|
76
|
+
|
77
|
+
expect(queued_mail_1.key).to_not eq(queued_mail_2.key)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#require_uniqueness' do
|
82
|
+
it 'is required by default' do
|
83
|
+
mail_campaign = FactoryGirl.create(:mail_campaign)
|
84
|
+
|
85
|
+
queued_mail_1 = Smailer::Models::QueuedMail.new
|
86
|
+
queued_mail_1.mail_campaign = mail_campaign
|
87
|
+
queued_mail_1.to = 'text@example.com'
|
88
|
+
queued_mail_1.save!
|
89
|
+
|
90
|
+
queued_mail_2 = Smailer::Models::QueuedMail.new
|
91
|
+
queued_mail_2.mail_campaign = mail_campaign
|
92
|
+
queued_mail_2.to = 'text@example.com'
|
93
|
+
|
94
|
+
expect(queued_mail_2.save).to eq(false)
|
95
|
+
expect(queued_mail_2.errors[:to]).to be_present
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'could be turned off by setting it to false' do
|
99
|
+
mail_campaign = FactoryGirl.create(:mail_campaign)
|
100
|
+
|
101
|
+
queued_mail_1 = Smailer::Models::QueuedMail.new
|
102
|
+
queued_mail_1.mail_campaign = mail_campaign
|
103
|
+
queued_mail_1.to = 'text@example.com'
|
104
|
+
queued_mail_1.save!
|
105
|
+
|
106
|
+
queued_mail_2 = Smailer::Models::QueuedMail.new
|
107
|
+
queued_mail_2.mail_campaign = mail_campaign
|
108
|
+
queued_mail_2.to = 'text@example.com'
|
109
|
+
queued_mail_2.require_uniqueness = false
|
110
|
+
|
111
|
+
expect(queued_mail_2.save).to eq(true)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,6 +11,9 @@ Spork.prefork do
|
|
11
11
|
# in spec/support/ and its subdirectories.
|
12
12
|
Dir[File.expand_path('../../spec/support/**/*.rb', __FILE__)].each {|f| require f }
|
13
13
|
|
14
|
+
FactoryGirl.definition_file_paths = [File.expand_path('../../spec/factories/', __FILE__)]
|
15
|
+
FactoryGirl.find_definitions
|
16
|
+
|
14
17
|
# Checks for pending migrations before tests are run.
|
15
18
|
# If you are not using ActiveRecord, you can remove this line.
|
16
19
|
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
|
@@ -43,11 +46,26 @@ Spork.prefork do
|
|
43
46
|
# --seed 1234
|
44
47
|
config.order = "random"
|
45
48
|
|
46
|
-
config.before(:suite)
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
config.before(:suite) do
|
50
|
+
DatabaseCleaner.clean_with(:truncation)
|
51
|
+
|
52
|
+
begin
|
53
|
+
DatabaseCleaner.start
|
54
|
+
FactoryGirl.lint
|
55
|
+
ensure
|
56
|
+
DatabaseCleaner.clean
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
config.before(:each) do
|
61
|
+
DatabaseCleaner.strategy = :transaction
|
62
|
+
|
63
|
+
DatabaseCleaner.start
|
64
|
+
end
|
50
65
|
|
66
|
+
config.after(:each) do
|
67
|
+
DatabaseCleaner.clean
|
68
|
+
end
|
51
69
|
end
|
52
70
|
end
|
53
71
|
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class SmailerV073ToV080 < ActiveRecord::Migration
|
2
|
+
class MailCampaign < ActiveRecord::Base
|
3
|
+
has_one :mail_template
|
4
|
+
has_many :mail_attachments
|
5
|
+
end
|
6
|
+
|
7
|
+
class MailTemplate < ActiveRecord::Base
|
8
|
+
belongs_to :mail_campaign
|
9
|
+
belongs_to :queued_mail, :dependent => :destroy
|
10
|
+
|
11
|
+
has_many :mail_attachments
|
12
|
+
end
|
13
|
+
|
14
|
+
class MailAttachment < ActiveRecord::Base
|
15
|
+
belongs_to :mail_campaign
|
16
|
+
belongs_to :mail_template
|
17
|
+
end
|
18
|
+
|
19
|
+
class QueuedMail < ActiveRecord::Base
|
20
|
+
has_one :queued_mail
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.up
|
24
|
+
create_table :mail_templates do |t|
|
25
|
+
t.references :mail_campaign
|
26
|
+
t.references :queued_mail
|
27
|
+
|
28
|
+
t.string :from
|
29
|
+
t.string :subject
|
30
|
+
t.text :body_html
|
31
|
+
t.text :body_text
|
32
|
+
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
|
36
|
+
add_index :mail_templates, :mail_campaign_id
|
37
|
+
add_index :mail_templates, :queued_mail_id
|
38
|
+
|
39
|
+
rename_table :mail_campaign_attachments, :mail_attachments
|
40
|
+
|
41
|
+
add_column :mail_attachments, :mail_template_id, :integer
|
42
|
+
add_index :mail_attachments, :mail_template_id
|
43
|
+
|
44
|
+
MailCampaign.all.each do |mail_campaign|
|
45
|
+
mail_template = MailTemplate.new
|
46
|
+
|
47
|
+
mail_template.mail_campaign = mail_campaign
|
48
|
+
mail_template.mail_attachments = mail_campaign.mail_attachments
|
49
|
+
mail_template.from = mail_campaign.from
|
50
|
+
mail_template.subject = mail_campaign.subject
|
51
|
+
mail_template.body_html = mail_campaign.body_html
|
52
|
+
mail_template.body_text = mail_campaign.body_text
|
53
|
+
|
54
|
+
mail_template.save!
|
55
|
+
end
|
56
|
+
|
57
|
+
remove_column :mail_attachments, :mail_campaign_id
|
58
|
+
|
59
|
+
remove_column :mail_campaigns, :from
|
60
|
+
remove_column :mail_campaigns, :subject
|
61
|
+
remove_column :mail_campaigns, :body_html
|
62
|
+
remove_column :mail_campaigns, :body_text
|
63
|
+
|
64
|
+
add_column :queued_mails, :require_uniqueness, :boolean, :default => true
|
65
|
+
|
66
|
+
remove_index :queued_mails, :name => 'index_queued_mails_on_mail_campain_id_and_to'
|
67
|
+
add_index :queued_mails, [:mail_campaign_id, :to, :require_uniqueness], :name => 'index_queued_mails_uniqueness_for_to', :unique => true
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.down
|
71
|
+
remove_index :queued_mails, :name => 'index_queued_mails_uniqueness_for_to'
|
72
|
+
add_index :queued_mails, [:mail_campaign_id, :to], :name => 'index_queued_mails_on_mail_campain_id_and_to', :unique => true
|
73
|
+
|
74
|
+
QueuedMail.destroy_all('require_uniqueness is null')
|
75
|
+
|
76
|
+
remove_column :queued_mails, :require_uniqueness
|
77
|
+
|
78
|
+
MailTemplate.destroy_all('queued_mail_id is not null')
|
79
|
+
|
80
|
+
add_column :mail_campaigns, :from, :string
|
81
|
+
add_column :mail_campaigns, :subject, :string
|
82
|
+
add_column :mail_campaigns, :body_html, :text
|
83
|
+
add_column :mail_campaigns, :body_text, :text
|
84
|
+
|
85
|
+
add_column :mail_attachments, :mail_campaign_id, :integer
|
86
|
+
add_index :mail_attachments, :mail_campaign_id
|
87
|
+
|
88
|
+
MailCampaign.all.each do |mail_campaign|
|
89
|
+
mail_template = mail_campaign.mail_template
|
90
|
+
|
91
|
+
mail_campaign.mail_attachments = mail_template.mail_attachments
|
92
|
+
|
93
|
+
mail_campaign.from = mail_template.from
|
94
|
+
mail_campaign.subject = mail_template.subject
|
95
|
+
mail_campaign.body_html = mail_template.body_html
|
96
|
+
mail_campaign.body_text = mail_template.body_text
|
97
|
+
|
98
|
+
mail_campaign.save!
|
99
|
+
end
|
100
|
+
|
101
|
+
remove_column :mail_attachments, :mail_template_id
|
102
|
+
rename_table :mail_attachments, :mail_campaign_attachments
|
103
|
+
|
104
|
+
drop_table :mail_templates
|
105
|
+
end
|
106
|
+
end
|