mail_spy 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -7
- data/app/helpers/mail_spy/email_helper.rb +17 -3
- data/app/mailers/mail_spy/core_mailer.rb +11 -8
- data/app/models/mail_spy/email.rb +33 -14
- data/lib/generators/mail_spy/templates/mail_spy.rb +17 -1
- data/lib/mail_spy/manager.rb +9 -24
- data/lib/mail_spy/version.rb +1 -1
- data/lib/mail_spy.rb +29 -4
- data/test/dummy/app/views/email_templates/helper_test.html.erb +15 -0
- data/test/dummy/app/views/email_templates/helper_test.text.erb +11 -0
- data/test/dummy/config/environments/development.rb +1 -1
- data/test/dummy/config/environments/production.rb +5 -1
- data/test/dummy/config/environments/test.rb +4 -1
- data/test/dummy/config/initializers/mail_spy.rb +38 -0
- data/test/dummy/config/routes.rb +0 -1
- data/test/dummy/lib/tasks/emails.rake +8 -0
- data/test/dummy/log/development.log +12 -0
- data/test/dummy/log/test.log +3545 -0
- data/test/functional/mail_spy/core_mailer_test.rb +16 -9
- data/test/functional/mail_spy/tracking_controller_test.rb +1 -9
- data/test/test_email_credentials.yml +2 -2
- data/test/test_email_credentials.yml.sample +0 -8
- data/test/test_helper.rb +15 -41
- data/test/unit/mail_spy/manager_test.rb +91 -125
- metadata +37 -23
- data/app/models/mail_spy/email_template.rb +0 -17
- data/test/fixtures/mail_spy/email_templates.yml +0 -9
- data/test/unit/mail_spy/email_template_test.rb +0 -9
data/README.rdoc
CHANGED
@@ -20,6 +20,7 @@ Create the mongoid configuration for the mongo db
|
|
20
20
|
rails g mail_spy:initializer
|
21
21
|
|
22
22
|
Fill out the generated mailspy.rb with your esp settings (file has example)
|
23
|
+
and AWS settings
|
23
24
|
|
24
25
|
Then mount the engine in the routes.rb file
|
25
26
|
Rails.application.routes.draw do
|
@@ -32,13 +33,7 @@ Mailspy is centered around email templates and instances of those templates
|
|
32
33
|
(aka emails).
|
33
34
|
|
34
35
|
To create a template:
|
35
|
-
|
36
|
-
MailSpy.create_template(template_name, html_erb, text_erb, template_values_definition)
|
37
|
-
|
38
|
-
* Template name must be unique to all templates
|
39
|
-
* html, and text are the contents for a multipart email
|
40
|
-
* template_values_definition is a array of keys that should be present in the
|
41
|
-
template values of each hash
|
36
|
+
add a template to your s3 directory, campaign/stream/component.(text|html).erb
|
42
37
|
|
43
38
|
|
44
39
|
To create a instance:
|
@@ -21,8 +21,15 @@ module MailSpy
|
|
21
21
|
tag_options = tag_options(html_options)
|
22
22
|
|
23
23
|
# Inject our tracking url, and pass in the redirect_url
|
24
|
-
url = url_for(
|
25
|
-
|
24
|
+
url = url_for(
|
25
|
+
:controller => "mail_spy/tracking",
|
26
|
+
:action => :link,
|
27
|
+
:url => url,
|
28
|
+
:only_path => false,
|
29
|
+
:host => MailSpy.tracker_host,
|
30
|
+
:n => @_track_count,
|
31
|
+
:eid => @_email_id
|
32
|
+
)
|
26
33
|
|
27
34
|
|
28
35
|
href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href
|
@@ -33,7 +40,14 @@ module MailSpy
|
|
33
40
|
|
34
41
|
# Support for open tracking, client support, etc
|
35
42
|
def tracking_bug
|
36
|
-
|
43
|
+
url = url_for(
|
44
|
+
:controller => "mail_spy/tracking",
|
45
|
+
:action => :bug,
|
46
|
+
:eid => @_email_id,
|
47
|
+
:only_path => false,
|
48
|
+
:host => MailSpy.tracker_host
|
49
|
+
)
|
50
|
+
"<img src='#{url}' style='display:none' width='1' height='1' border='0' />".html_safe
|
37
51
|
end
|
38
52
|
|
39
53
|
end
|
@@ -6,20 +6,23 @@ module MailSpy
|
|
6
6
|
# Slight hack to provide information to helpers
|
7
7
|
@_email_id = email.id
|
8
8
|
@template_values = email.template_values
|
9
|
+
email_hash = {}
|
10
|
+
|
11
|
+
|
12
|
+
# Evaluate the subject line as erb
|
13
|
+
if email.subject.present?
|
14
|
+
subject = ERB.new(email.subject).result(binding)
|
15
|
+
email_hash[:subject] = subject
|
16
|
+
end
|
9
17
|
|
10
18
|
# Set Headers
|
11
|
-
|
12
|
-
std_email_keys = [:to, :cc, :bcc, :from, :subject, :message_id, :sender, :reply_to]
|
19
|
+
std_email_keys = [:to, :cc, :bcc, :from, :message_id, :sender, :reply_to]
|
13
20
|
std_email_keys.each { |key| set_if_present(email_hash,email, key) }
|
14
21
|
headers.merge!(email.headers) if email.headers.present?
|
15
22
|
|
16
|
-
# Content of the email
|
17
|
-
html_erb = email.email_template.html_erb || ""
|
18
|
-
text_erb = email.email_template.html_erb || ""
|
19
|
-
|
20
23
|
mail_message = mail(email_hash) do |format|
|
21
|
-
format.text { render :inline => text_erb }
|
22
|
-
format.html { render :inline => html_erb }
|
24
|
+
format.text { render :inline => email.text_erb }
|
25
|
+
format.html { render :inline => email.html_erb }
|
23
26
|
end
|
24
27
|
|
25
28
|
# Email Service provider setup
|
@@ -3,6 +3,8 @@ module MailSpy
|
|
3
3
|
include Mongoid::Document
|
4
4
|
include Mongoid::Timestamps
|
5
5
|
|
6
|
+
@@template_cache = {}
|
7
|
+
|
6
8
|
# Standard Email options
|
7
9
|
field :to
|
8
10
|
field :cc
|
@@ -15,9 +17,7 @@ module MailSpy
|
|
15
17
|
field :message_id #Sets the unique id for each email
|
16
18
|
|
17
19
|
# Email content
|
18
|
-
field :template_name, :type => String
|
19
20
|
field :template_values, :type => Hash
|
20
|
-
belongs_to :email_template, :class_name => 'MailSpy::EmailTemplate'
|
21
21
|
|
22
22
|
# Support for tracking which esp ran the email
|
23
23
|
field :email_service_provider, :type => String
|
@@ -43,18 +43,6 @@ module MailSpy
|
|
43
43
|
self.read_attribute(:template_values).try(:with_indifferent_access)
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
def email_parts
|
48
|
-
template = self.email_template
|
49
|
-
html_erb = template.html_erb
|
50
|
-
text_erb = template.text_erb
|
51
|
-
mail = MailSpy::DummyMailer.template(html_erb, text_erb, self.template_values)
|
52
|
-
return {
|
53
|
-
:html => mail.html_part.body.to_s,
|
54
|
-
:text => mail.text_part.body.to_s
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
46
|
# Great for debugging emails
|
59
47
|
def parsed_html_content
|
60
48
|
return MailSpy::CoreMailer.template(self).text_part.body.to_s
|
@@ -64,6 +52,37 @@ module MailSpy
|
|
64
52
|
return MailSpy::CoreMailer.template(self).html_part.body.to_s
|
65
53
|
end
|
66
54
|
|
55
|
+
def html_erb
|
56
|
+
load_template("html.erb")
|
57
|
+
end
|
58
|
+
|
59
|
+
def text_erb
|
60
|
+
load_template("text.erb")
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# Loads a template file from s3 using
|
66
|
+
def load_template(suffix)
|
67
|
+
raise "No aws_campaign_bucket_set" if MailSpy.aws_campaign_bucket.strip.blank?
|
68
|
+
|
69
|
+
# Check the cache using our convention
|
70
|
+
path = "#{self.campaign}/#{self.stream}/#{self.component}.#{suffix}"
|
71
|
+
return @@template_cache[path] if @@template_cache[path].present?
|
72
|
+
|
73
|
+
# Load the object from s3
|
74
|
+
s3 = AWS::S3.new(:access_key_id => MailSpy.aws_access_key_id,
|
75
|
+
:secret_access_key => MailSpy.aws_secret_access_key)
|
76
|
+
campaign_bucket = s3.buckets[MailSpy.aws_campaign_bucket]
|
77
|
+
campaign_bucket = buckets.create(MailSpy.aws_campaign_bucket) unless campaign_bucket.exists?
|
78
|
+
object = campaign_bucket.objects[path]
|
79
|
+
raise "no object found at path: #{path}" unless object.exists?
|
80
|
+
|
81
|
+
# Read and return
|
82
|
+
@@template_cache[path] = (object.read || "")
|
83
|
+
return @@template_cache[path]
|
84
|
+
end
|
85
|
+
|
67
86
|
|
68
87
|
def self.generate_campaign_stats
|
69
88
|
|
@@ -1,3 +1,19 @@
|
|
1
|
+
# MailSpy configuration
|
2
|
+
# tracking_host: The hostname and port (if necessary) of the server MailSpy
|
3
|
+
# will make requests to for tracking.
|
4
|
+
#
|
5
|
+
# aws credentials: We use aws to store the templates for the emails.
|
6
|
+
#
|
7
|
+
|
8
|
+
|
9
|
+
MailSpy.configure do |config|
|
10
|
+
config.tracker_host = "myapp.herokuapp.com"
|
11
|
+
config.aws_access_key_id = "YOUR ACCESS KEY"
|
12
|
+
config.aws_secret_access_key = "YOUR SECRET ACCESS KEY"
|
13
|
+
config.aws_campaign_bucket = "campaigns-yourco-com"
|
14
|
+
end
|
15
|
+
|
16
|
+
|
1
17
|
# MailSpy allows you to add multiple email service providers simply
|
2
18
|
# call this method and specify the following options
|
3
19
|
#
|
@@ -16,7 +32,7 @@
|
|
16
32
|
MailSpy.add_email_service_provider do |esp|
|
17
33
|
esp.name = "sendgrid"
|
18
34
|
esp.address = "smtp.sendgrid.net"
|
19
|
-
esp.port = "587"
|
35
|
+
esp.port = "587"
|
20
36
|
esp.domain = "herokuapp.com"
|
21
37
|
esp.user_name = ENV['SENDGRID_USERNAME']
|
22
38
|
esp.password = ENV['SENDGRID_PASSWORD']
|
data/lib/mail_spy/manager.rb
CHANGED
@@ -1,43 +1,28 @@
|
|
1
1
|
module MailSpy
|
2
2
|
module Manager
|
3
3
|
|
4
|
-
# -------------------------------------------
|
5
|
-
# Programmatically add email templates with a schema of search and
|
6
|
-
# replaceable text
|
7
|
-
#
|
8
|
-
def create_template(name, html_erb, text_erb, template_values_definition)
|
9
|
-
MailSpy::EmailTemplate.create!(
|
10
|
-
{
|
11
|
-
:name => name,
|
12
|
-
:html_erb => html_erb,
|
13
|
-
:text_erb => text_erb,
|
14
|
-
:template_values_definition => template_values_definition
|
15
|
-
})
|
16
|
-
end
|
17
|
-
|
18
|
-
# ------------------------------------------- ADD INSTANCE
|
4
|
+
# ------------------------------------------- CREATE EMAIL
|
19
5
|
# Adds a instance of a email template to the queue to send
|
20
6
|
#
|
21
7
|
def create_email(options)
|
22
8
|
options.to_options!
|
23
9
|
|
24
10
|
required_options = [
|
25
|
-
:
|
11
|
+
:campaign, :stream, :component, :schedule_at, :subject,
|
26
12
|
:template_values, :from, :reply_to]
|
27
13
|
to_options = [:to, :cc, :bcc]
|
28
14
|
|
29
15
|
# Ensure that we have the required options
|
30
16
|
required_options.each { |ro| raise "create_email call missing #{ro}" unless options.include? ro }
|
31
17
|
|
18
|
+
# Ensure that the campaign, stream and component are not blank
|
19
|
+
# These keys are used to define the s3 paths for the templates
|
20
|
+
[:campaign, :stream, :component].each { |key| raise "##{key} can't be blank" if options[key].blank? }
|
21
|
+
|
32
22
|
# Make sure we have someone to send to
|
33
23
|
has_sender = options.keys.select { |option| to_options.include? option.intern }.present?
|
34
24
|
raise "Email instance has no sender (to,cc,bcc were all blank)" unless has_sender
|
35
25
|
|
36
|
-
# Make sure we have a template
|
37
|
-
options[:template_name]
|
38
|
-
template = MailSpy::EmailTemplate.first(conditions: {name: options[:template_name]})
|
39
|
-
raise "No template: #{options[:template_name]} found, try create_template first" unless template
|
40
|
-
|
41
26
|
|
42
27
|
# Make sure that
|
43
28
|
(required_options + to_options).each do |option|
|
@@ -46,9 +31,7 @@ module MailSpy
|
|
46
31
|
end
|
47
32
|
end
|
48
33
|
|
49
|
-
|
50
34
|
email = MailSpy::Email.create!(options)
|
51
|
-
email.email_template = template
|
52
35
|
email.save!
|
53
36
|
email
|
54
37
|
end
|
@@ -75,7 +58,9 @@ module MailSpy
|
|
75
58
|
where(:schedule_at.lte => DateTime.now, :sent => false).all
|
76
59
|
break if mails.blank?
|
77
60
|
mails.each do |email|
|
78
|
-
MailSpy::CoreMailer.template(email)
|
61
|
+
mail = MailSpy::CoreMailer.template(email)
|
62
|
+
#TODO might be nice to flush mail out in debug mode
|
63
|
+
mail.deliver
|
79
64
|
email.update_attribute(:sent, true)
|
80
65
|
sent += 1
|
81
66
|
end
|
data/lib/mail_spy/version.rb
CHANGED
data/lib/mail_spy.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pp' #For debugging
|
2
|
+
require 'aws-sdk'
|
2
3
|
require "mongoid"
|
3
4
|
require "mail_spy/engine"
|
4
5
|
require "mail_spy/manager"
|
@@ -14,12 +15,17 @@ module MailSpy
|
|
14
15
|
)
|
15
16
|
@@esps = {}
|
16
17
|
|
17
|
-
|
18
|
+
MailSpyConfig = Struct.new(
|
19
|
+
:tracker_host, :aws_access_key_id,
|
20
|
+
:aws_secret_access_key, :aws_campaign_bucket
|
21
|
+
|
22
|
+
)
|
23
|
+
@@config = MailSpyConfig.new
|
18
24
|
|
19
25
|
# Allows the initializer to set the configuration
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
def self.configure(&block)
|
27
|
+
block.call(@@config)
|
28
|
+
end
|
23
29
|
|
24
30
|
#TODO eventually have this be a view with a interface
|
25
31
|
def self.add_email_service_provider(&block)
|
@@ -32,5 +38,24 @@ module MailSpy
|
|
32
38
|
@@esps
|
33
39
|
end
|
34
40
|
|
41
|
+
def self.tracker_host
|
42
|
+
@@config.tracker_host
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.template_directory
|
46
|
+
@@config.template_directory
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.aws_access_key_id
|
50
|
+
@@config.aws_access_key_id
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.aws_secret_access_key
|
54
|
+
@@config.aws_secret_access_key
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.aws_campaign_bucket
|
58
|
+
@@config.aws_campaign_bucket
|
59
|
+
end
|
35
60
|
|
36
61
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<p>
|
2
|
+
You should be able to click on the link and have the server record the even
|
3
|
+
and forward you to the correct place.
|
4
|
+
|
5
|
+
A link : <%= track_link 'My home', 'www.google.com' %>
|
6
|
+
</p>
|
7
|
+
|
8
|
+
<br>
|
9
|
+
|
10
|
+
<p>
|
11
|
+
You should be able to show images in the email client and have the server
|
12
|
+
track the open
|
13
|
+
A Bug is in the parenthesis (<%= tracking_bug %>)
|
14
|
+
</p>
|
15
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
You should be able to click on the link and have the server record the even
|
2
|
+
and forward you to the correct place.
|
3
|
+
|
4
|
+
A link : <%= track_link 'My home', 'www.google.com' %>
|
5
|
+
---------------------------
|
6
|
+
|
7
|
+
You should be able to show images in the email client and have the server
|
8
|
+
track the open
|
9
|
+
A Bug is in the parenthesis (<%= tracking_bug %>)
|
10
|
+
|
11
|
+
|
@@ -14,7 +14,7 @@ Dummy::Application.configure do
|
|
14
14
|
config.action_controller.perform_caching = false
|
15
15
|
|
16
16
|
# Don't care if the mailer can't send
|
17
|
-
config.action_mailer.raise_delivery_errors =
|
17
|
+
config.action_mailer.raise_delivery_errors = true
|
18
18
|
|
19
19
|
# Print deprecation notices to the Rails logger
|
20
20
|
config.active_support.deprecation = :log
|
@@ -5,7 +5,7 @@ Dummy::Application.configure do
|
|
5
5
|
config.cache_classes = true
|
6
6
|
|
7
7
|
# Full error reports are disabled and caching is turned on
|
8
|
-
config.consider_all_requests_local
|
8
|
+
config.consider_all_requests_local = false
|
9
9
|
config.action_controller.perform_caching = true
|
10
10
|
|
11
11
|
# Disable Rails's static asset server (Apache or nginx will already do this)
|
@@ -64,4 +64,8 @@ Dummy::Application.configure do
|
|
64
64
|
# Log the query plan for queries taking more than this (works
|
65
65
|
# with SQLite, MySQL, and PostgreSQL)
|
66
66
|
# config.active_record.auto_explain_threshold_in_seconds = 0.5
|
67
|
+
|
68
|
+
# Raise errors on mailer failure
|
69
|
+
config.action_mailer.raise_delivery_errors = true
|
70
|
+
|
67
71
|
end
|
@@ -27,11 +27,14 @@ Dummy::Application.configure do
|
|
27
27
|
# Tell Action Mailer not to deliver emails to the real world.
|
28
28
|
# The :test delivery method accumulates sent emails in the
|
29
29
|
# ActionMailer::Base.deliveries array.
|
30
|
-
config.action_mailer.delivery_method = :
|
30
|
+
config.action_mailer.delivery_method = :smtp
|
31
31
|
|
32
32
|
# Raise exception on mass assignment protection for Active Record models
|
33
33
|
config.active_record.mass_assignment_sanitizer = :strict
|
34
34
|
|
35
35
|
# Print deprecation notices to the stderr
|
36
36
|
config.active_support.deprecation = :stderr
|
37
|
+
|
38
|
+
# Raise errors on mailer failure
|
39
|
+
config.action_mailer.raise_delivery_errors = true
|
37
40
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# MailSpy configuration
|
2
|
+
# tracking_host: The hostname and port (if necessary) of the server MailSpy
|
3
|
+
# will make requests to for tracking.
|
4
|
+
|
5
|
+
|
6
|
+
MailSpy.configure do |config|
|
7
|
+
config.tracker_host = "localhost:5000"
|
8
|
+
config.aws_access_key_id = "AKIAIF6DRMAH3BR47AZQ"
|
9
|
+
config.aws_secret_access_key = "iy3wBOLSX0a7clbfYPSqvDprVnUzQURokZUazMpZ"
|
10
|
+
config.aws_campaign_bucket = "daviacalendar-mailspy"
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
# MailSpy allows you to add multiple email service providers simply
|
15
|
+
# call this method and specify the following options
|
16
|
+
#
|
17
|
+
# :name (must be unique for all esps you add)
|
18
|
+
# :via (stmp or sendmail)
|
19
|
+
#
|
20
|
+
# SMTP OPTIONS
|
21
|
+
# :address,
|
22
|
+
# :port,
|
23
|
+
# :authentication,
|
24
|
+
# :user_name,
|
25
|
+
# :password,
|
26
|
+
# :domain,
|
27
|
+
# :enable_starttls_auto
|
28
|
+
|
29
|
+
MailSpy.add_email_service_provider do |esp|
|
30
|
+
esp.name = "sendgrid"
|
31
|
+
esp.address = "smtp.sendgrid.net"
|
32
|
+
esp.port = "587"
|
33
|
+
esp.domain = "herokuapp.com"
|
34
|
+
esp.user_name = "app2602840@heroku.com"
|
35
|
+
esp.password = "qb6qnim0"
|
36
|
+
esp.authentication = :plain
|
37
|
+
esp.enable_starttls_auto = true
|
38
|
+
end
|
data/test/dummy/config/routes.rb
CHANGED
@@ -210,3 +210,15 @@ MONGODB dummy_development['mail_spy_emails'].find({}).limit(-1).sort([[:_id, :de
|
|
210
210
|
MONGODB dummy_development['mail_spy_emails'].find({}).limit(-1).sort([[:_id, :desc]])
|
211
211
|
MONGODB dummy_development['system.namespaces'].find({})
|
212
212
|
MONGODB dummy_development['mail_spy_emails'].find({}).limit(-1).sort([[:_id, :desc]])
|
213
|
+
MONGODB [WARNING] Please note that logging negatively impacts client-side performance. You should set your logging level no lower than :info in production.
|
214
|
+
MONGODB admin['$cmd'].find({:ismaster=>1}).limit(-1)
|
215
|
+
MONGODB [WARNING] Please note that logging negatively impacts client-side performance. You should set your logging level no lower than :info in production.
|
216
|
+
MONGODB admin['$cmd'].find({:ismaster=>1}).limit(-1)
|
217
|
+
MONGODB [WARNING] Please note that logging negatively impacts client-side performance. You should set your logging level no lower than :info in production.
|
218
|
+
MONGODB admin['$cmd'].find({:ismaster=>1}).limit(-1)
|
219
|
+
MONGODB [WARNING] Please note that logging negatively impacts client-side performance. You should set your logging level no lower than :info in production.
|
220
|
+
MONGODB admin['$cmd'].find({:ismaster=>1}).limit(-1)
|
221
|
+
MONGODB [WARNING] Please note that logging negatively impacts client-side performance. You should set your logging level no lower than :info in production.
|
222
|
+
MONGODB admin['$cmd'].find({:ismaster=>1}).limit(-1)
|
223
|
+
MONGODB [WARNING] Please note that logging negatively impacts client-side performance. You should set your logging level no lower than :info in production.
|
224
|
+
MONGODB admin['$cmd'].find({:ismaster=>1}).limit(-1)
|