ahoy_email 0.2.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +6 -0
- data/README.md +11 -1
- data/Rakefile +7 -0
- data/ahoy_email.gemspec +7 -3
- data/lib/ahoy_email.rb +2 -0
- data/lib/ahoy_email/mailer.rb +3 -0
- data/lib/ahoy_email/processor.rb +44 -45
- data/lib/ahoy_email/version.rb +1 -1
- data/test/internal/config/database.yml +3 -0
- data/test/internal/config/routes.rb +3 -0
- data/test/internal/db/schema.rb +30 -0
- data/test/mailer_test.rb +46 -0
- data/test/test_helper.rb +8 -0
- metadata +70 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49c170ba212ecf55a91229da66bd709ae46845d3
|
4
|
+
data.tar.gz: 5c63a301d6ec6cb0d2f711a0398b3d0655eb2262
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5be291db638905583b8417f1a402083ae5c3d675b76acdc7a1115400510e2b9685ecbdea94d5ce6952daba528d4fff98e69650d95408e435aff6358ebe666439
|
7
|
+
data.tar.gz: 1f39a60bc2ec8a69c3584b0cbaa2888e5e055272d973f448aefa49dc7bb70d2c653dabccc15a7fdea01fed5cf493bba8b8f215918886acd6889d2a45fcb57a90
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -116,7 +116,17 @@ Use `track utm_params: false` to skip tagging, or skip specific links with:
|
|
116
116
|
|
117
117
|
### Extra Attributes
|
118
118
|
|
119
|
-
Create a migration to add extra attributes to the `ahoy_messages` table
|
119
|
+
Create a migration to add extra attributes to the `ahoy_messages` table, for example:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
class AddCampaignIdToAhoyMessages < ActiveRecord::Migration
|
123
|
+
def change
|
124
|
+
add_column :ahoy_messages, :campaign_id, :integer
|
125
|
+
end
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
Then use:
|
120
130
|
|
121
131
|
```ruby
|
122
132
|
track extra: {campaign_id: 1}
|
data/Rakefile
CHANGED
data/ahoy_email.gemspec
CHANGED
@@ -18,10 +18,14 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.
|
23
|
-
spec.
|
21
|
+
spec.add_runtime_dependency "rails"
|
22
|
+
spec.add_runtime_dependency "addressable"
|
23
|
+
spec.add_runtime_dependency "nokogiri"
|
24
|
+
spec.add_runtime_dependency "safely_block"
|
24
25
|
|
25
26
|
spec.add_development_dependency "bundler", "~> 1.6"
|
26
27
|
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "minitest"
|
29
|
+
spec.add_development_dependency "combustion"
|
30
|
+
spec.add_development_dependency "sqlite3"
|
27
31
|
end
|
data/lib/ahoy_email.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require "ahoy_email/version"
|
2
2
|
require "action_mailer"
|
3
|
+
require "rails"
|
3
4
|
require "nokogiri"
|
4
5
|
require "addressable/uri"
|
5
6
|
require "openssl"
|
7
|
+
require "safely_block"
|
6
8
|
require "ahoy_email/processor"
|
7
9
|
require "ahoy_email/interceptor"
|
8
10
|
require "ahoy_email/mailer"
|
data/lib/ahoy_email/mailer.rb
CHANGED
@@ -21,6 +21,9 @@ module AhoyEmail
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def mail_with_ahoy(headers = {}, &block)
|
24
|
+
# this mimics what original method does
|
25
|
+
return message if @_mail_was_called && headers.blank? && !block
|
26
|
+
|
24
27
|
message = mail_without_ahoy(headers, &block)
|
25
28
|
AhoyEmail::Processor.new(message, self).process
|
26
29
|
message
|
data/lib/ahoy_email/processor.rb
CHANGED
@@ -10,44 +10,44 @@ module AhoyEmail
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def process
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
13
|
+
safely do
|
14
|
+
action_name = mailer.action_name.to_sym
|
15
|
+
if options[:message] && (!options[:only] || options[:only].include?(action_name)) && !options[:except].to_a.include?(action_name)
|
16
|
+
@ahoy_message = AhoyEmail.message_model.new
|
17
|
+
ahoy_message.token = generate_token
|
18
|
+
ahoy_message.to = Array(message.to).join(", ") if ahoy_message.respond_to?(:to=)
|
19
|
+
ahoy_message.user = options[:user]
|
20
|
+
|
21
|
+
track_open if options[:open]
|
22
|
+
track_links if options[:utm_params] || options[:click]
|
23
|
+
|
24
|
+
ahoy_message.mailer = options[:mailer] if ahoy_message.respond_to?(:mailer=)
|
25
|
+
ahoy_message.subject = message.subject if ahoy_message.respond_to?(:subject=)
|
26
|
+
ahoy_message.content = message.to_s if ahoy_message.respond_to?(:content=)
|
27
|
+
|
28
|
+
UTM_PARAMETERS.each do |k|
|
29
|
+
ahoy_message.send("#{k}=", options[k.to_sym]) if ahoy_message.respond_to?("#{k}=")
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
+
ahoy_message.assign_attributes(options[:extra] || {})
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
ahoy_message.save
|
35
|
+
message["Ahoy-Message-Id"] = ahoy_message.id.to_s
|
36
|
+
end
|
35
37
|
end
|
36
|
-
rescue => e
|
37
|
-
report_error(e)
|
38
38
|
end
|
39
39
|
|
40
40
|
def track_send
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
ahoy_message
|
45
|
-
|
41
|
+
safely do
|
42
|
+
if (message_id = message["Ahoy-Message-Id"])
|
43
|
+
ahoy_message = AhoyEmail.message_model.where(id: message_id.to_s).first
|
44
|
+
if ahoy_message
|
45
|
+
ahoy_message.sent_at = Time.now
|
46
|
+
ahoy_message.save
|
47
|
+
end
|
48
|
+
message["Ahoy-Message-Id"] = nil
|
46
49
|
end
|
47
|
-
message["Ahoy-Message-Id"] = nil
|
48
50
|
end
|
49
|
-
rescue => e
|
50
|
-
report_error(e)
|
51
51
|
end
|
52
52
|
|
53
53
|
protected
|
@@ -99,9 +99,10 @@ module AhoyEmail
|
|
99
99
|
|
100
100
|
doc = Nokogiri::HTML(body.raw_source)
|
101
101
|
doc.css("a[href]").each do |link|
|
102
|
+
uri = parse_uri(link["href"])
|
103
|
+
next unless trackable?(uri)
|
102
104
|
# utm params first
|
103
105
|
if options[:utm_params] && !skip_attribute?(link, "utm-params")
|
104
|
-
uri = Addressable::URI.parse(link["href"])
|
105
106
|
params = uri.query_values || {}
|
106
107
|
UTM_PARAMETERS.each do |key|
|
107
108
|
params[key] ||= options[key.to_sym] if options[key.to_sym]
|
@@ -141,30 +142,28 @@ module AhoyEmail
|
|
141
142
|
elsif link["href"].to_s =~ /unsubscribe/i
|
142
143
|
# try to avoid unsubscribe links
|
143
144
|
true
|
144
|
-
elsif link["href"].to_s.start_with?("mailto:")
|
145
|
-
# mailto's shouldn't go through a redirect
|
146
|
-
true
|
147
145
|
else
|
148
146
|
false
|
149
147
|
end
|
150
148
|
end
|
151
149
|
|
150
|
+
# Filter trackable URIs, i.e. absolute one with http
|
151
|
+
def trackable?(uri)
|
152
|
+
uri && uri.absolute? && %w(http https).include?(uri.scheme)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Parse href attribute
|
156
|
+
# Return uri if valid, nil otherwise
|
157
|
+
def parse_uri(href)
|
158
|
+
# to_s prevent to return nil from this method
|
159
|
+
Addressable::URI.parse(href.to_s) rescue nil
|
160
|
+
end
|
161
|
+
|
152
162
|
def url_for(opt)
|
153
163
|
opt = (ActionMailer::Base.default_url_options || {})
|
154
164
|
.merge(options[:url_options])
|
155
165
|
.merge(opt)
|
156
166
|
AhoyEmail::Engine.routes.url_helpers.url_for(opt)
|
157
167
|
end
|
158
|
-
|
159
|
-
# not a fan of quiet errors
|
160
|
-
# but tracking should *not* break
|
161
|
-
# email delivery in production
|
162
|
-
def report_error(e)
|
163
|
-
if Rails.env.production?
|
164
|
-
$stderr.puts e
|
165
|
-
else
|
166
|
-
raise e
|
167
|
-
end
|
168
|
-
end
|
169
168
|
end
|
170
169
|
end
|
data/lib/ahoy_email/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table :ahoy_messages, force: true do |t|
|
3
|
+
t.string :token
|
4
|
+
|
5
|
+
# user
|
6
|
+
t.text :to
|
7
|
+
t.integer :user_id
|
8
|
+
t.string :user_type
|
9
|
+
|
10
|
+
# optional - feel free to remove
|
11
|
+
t.string :mailer
|
12
|
+
t.text :subject
|
13
|
+
t.text :content
|
14
|
+
|
15
|
+
# optional
|
16
|
+
t.string :utm_source
|
17
|
+
t.string :utm_medium
|
18
|
+
t.string :utm_term
|
19
|
+
t.string :utm_content
|
20
|
+
t.string :utm_campaign
|
21
|
+
|
22
|
+
# timestamps
|
23
|
+
t.timestamp :sent_at
|
24
|
+
t.timestamp :opened_at
|
25
|
+
t.timestamp :clicked_at
|
26
|
+
end
|
27
|
+
|
28
|
+
add_index :ahoy_messages, [:token]
|
29
|
+
add_index :ahoy_messages, [:user_id, :user_type]
|
30
|
+
end
|
data/test/mailer_test.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class UserMailer < ActionMailer::Base
|
4
|
+
after_action :prevent_delivery_to_guests, only: [:welcome2]
|
5
|
+
|
6
|
+
def welcome
|
7
|
+
mail to: "test@example.org", subject: "Hello", body: "World"
|
8
|
+
end
|
9
|
+
|
10
|
+
def welcome2
|
11
|
+
mail to: "test@example.org", subject: "Hello", body: "World"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def prevent_delivery_to_guests
|
17
|
+
mail.perform_deliveries = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class MailerTest < Minitest::Test
|
22
|
+
def setup
|
23
|
+
Ahoy::Message.delete_all
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_basic
|
27
|
+
assert_message :welcome
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_prevent_delivery
|
31
|
+
assert_message :welcome2
|
32
|
+
end
|
33
|
+
|
34
|
+
def assert_message(method)
|
35
|
+
message = UserMailer.send(method)
|
36
|
+
message.to # trigger creation
|
37
|
+
ahoy_message = Ahoy::Message.first
|
38
|
+
assert_equal 1, Ahoy::Message.count
|
39
|
+
assert_equal "test@example.org", ahoy_message.to
|
40
|
+
assert_equal "UserMailer##{method}", ahoy_message.mailer
|
41
|
+
assert_equal "Hello", ahoy_message.subject
|
42
|
+
assert_equal "user_mailer", ahoy_message.utm_source
|
43
|
+
assert_equal "email", ahoy_message.utm_medium
|
44
|
+
assert_equal method.to_s, ahoy_message.utm_campaign
|
45
|
+
end
|
46
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ahoy_email
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: safely_block
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,48 @@ dependencies:
|
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: combustion
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sqlite3
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
83
139
|
description: Simple, powerful email tracking for Rails
|
84
140
|
email:
|
85
141
|
- andrew@chartkick.com
|
@@ -105,6 +161,11 @@ files:
|
|
105
161
|
- lib/ahoy_email/version.rb
|
106
162
|
- lib/generators/ahoy_email/install_generator.rb
|
107
163
|
- lib/generators/ahoy_email/templates/install.rb
|
164
|
+
- test/internal/config/database.yml
|
165
|
+
- test/internal/config/routes.rb
|
166
|
+
- test/internal/db/schema.rb
|
167
|
+
- test/mailer_test.rb
|
168
|
+
- test/test_helper.rb
|
108
169
|
homepage: https://github.com/ankane/ahoy_email
|
109
170
|
licenses:
|
110
171
|
- MIT
|
@@ -125,9 +186,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
186
|
version: '0'
|
126
187
|
requirements: []
|
127
188
|
rubyforge_project:
|
128
|
-
rubygems_version: 2.4.5
|
189
|
+
rubygems_version: 2.4.5.1
|
129
190
|
signing_key:
|
130
191
|
specification_version: 4
|
131
192
|
summary: Simple, powerful email tracking for Rails
|
132
|
-
test_files:
|
133
|
-
|
193
|
+
test_files:
|
194
|
+
- test/internal/config/database.yml
|
195
|
+
- test/internal/config/routes.rb
|
196
|
+
- test/internal/db/schema.rb
|
197
|
+
- test/mailer_test.rb
|
198
|
+
- test/test_helper.rb
|