ahoy_email 0.5.2 → 1.1.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.
@@ -1,103 +1,90 @@
1
1
  module AhoyEmail
2
2
  class Processor
3
- attr_reader :message, :mailer, :ahoy_message
3
+ attr_reader :mailer, :options
4
4
 
5
5
  UTM_PARAMETERS = %w(utm_source utm_medium utm_term utm_content utm_campaign)
6
6
 
7
- def initialize(message, mailer = nil)
8
- @message = message
7
+ def initialize(mailer, options)
9
8
  @mailer = mailer
10
- end
11
-
12
- def process
13
- Safely.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]
9
+ @options = options
20
10
 
21
- track_open if options[:open]
22
- track_links if options[:utm_params] || options[:click]
11
+ unknown_keywords = options.keys - AhoyEmail.default_options.keys
12
+ raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
13
+ end
23
14
 
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=)
15
+ def perform
16
+ track_open if options[:open]
17
+ track_links if options[:utm_params] || options[:click]
18
+ track_message
19
+ end
27
20
 
28
- UTM_PARAMETERS.each do |k|
29
- ahoy_message.send("#{k}=", options[k.to_sym]) if ahoy_message.respond_to?("#{k}=")
30
- end
21
+ protected
31
22
 
32
- ahoy_message.assign_attributes(options[:extra] || {})
23
+ def message
24
+ mailer.message
25
+ end
33
26
 
34
- ahoy_message.save!
35
- message["Ahoy-Message-Id"] = ahoy_message.id.to_s
36
- end
37
- end
27
+ def token
28
+ @token ||= SecureRandom.urlsafe_base64(32).gsub(/[\-_]/, "").first(32)
38
29
  end
39
30
 
40
- def track_send
41
- Safely.safely do
42
- if (message_id = message["Ahoy-Message-Id"]) && message.perform_deliveries
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
49
- end
31
+ def track_message
32
+ data = {
33
+ mailer: options[:mailer],
34
+ extra: options[:extra],
35
+ user: options[:user]
36
+ }
37
+
38
+ # legacy, remove in next major version
39
+ user = options[:user]
40
+ if user
41
+ data[:user_type] = user.model_name.name
42
+ id = user.id
43
+ data[:user_id] = id.is_a?(Integer) ? id : id.to_s
50
44
  end
51
- end
52
45
 
53
- protected
46
+ if options[:open] || options[:click]
47
+ data[:token] = token
48
+ end
54
49
 
55
- def options
56
- @options ||= begin
57
- options = AhoyEmail.options.merge(mailer.class.ahoy_options)
58
- if mailer.ahoy_options
59
- options = options.except(:only, :except).merge(mailer.ahoy_options)
60
- end
61
- options.each do |k, v|
62
- if v.respond_to?(:call)
63
- options[k] = v.call(message, mailer)
64
- end
50
+ if options[:utm_params]
51
+ UTM_PARAMETERS.map(&:to_sym).each do |k|
52
+ data[k] = options[k] if options[k]
65
53
  end
66
- options
67
54
  end
68
- end
69
55
 
70
- def generate_token
71
- SecureRandom.urlsafe_base64(32).gsub(/[\-_]/, "").first(32)
56
+ mailer.message.ahoy_data = data
72
57
  end
73
58
 
74
59
  def track_open
75
60
  if html_part?
76
- raw_source = (message.html_part || message).body.raw_source
61
+ part = message.html_part || message
62
+ raw_source = part.body.raw_source
63
+
77
64
  regex = /<\/body>/i
78
65
  url =
79
66
  url_for(
80
67
  controller: "ahoy/messages",
81
68
  action: "open",
82
- id: ahoy_message.token,
69
+ id: token,
83
70
  format: "gif"
84
71
  )
85
72
  pixel = ActionController::Base.helpers.image_tag(url, size: "1x1", alt: "")
86
73
 
87
74
  # try to add before body tag
88
75
  if raw_source.match(regex)
89
- raw_source.gsub!(regex, "#{pixel}\\0")
76
+ part.body = raw_source.gsub(regex, "#{pixel}\\0")
90
77
  else
