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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 784f96fb14f9b6b23c849aa435ffbbf3acdac543
4
- data.tar.gz: 6cc58343026c75ad6536665617838a5b593669ad
3
+ metadata.gz: 49c170ba212ecf55a91229da66bd709ae46845d3
4
+ data.tar.gz: 5c63a301d6ec6cb0d2f711a0398b3d0655eb2262
5
5
  SHA512:
6
- metadata.gz: a19c3241eeaa13893da620b0eea2d35dda95948a5603591e33067de0805e0daad208929da1d6d6f86b26b1a6c93598e2c51536888d1a0215b93c9d6957651c84
7
- data.tar.gz: cc06e0550beaff17574a3f57108cd444584c5d17eb7a91bc7efa8b81f7f2f42fe3cc8937c97110f8b1c09fa4a010cf60977d86c210d83730d7fbcc603c806d2f
6
+ metadata.gz: 5be291db638905583b8417f1a402083ae5c3d675b76acdc7a1115400510e2b9685ecbdea94d5ce6952daba528d4fff98e69650d95408e435aff6358ebe666439
7
+ data.tar.gz: 1f39a60bc2ec8a69c3584b0cbaa2888e5e055272d973f448aefa49dc7bb70d2c653dabccc15a7fdea01fed5cf493bba8b8f215918886acd6889d2a45fcb57a90
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ *.log
24
+ *.sqlite
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.3.0
2
+
3
+ - Added safely for error reporting
4
+ - Fixed error with `to`
5
+ - Prevent duplicate records when mail called multiple times
6
+
1
7
  ## 0.2.4
2
8
 
3
9
  - Added `extra` option for extra attributes
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 and use:
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
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task default: :test
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/**/*_test.rb"
8
+ end
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.add_dependency "rails"
22
- spec.add_dependency "addressable"
23
- spec.add_dependency "nokogiri"
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"
@@ -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
@@ -10,44 +10,44 @@ module AhoyEmail
10
10
  end
11
11
 
12
12
  def process
13
- action_name = mailer.action_name.to_sym
14
- if options[:message] && (!options[:only] || options[:only].include?(action_name)) && !options[:except].to_a.include?(action_name)
15
- @ahoy_message = AhoyEmail.message_model.new
16
- ahoy_message.token = generate_token
17
- ahoy_message.to = message.to.join(", ") if ahoy_message.respond_to?(:to=)
18
- ahoy_message.user = options[:user]
19
-
20
- track_open if options[:open]
21
- track_links if options[:utm_params] || options[:click]
22
-
23
- ahoy_message.mailer = options[:mailer] if ahoy_message.respond_to?(:mailer=)
24
- ahoy_message.subject = message.subject if ahoy_message.respond_to?(:subject=)
25
- ahoy_message.content = message.to_s if ahoy_message.respond_to?(:content=)
26
-
27
- UTM_PARAMETERS.each do |k|
28
- ahoy_message.send("#{k}=", options[k.to_sym]) if ahoy_message.respond_to?("#{k}=")
29
- end
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
- ahoy_message.assign_attributes(options[:extra] || {})
32
+ ahoy_message.assign_attributes(options[:extra] || {})
32
33
 
33
- ahoy_message.save
34
- message["Ahoy-Message-Id"] = ahoy_message.id.to_s
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
- if (message_id = message["Ahoy-Message-Id"])
42
- ahoy_message = AhoyEmail.message_model.where(id: message_id.to_s).first
43
- if ahoy_message
44
- ahoy_message.sent_at = Time.now
45
- ahoy_message.save
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
@@ -1,3 +1,3 @@
1
1
  module AhoyEmail
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/combustion_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+ Bundler.require(:default)
3
+ require "minitest/autorun"
4
+ require "minitest/pride"
5
+ require "combustion"
6
+
7
+ Combustion.path = "test/internal"
8
+ Combustion.initialize! :all
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.2.4
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-07-30 00:00:00.000000000 Z
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
- has_rdoc:
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