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 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