91
- raw_source << pixel
78
+ part.body = raw_source + pixel
92
79
  end
93
80
  end
94
81
  end
95
82
 
96
83
  def track_links
97
84
  if html_part?
98
- body = (message.html_part || message).body
85
+ part = message.html_part || message
99
86
 
100
- doc = Nokogiri::HTML(body.raw_source)
87
+ doc = Nokogiri::HTML(part.body.raw_source)
101
88
  doc.css("a[href]").each do |link|
102
89
  uri = parse_uri(link["href"])
103
90
  next unless trackable?(uri)
@@ -113,20 +100,22 @@ module AhoyEmail
113
100
  end
114
101
 
115
102
  if options[:click] && !skip_attribute?(link, "click")
116
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha1"), AhoyEmail.secret_token, link["href"])
103
+ raise "Secret token is empty" unless AhoyEmail.secret_token
104
+
105
+ # TODO sign more than just url and transition to HMAC-SHA256
106
+ signature = OpenSSL::HMAC.hexdigest("SHA1", AhoyEmail.secret_token, link["href"])
117
107
  link["href"] =
118
108
  url_for(
119
109
  controller: "ahoy/messages",
120
110
  action: "click",
121
- id: ahoy_message.token,
111
+ id: token,
122
112
  url: link["href"],
123
113
  signature: signature
124
114
  )
125
115
  end
126
116
  end
127
117
 
128
- # hacky
129
- body.raw_source.sub!(body.raw_source, doc.to_s)
118
+ part.body = doc.to_s
130
119
  end
131
120
  end
132
121
 
@@ -157,11 +146,7 @@ module AhoyEmail
157
146
  # Return uri if valid, nil otherwise
158
147
  def parse_uri(href)
159
148
  # to_s prevent to return nil from this method
160
- if options[:heuristic_parse]
161
- Addressable::URI.heuristic_parse(href.to_s) rescue nil
162
- else
163
- Addressable::URI.parse(href.to_s) rescue nil
164
- end
149
+ Addressable::URI.heuristic_parse(href.to_s) rescue nil
165
150
  end
166
151
 
167
152
  def url_for(opt)
@@ -0,0 +1,19 @@
1
+ module AhoyEmail
2
+ class Tracker
3
+ attr_reader :message
4
+
5
+ def initialize(message)
6
+ @message = message
7
+ end
8
+
9
+ def perform
10
+ Safely.safely do
11
+ # perform_deliveries check still needed in observer
12
+ if message.perform_deliveries && message.ahoy_data
13
+ data = message.ahoy_data.merge(message: message)
14
+ message.ahoy_message = AhoyEmail.track_method.call(data)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module AhoyEmail
2
- VERSION = "0.5.2"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/ahoy_email.rb CHANGED
@@ -1,47 +1,74 @@
1
+ # dependencies
1
2
  require "active_support"
2
- require "nokogiri"
3
3
  require "addressable/uri"
4
+ require "nokogiri"
4
5
  require "openssl"
5
6
  require "safely/core"
7
+
8
+ # modules
6
9
  require "ahoy_email/processor"
7
- require "ahoy_email/interceptor"
10
+ require "ahoy_email/tracker"
11
+ require "ahoy_email/observer"
8
12
  require "ahoy_email/mailer"
9
- require "ahoy_email/engine"
10
13
  require "ahoy_email/version"
14
+ require "ahoy_email/engine" if defined?(Rails)
11
15
 
12
16
  module AhoyEmail
13
- mattr_accessor :secret_token, :options, :subscribers, :belongs_to, :invalid_redirect_url
17
+ mattr_accessor :secret_token, :default_options, :subscribers, :invalid_redirect_url, :track_method, :api, :preserve_callbacks
18
+ mattr_writer :message_model
19
+
20
+ self.api = false
14
21
 
