ahoy_email 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|