ahoy_email 1.0.3 → 2.0.2
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/CHANGELOG.md +56 -22
- data/LICENSE.txt +1 -1
- data/README.md +181 -135
- data/app/controllers/ahoy/messages_controller.rb +27 -45
- data/app/models/ahoy/click.rb +5 -0
- data/app/models/ahoy/message.rb +1 -1
- data/config/routes.rb +3 -0
- data/lib/ahoy_email.rb +34 -9
- data/lib/ahoy_email/database_subscriber.rb +42 -0
- data/lib/ahoy_email/engine.rb +3 -1
- data/lib/ahoy_email/mailer.rb +45 -17
- data/lib/ahoy_email/message_subscriber.rb +12 -0
- data/lib/ahoy_email/processor.rb +24 -43
- data/lib/ahoy_email/redis_subscriber.rb +79 -0
- data/lib/ahoy_email/tracker.rb +9 -3
- data/lib/ahoy_email/utils.rb +35 -0
- data/lib/ahoy_email/version.rb +1 -1
- data/lib/generators/ahoy/clicks/activerecord_generator.rb +20 -0
- data/lib/generators/ahoy/clicks/mongoid_generator.rb +15 -0
- data/lib/generators/ahoy/clicks/templates/migration.rb.tt +11 -0
- data/lib/generators/ahoy/clicks/templates/mongoid.rb.tt +8 -0
- data/lib/generators/ahoy/clicks_generator.rb +37 -0
- data/lib/generators/ahoy/messages/activerecord_generator.rb +40 -0
- data/lib/generators/ahoy/messages/mongoid_generator.rb +21 -0
- data/lib/generators/{ahoy_email/templates/install.rb.tt → ahoy/messages/templates/migration.rb.tt} +2 -2
- data/lib/generators/ahoy/messages/templates/model_encrypted.rb.tt +8 -0
- data/lib/generators/ahoy/messages/templates/mongoid.rb.tt +12 -0
- data/lib/generators/ahoy/messages/templates/mongoid_encrypted.rb.tt +16 -0
- data/lib/generators/ahoy/messages_generator.rb +39 -0
- metadata +29 -113
- data/lib/generators/ahoy_email/install_generator.rb +0 -35
@@ -0,0 +1,79 @@
|
|
1
|
+
module AhoyEmail
|
2
|
+
class RedisSubscriber
|
3
|
+
attr_reader :redis, :prefix
|
4
|
+
|
5
|
+
def initialize(redis: nil, prefix: "ahoy_email")
|
6
|
+
@redis = redis || Redis.new
|
7
|
+
@prefix = prefix
|
8
|
+
end
|
9
|
+
|
10
|
+
def track_send(event)
|
11
|
+
campaign_prefix = campaign_key(event[:campaign])
|
12
|
+
redis.pipelined do
|
13
|
+
redis.incr("#{campaign_prefix}:sends")
|
14
|
+
redis.sadd(campaigns_key, event[:campaign])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def track_click(event)
|
19
|
+
campaign_prefix = campaign_key(event[:campaign])
|
20
|
+
redis.pipelined do
|
21
|
+
redis.incr("#{campaign_prefix}:clicks")
|
22
|
+
redis.pfadd("#{campaign_prefix}:unique_clicks", event[:token])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def stats(campaign = nil)
|
27
|
+
if campaign
|
28
|
+
# return nil instead of zeros if not a campaign
|
29
|
+
if campaign_exists?(campaign)
|
30
|
+
campaign_stats(campaign)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
campaigns.inject({}) do |memo, campaign|
|
34
|
+
memo[campaign] = campaign_stats(campaign)
|
35
|
+
memo
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def campaigns
|
41
|
+
redis.smembers(campaigns_key)
|
42
|
+
end
|
43
|
+
|
44
|
+
def campaign_exists?(campaign)
|
45
|
+
redis.sismember(campaigns_key, campaign)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def campaigns_key
|
51
|
+
"#{prefix}:campaigns"
|
52
|
+
end
|
53
|
+
|
54
|
+
def campaign_key(campaign)
|
55
|
+
"#{prefix}:campaigns:#{campaign}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def campaign_stats(campaign)
|
59
|
+
# scope
|
60
|
+
sends = nil
|
61
|
+
clicks = nil
|
62
|
+
unique_clicks = nil
|
63
|
+
|
64
|
+
campaign_prefix = campaign_key(campaign)
|
65
|
+
redis.pipelined do
|
66
|
+
sends = redis.get("#{campaign_prefix}:sends")
|
67
|
+
clicks = redis.get("#{campaign_prefix}:clicks")
|
68
|
+
unique_clicks = redis.pfcount("#{campaign_prefix}:unique_clicks")
|
69
|
+
end
|
70
|
+
|
71
|
+
{
|
72
|
+
sends: sends.value.to_i,
|
73
|
+
clicks: clicks.value.to_i,
|
74
|
+
unique_clicks: unique_clicks.value,
|
75
|
+
ctr: 100 * unique_clicks.value / sends.value.to_f
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/ahoy_email/tracker.rb
CHANGED
@@ -9,9 +9,15 @@ module AhoyEmail
|
|
9
9
|
def perform
|
10
10
|
Safely.safely do
|
11
11
|
# perform_deliveries check still needed in observer
|
12
|
-
if message.perform_deliveries
|
13
|
-
|
14
|
-
|
12
|
+
if message.perform_deliveries
|
13
|
+
if message.ahoy_data
|
14
|
+
data = message.ahoy_data.merge(message: message)
|
15
|
+
message.ahoy_message = AhoyEmail.track_method.call(data)
|
16
|
+
end
|
17
|
+
|
18
|
+
if message.ahoy_options && message.ahoy_options[:click]
|
19
|
+
Utils.publish(:send, message.ahoy_options.slice(:campaign))
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module AhoyEmail
|
2
|
+
class Utils
|
3
|
+
OPTION_KEYS = {
|
4
|
+
message: %i(message mailer user extra),
|
5
|
+
utm_params: %i(utm_source utm_medium utm_term utm_content utm_campaign),
|
6
|
+
click: %i(campaign url_options unsubscribe_links)
|
7
|
+
}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def signature(token:, campaign:, url:)
|
11
|
+
# encode and join with a character outside encoding
|
12
|
+
data = [token, campaign, url].map { |v| Base64.strict_encode64(v.to_s) }.join("|")
|
13
|
+
|
14
|
+
Base64.urlsafe_encode64(OpenSSL::HMAC.digest("SHA256", secret_token, data), padding: false)
|
15
|
+
end
|
16
|
+
|
17
|
+
def publish(name, event)
|
18
|
+
method_name = "track_#{name}"
|
19
|
+
AhoyEmail.subscribers.each do |subscriber|
|
20
|
+
subscriber = subscriber.new if subscriber.is_a?(Class)
|
21
|
+
if subscriber.respond_to?(method_name)
|
22
|
+
subscriber.send(method_name, event.dup)
|
23
|
+
elsif name == :click && subscriber.respond_to?(:click)
|
24
|
+
# legacy
|
25
|
+
subscriber.send(:click, event.dup)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def secret_token
|
31
|
+
AhoyEmail.secret_token || (raise "Secret token is empty")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/ahoy_email/version.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rails/generators/active_record"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Generators
|
5
|
+
module Clicks
|
6
|
+
class ActiverecordGenerator < Rails::Generators::Base
|
7
|
+
include ActiveRecord::Generators::Migration
|
8
|
+
source_root File.join(__dir__, "templates")
|
9
|
+
|
10
|
+
def copy_migration
|
11
|
+
migration_template "migration.rb", "db/migrate/create_ahoy_clicks.rb", migration_version: migration_version
|
12
|
+
end
|
13
|
+
|
14
|
+
def migration_version
|
15
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Generators
|
5
|
+
module Clicks
|
6
|
+
class MongoidGenerator < Rails::Generators::Base
|
7
|
+
source_root File.join(__dir__, "templates")
|
8
|
+
|
9
|
+
def copy_templates
|
10
|
+
template "mongoid.rb", "app/models/ahoy/click.rb"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
create_table :ahoy_clicks do |t|
|
4
|
+
t.string :campaign, index: true
|
5
|
+
t.string :token
|
6
|
+
end
|
7
|
+
|
8
|
+
add_column :ahoy_messages, :campaign, :string
|
9
|
+
add_index :ahoy_messages, :campaign
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Generators
|
5
|
+
class ClicksGenerator < Rails::Generators::Base
|
6
|
+
def copy_templates
|
7
|
+
activerecord = defined?(ActiveRecord)
|
8
|
+
mongoid = defined?(Mongoid)
|
9
|
+
|
10
|
+
selection =
|
11
|
+
if activerecord && mongoid
|
12
|
+
puts <<-MSG
|
13
|
+
|
14
|
+
Which data store would you like to use?
|
15
|
+
1. Active Record (default)
|
16
|
+
2. Mongoid
|
17
|
+
MSG
|
18
|
+
|
19
|
+
ask(">")
|
20
|
+
elsif activerecord
|
21
|
+
"1"
|
22
|
+
else
|
23
|
+
"2"
|
24
|
+
end
|
25
|
+
|
26
|
+
case selection
|
27
|
+
when "", "1"
|
28
|
+
invoke "ahoy:clicks:activerecord"
|
29
|
+
when "2"
|
30
|
+
invoke "ahoy:clicks:mongoid"
|
31
|
+
else
|
32
|
+
abort "Error: must enter a number [1-2]"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rails/generators/active_record"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Generators
|
5
|
+
module Messages
|
6
|
+
class ActiverecordGenerator < Rails::Generators::Base
|
7
|
+
include ActiveRecord::Generators::Migration
|
8
|
+
source_root File.join(__dir__, "templates")
|
9
|
+
|
10
|
+
class_option :unencrypted, type: :boolean
|
11
|
+
|
12
|
+
def copy_migration
|
13
|
+
migration_template "migration.rb", "db/migrate/create_ahoy_messages.rb", migration_version: migration_version
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_template
|
17
|
+
if encrypted?
|
18
|
+
template "model_encrypted.rb", "app/models/ahoy/message.rb"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def migration_version
|
23
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_column
|
27
|
+
if encrypted?
|
28
|
+
"t.text :to_ciphertext\n t.string :to_bidx, index: true"
|
29
|
+
else
|
30
|
+
"t.string :to, index: true"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def encrypted?
|
35
|
+
!options[:unencrypted]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Generators
|
5
|
+
module Messages
|
6
|
+
class MongoidGenerator < Rails::Generators::Base
|
7
|
+
source_root File.join(__dir__, "templates")
|
8
|
+
|
9
|
+
class_option :unencrypted, type: :boolean
|
10
|
+
|
11
|
+
def copy_templates
|
12
|
+
if options[:unencrypted]
|
13
|
+
template "mongoid.rb", "app/models/ahoy/message.rb"
|
14
|
+
else
|
15
|
+
template "mongoid_encrypted.rb", "app/models/ahoy/message.rb"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/generators/{ahoy_email/templates/install.rb.tt → ahoy/messages/templates/migration.rb.tt}
RENAMED
@@ -2,10 +2,10 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
2
2
|
def change
|
3
3
|
create_table :ahoy_messages do |t|
|
4
4
|
t.references :user, polymorphic: true
|
5
|
-
|
5
|
+
<%= to_column %>
|
6
6
|
t.string :mailer
|
7
7
|
t.text :subject
|
8
|
-
t.
|
8
|
+
t.datetime :sent_at
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Ahoy::Message
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
belongs_to :user, polymorphic: true, optional: true, index: true
|
5
|
+
|
6
|
+
field :to, type: String
|
7
|
+
field :mailer, type: String
|
8
|
+
field :subject, type: String
|
9
|
+
field :sent_at, type: Time
|
10
|
+
|
11
|
+
index({to: 1})
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Ahoy::Message
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
belongs_to :user, polymorphic: true, optional: true, index: true
|
5
|
+
|
6
|
+
field :to_ciphertext, type: String
|
7
|
+
field :to_bidx, type: String
|
8
|
+
field :mailer, type: String
|
9
|
+
field :subject, type: String
|
10
|
+
field :sent_at, type: Time
|
11
|
+
|
12
|
+
index({to_bidx: 1})
|
13
|
+
|
14
|
+
encrypts :to
|
15
|
+
blind_index :to
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Ahoy
|
4
|
+
module Generators
|
5
|
+
class MessagesGenerator < Rails::Generators::Base
|
6
|
+
class_option :unencrypted, type: :boolean
|
7
|
+
|
8
|
+
def copy_templates
|
9
|
+
activerecord = defined?(ActiveRecord)
|
10
|
+
mongoid = defined?(Mongoid)
|
11
|
+
|
12
|
+
selection =
|
13
|
+
if activerecord && mongoid
|
14
|
+
puts <<-MSG
|
15
|
+
|
16
|
+
Which data store would you like to use?
|
17
|
+
1. Active Record (default)
|
18
|
+
2. Mongoid
|
19
|
+
MSG
|
20
|
+
|
21
|
+
ask(">")
|
22
|
+
elsif activerecord
|
23
|
+
"1"
|
24
|
+
else
|
25
|
+
"2"
|
26
|
+
end
|
27
|
+
|
28
|
+
case selection
|
29
|
+
when "", "1"
|
30
|
+
invoke "ahoy:messages:activerecord"
|
31
|
+
when "2"
|
32
|
+
invoke "ahoy:messages:mongoid"
|
33
|
+
else
|
34
|
+
abort "Error: must enter a number [1-2]"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
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:
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionmailer
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: addressable
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,106 +66,8 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.1.1
|
69
|
-
|
70
|
-
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rake
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
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: activerecord
|
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: combustion
|
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'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: rails
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: sqlite3
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - "~>"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: 1.3.0
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - "~>"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: 1.3.0
|
167
|
-
description:
|
168
|
-
email: andrew@chartkick.com
|
69
|
+
description:
|
70
|
+
email: andrew@ankane.org
|
169
71
|
executables: []
|
170
72
|
extensions: []
|
171
73
|
extra_rdoc_files: []
|
@@ -175,22 +77,37 @@ files:
|
|
175
77
|
- LICENSE.txt
|
176
78
|
- README.md
|
177
79
|
- app/controllers/ahoy/messages_controller.rb
|
80
|
+
- app/models/ahoy/click.rb
|
178
81
|
- app/models/ahoy/message.rb
|
179
82
|
- config/routes.rb
|
180
83
|
- lib/ahoy_email.rb
|
84
|
+
- lib/ahoy_email/database_subscriber.rb
|
181
85
|
- lib/ahoy_email/engine.rb
|
182
86
|
- lib/ahoy_email/mailer.rb
|
87
|
+
- lib/ahoy_email/message_subscriber.rb
|
183
88
|
- lib/ahoy_email/observer.rb
|
184
89
|
- lib/ahoy_email/processor.rb
|
90
|
+
- lib/ahoy_email/redis_subscriber.rb
|
185
91
|
- lib/ahoy_email/tracker.rb
|
92
|
+
- lib/ahoy_email/utils.rb
|
186
93
|
- lib/ahoy_email/version.rb
|
187
|
-
- lib/generators/
|
188
|
-
- lib/generators/
|
94
|
+
- lib/generators/ahoy/clicks/activerecord_generator.rb
|
95
|
+
- lib/generators/ahoy/clicks/mongoid_generator.rb
|
96
|
+
- lib/generators/ahoy/clicks/templates/migration.rb.tt
|
97
|
+
- lib/generators/ahoy/clicks/templates/mongoid.rb.tt
|
98
|
+
- lib/generators/ahoy/clicks_generator.rb
|
99
|
+
- lib/generators/ahoy/messages/activerecord_generator.rb
|
100
|
+
- lib/generators/ahoy/messages/mongoid_generator.rb
|
101
|
+
- lib/generators/ahoy/messages/templates/migration.rb.tt
|
102
|
+
- lib/generators/ahoy/messages/templates/model_encrypted.rb.tt
|
103
|
+
- lib/generators/ahoy/messages/templates/mongoid.rb.tt
|
104
|
+
- lib/generators/ahoy/messages/templates/mongoid_encrypted.rb.tt
|
105
|
+
- lib/generators/ahoy/messages_generator.rb
|
189
106
|
homepage: https://github.com/ankane/ahoy_email
|
190
107
|
licenses:
|
191
108
|
- MIT
|
192
109
|
metadata: {}
|
193
|
-
post_install_message:
|
110
|
+
post_install_message:
|
194
111
|
rdoc_options: []
|
195
112
|
require_paths:
|
196
113
|
- lib
|
@@ -198,16 +115,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
198
115
|
requirements:
|
199
116
|
- - ">="
|
200
117
|
- !ruby/object:Gem::Version
|
201
|
-
version: '2.
|
118
|
+
version: '2.6'
|
202
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
120
|
requirements:
|
204
121
|
- - ">="
|
205
122
|
- !ruby/object:Gem::Version
|
206
123
|
version: '0'
|
207
124
|
requirements: []
|
208
|
-
|
209
|
-
|
210
|
-
signing_key:
|
125
|
+
rubygems_version: 3.2.3
|
126
|
+
signing_key:
|
211
127
|
specification_version: 4
|
212
|
-
summary:
|
128
|
+
summary: First-party email analytics for Rails
|
213
129
|
test_files: []
|