15
- self.options = {
22
+ self.default_options = {
16
23
  message: true,
17
- open: true,
18
- click: true,
19
- utm_params: true,
20
- utm_source: ->(message, mailer) { mailer.mailer_name },
24
+ open: false,
25
+ click: false,
26
+ utm_params: false,
27
+ utm_source: -> { mailer_name },
21
28
  utm_medium: "email",
22
29
  utm_term: nil,
23
30
  utm_content: nil,
24
- utm_campaign: ->(message, mailer) { mailer.action_name },
25
- user: ->(message, mailer) { (message.to.size == 1 ? User.where(email: message.to.first).first : nil) rescue nil },
26
- mailer: ->(message, mailer) { "#{mailer.class.name}##{mailer.action_name}" },
31
+ utm_campaign: -> { action_name },
32
+ user: -> { @user || (respond_to?(:params) && params && params[:user]) || (message.to.try(:size) == 1 ? (User.find_by(email: message.to.first) rescue nil) : nil) },
33
+ mailer: -> { "#{self.class.name}##{action_name}" },
27
34
  url_options: {},
28
- heuristic_parse: false
35
+ extra: {},
36
+ unsubscribe_links: false
29
37
  }
30
38
 
31
- self.subscribers = []
39
+ self.track_method = lambda do |data|
40
+ message = data[:message]
32
41
 
33
- self.belongs_to = {}
42
+ ahoy_message = AhoyEmail.message_model.new
43
+ ahoy_message.to = Array(message.to).join(", ") if ahoy_message.respond_to?(:to=)
44
+ ahoy_message.user = data[:user] if ahoy_message.respond_to?(:user=)
34
45
 
35
- def self.track(options)
36
- self.options = self.options.merge(options)
37
- end
46
+ ahoy_message.mailer = data[:mailer] if ahoy_message.respond_to?(:mailer=)
47
+ ahoy_message.subject = message.subject if ahoy_message.respond_to?(:subject=)
48
+ ahoy_message.content = message.encoded if ahoy_message.respond_to?(:content=)
49
+
50
+ AhoyEmail::Processor::UTM_PARAMETERS.each do |k|
51
+ ahoy_message.send("#{k}=", data[k.to_sym]) if ahoy_message.respond_to?("#{k}=")
52
+ end
38
53
 
39
- class << self
40
- attr_writer :message_model
54
+ ahoy_message.token = data[:token] if ahoy_message.respond_to?(:token=)
55
+
56
+ ahoy_message.assign_attributes(data[:extra] || {})
57
+
58
+ ahoy_message.sent_at = Time.now
59
+ ahoy_message.save!
60
+
61
+ ahoy_message
41
62
  end
42
63
 
64
+ self.subscribers = []
65
+
66
+ self.preserve_callbacks = []
67
+
68
+ self.message_model = -> { ::Ahoy::Message }
69
+
43
70
  def self.message_model
44
- model = (defined?(@message_model) && @message_model) || ::Ahoy::Message
71
+ model = defined?(@@message_model) && @@message_model
45
72
  model = model.call if model.respond_to?(:call)
46
73
  model
47
74
  end
@@ -49,5 +76,6 @@ end
49
76
 
50
77
  ActiveSupport.on_load(:action_mailer) do
51
78
  include AhoyEmail::Mailer
52
- register_interceptor AhoyEmail::Interceptor
79
+ register_observer AhoyEmail::Observer
80
+ Mail::Message.send(:attr_accessor, :ahoy_data, :ahoy_message)
53
81
  end
@@ -1,34 +1,17 @@
1
- # taken from https://github.com/collectiveidea/audited/blob/master/lib/generators/audited/install_generator.rb
2
- require "rails/generators"
3
- require "rails/generators/migration"
4
- require "active_record"
5
1
  require "rails/generators/active_record"
6
2
 
7
3
  module AhoyEmail
8
4
  module Generators
9
5
  class InstallGenerator < Rails::Generators::Base
10
- include Rails::Generators::Migration
11
-
12
- source_root File.expand_path("../templates", __FILE__)
13
-
14
- # Implement the required interface for Rails::Generators::Migration.
15
- def self.next_migration_number(dirname) #:nodoc:
16
- next_migration_number = current_migration_number(dirname) + 1
17
- if ActiveRecord::Base.timestamped_migrations
18
- [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
19
- else
20
- "%.3d" % next_migration_number
21
- end
22
- end
6
+ include ActiveRecord::Generators::Migration
7
+ source_root File.join(__dir__, "templates")
23
8
 
24
9
  def copy_migration
25
10
  migration_template "install.rb", "db/migrate/create_ahoy_messages.rb", migration_version: migration_version
26
11
  end
27
12
 
28
13
  def migration_version
29
- if ActiveRecord::VERSION::MAJOR >= 5
30
- "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
31
- end
14
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
32
15
  end
33
16
  end
34
17
  end
@@ -0,0 +1,11 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :ahoy_messages do |t|
4
+ t.references :user, polymorphic: true
5
+ t.text :to
6
+ t.string :mailer
7
+ t.text :subject
8
+ t.timestamp :sent_at
9
+ end
10
+ end
11
+ end
metadata CHANGED
@@ -1,57 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ahoy_email
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 1.1.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: 2018-04-26 00:00:00.000000000 Z
11
+ date: 2019-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: railties
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: actionmailer
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - ">="
32
18
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: activerecord
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
19
+ version: '5'
48
20
  type: :runtime
49
21
  prerelease: false
50
22
  version_requirements: !ruby/object:Gem::Requirement
51
23
  requirements:
52
24
  - - ">="
53
25
  - !ruby/object:Gem::Version
54
- version: '0'
26
+ version: '5'
55
27
  - !ruby/object:Gem::Dependency
56
28
  name: addressable
57
29
  requirement: !ruby/object:Gem::Requirement
@@ -137,7 +109,7 @@ dependencies:
137
109
  - !ruby/object:Gem::Version
138
110
  version: '0'
139
111
  - !ruby/object:Gem::Dependency
140
- name: combustion
112
+ name: activerecord
141
113
  requirement: !ruby/object:Gem::Requirement
142
114
  requirements:
143
115
  - - ">="
@@ -151,7 +123,7 @@ dependencies:
151
123
  - !ruby/object:Gem::Version
152
124
  version: '0'
153
125
  - !ruby/object:Gem::Dependency
154
- name: rails
126
+ name: combustion
155
127
  requirement: !ruby/object:Gem::Requirement
156
128
  requirements:
157
129
  - - ">="
@@ -179,39 +151,27 @@ dependencies:
179
151
  - !ruby/object:Gem::Version
180
152
  version: '0'
181
153
  description:
182
- email:
183
- - andrew@chartkick.com
154
+ email: andrew@chartkick.com
184
155
  executables: []
185
156
  extensions: []
186
157
  extra_rdoc_files: []
187
158
  files:
188
- - ".gitignore"
189
- - ".travis.yml"
190
159
  - CHANGELOG.md
191
- - Gemfile
160
+ - CONTRIBUTING.md
192
161
  - LICENSE.txt
193
162
  - README.md
194
- - Rakefile
195
- - ahoy_email.gemspec
196
163
  - app/controllers/ahoy/messages_controller.rb
197
164
  - app/models/ahoy/message.rb
198
165
  - config/routes.rb
199
166
  - lib/ahoy_email.rb
200
167
  - lib/ahoy_email/engine.rb
201
- - lib/ahoy_email/interceptor.rb
202
168
  - lib/ahoy_email/mailer.rb
169
+ - lib/ahoy_email/observer.rb
203
170
  - lib/ahoy_email/processor.rb
171
+ - lib/ahoy_email/tracker.rb
204
172
  - lib/ahoy_email/version.rb
205
173
  - lib/generators/ahoy_email/install_generator.rb
206
- - lib/generators/ahoy_email/templates/install.rb
207
- - test/gemfiles/actionmailer42.gemfile
208
- - test/gemfiles/actionmailer50.gemfile
209
- - test/gemfiles/actionmailer51.gemfile
210
- - test/internal/config/database.yml
211
- - test/internal/config/routes.rb
212
- - test/internal/db/schema.rb
213
- - test/mailer_test.rb
214
- - test/test_helper.rb
174
+ - lib/generators/ahoy_email/templates/install.rb.tt
215
175
  homepage: https://github.com/ankane/ahoy_email
216
176
  licenses:
217
177
  - MIT
@@ -224,24 +184,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
184
  requirements:
225
185
  - - ">="
226
186
  - !ruby/object:Gem::Version
227
- version: 2.2.0
187
+ version: '2.4'
228
188
  required_rubygems_version: !ruby/object:Gem::Requirement
229
189
  requirements:
230
190
  - - ">="
231
191
  - !ruby/object:Gem::Version
232
192
  version: '0'
233
193
  requirements: []
234
- rubyforge_project:
235
- rubygems_version: 2.7.6
194
+ rubygems_version: 3.0.4
236
195
  signing_key:
237
196
  specification_version: 4
238
- summary: Simple, powerful email tracking for Rails
239
- test_files:
240
- - test/gemfiles/actionmailer42.gemfile
241
- - test/gemfiles/actionmailer50.gemfile
242
- - test/gemfiles/actionmailer51.gemfile
243
- - test/internal/config/database.yml
244
- - test/internal/config/routes.rb
245
- - test/internal/db/schema.rb
246
- - test/mailer_test.rb
247
- - test/test_helper.rb
197
+ summary: Email analytics for Rails
198
+ test_files: []
data/.gitignore DELETED
@@ -1,24 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- *.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- *.bundle
19
- *.so
20
- *.o
21
- *.a
22
- mkmf.log
23
- *.log
24
- *.sqlite
data/.travis.yml DELETED
@@ -1,16 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.4.2
4
- sudo: false
5
- script: bundle exec rake test
6
- before_script:
7
- - gem install bundler
8
- notifications:
9
- email:
10
- on_success: never
11
- on_failure: change
12
- gemfile:
13
- - Gemfile
14
- - test/gemfiles/actionmailer51.gemfile
15
- - test/gemfiles/actionmailer50.gemfile
16
- - test/gemfiles/actionmailer42.gemfile
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in ahoy_email.gemspec
4
- gemspec
5
-
6
- gem "actionmailer", "~> 5.2.0"
data/Rakefile DELETED
@@ -1,9 +0,0 @@
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
- t.warning = false
9
- end
data/ahoy_email.gemspec DELETED
@@ -1,35 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "ahoy_email/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "ahoy_email"
8
- spec.version = AhoyEmail::VERSION
9
- spec.authors = ["Andrew Kane"]
10
- spec.email = ["andrew@chartkick.com"]
11
- spec.summary = "Simple, powerful email tracking for Rails"
12
- spec.homepage = "https://github.com/ankane/ahoy_email"
13
- spec.license = "MIT"
14
-
15
- spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
19
-
20
- spec.required_ruby_version = ">= 2.2.0"
21
-
22
- spec.add_runtime_dependency "railties"
23
- spec.add_runtime_dependency "actionmailer"
24
- spec.add_runtime_dependency "activerecord"
25
- spec.add_runtime_dependency "addressable", ">= 2.3.2"
26
- spec.add_runtime_dependency "nokogiri"
27
- spec.add_runtime_dependency "safely_block", ">= 0.1.1"
28
-
29
- spec.add_development_dependency "bundler"
30
- spec.add_development_dependency "rake"
31
- spec.add_development_dependency "minitest"
32
- spec.add_development_dependency "combustion"
33
- spec.add_development_dependency "rails"
34
- spec.add_development_dependency "sqlite3"
35
- end
@@ -1,9 +0,0 @@
1
- module AhoyEmail
2
- class Interceptor
3
- class << self
4
- def delivering_email(message)
5
- AhoyEmail::Processor.new(message).track_send
6
- end
7
- end
8
- end
9
- end
@@ -1,30 +0,0 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
- def change
3
- create_table :ahoy_messages do |t|
4
- t.string :token
5
-
6
- # user
7
- t.text :to
8
- t.references :user, polymorphic: true
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
- end
30
- end
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in ahoy_email.gemspec
4
- gemspec path: "../../"
5
-
6
- gem "actionmailer", "~> 4.2.0"
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in ahoy_email.gemspec
4
- gemspec path: "../../"
5
-
6
- gem "actionmailer", "~> 5.0.0"