ahoy_email 0.5.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"