caffeinate 0.9 → 0.14.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 +4 -4
- data/app/models/caffeinate/campaign_subscription.rb +16 -6
- data/app/models/caffeinate/mailing.rb +6 -0
- data/lib/caffeinate/action_mailer/interceptor.rb +0 -1
- data/lib/caffeinate/configuration.rb +10 -1
- data/lib/caffeinate/drip.rb +37 -6
- data/lib/caffeinate/drip_evaluator.rb +2 -2
- data/lib/caffeinate/dripper/callbacks.rb +33 -3
- data/lib/caffeinate/dripper/delivery.rb +6 -1
- data/lib/caffeinate/dripper/perform.rb +9 -5
- data/lib/caffeinate/version.rb +1 -1
- data/lib/generators/caffeinate/install_generator.rb +13 -5
- data/lib/generators/caffeinate/templates/caffeinate.rb +11 -0
- data/{db/migrate/20201124183303_create_caffeinate_campaign_subscriptions.rb → lib/generators/caffeinate/templates/migrations/create_caffeinate_campaign_subscriptions.rb.tt} +1 -1
- data/{db/migrate/20201124183102_create_caffeinate_campaigns.rb → lib/generators/caffeinate/templates/migrations/create_caffeinate_campaigns.rb.tt} +1 -2
- data/{db/migrate/20201124183419_create_caffeinate_mailings.rb → lib/generators/caffeinate/templates/migrations/create_caffeinate_mailings.rb.tt} +1 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8ae5ac5375188ce5a7bc8960ac7d7b392ccc7dc7de71070e88a2285fa76e7af
|
4
|
+
data.tar.gz: dd06d837c2c1477bf8e48484e028016f00203066f0b2383c6f0a7a3cb5b5af1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c71b3ba2196a9a9f7ae66074b8eeb76ad6fe1a6b2ecff521e7f92c06da411c4722e0bc8b4a49fcfc9eaf0fde921c76fd4b13fbb5c7b59cfe1ec173ea69e5b29
|
7
|
+
data.tar.gz: 498848318785a2f8f4ab669cada385760f63663b1c6bf254235a7f640c189eaa170391aff58990c05c6a4d8ee6a6dc860399ea24745431dec7dfebaa8d760533
|
@@ -28,9 +28,13 @@ module Caffeinate
|
|
28
28
|
|
29
29
|
has_many :caffeinate_mailings, class_name: 'Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id, dependent: :destroy
|
30
30
|
has_many :mailings, class_name: 'Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id, dependent: :destroy
|
31
|
+
has_many :future_mailings, -> { upcoming.unsent }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
31
32
|
|
32
|
-
has_one :next_caffeinate_mailing, -> { upcoming.unsent.order(send_at: :asc) }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
33
|
-
has_one :next_mailing, -> { upcoming.unsent.order(send_at: :asc) }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
33
|
+
has_one :next_caffeinate_mailing, -> { joins(:caffeinate_campaign_subscription).where(caffeinate_campaign_subscriptions: { ended_at: nil, unsubscribed_at: nil }).upcoming.unsent.order(send_at: :asc) }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
34
|
+
has_one :next_mailing, -> { joins(:caffeinate_campaign_subscription).where(caffeinate_campaign_subscriptions: { ended_at: nil, unsubscribed_at: nil }).upcoming.unsent.order(send_at: :asc) }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
35
|
+
|
36
|
+
has_one :previous_caffeinate_mailing, -> { sent.order(sent_at: :desc) }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
37
|
+
has_one :previous_mailing, -> { sent.order(sent_at: :desc) }, class_name: '::Caffeinate::Mailing', foreign_key: :caffeinate_campaign_subscription_id
|
34
38
|
|
35
39
|
belongs_to :caffeinate_campaign, class_name: 'Caffeinate::Campaign', foreign_key: :caffeinate_campaign_id
|
36
40
|
alias_attribute :campaign, :caffeinate_campaign
|
@@ -49,6 +53,8 @@ module Caffeinate
|
|
49
53
|
before_validation :set_token!, on: [:create]
|
50
54
|
validates :token, uniqueness: true, on: [:create]
|
51
55
|
|
56
|
+
before_validation :call_dripper_before_subscribe_blocks!, on: :create
|
57
|
+
|
52
58
|
after_commit :create_mailings!, on: :create
|
53
59
|
|
54
60
|
after_commit :on_complete, if: :completed?
|
@@ -74,7 +80,7 @@ module Caffeinate
|
|
74
80
|
end
|
75
81
|
|
76
82
|
# Updates `ended_at` and runs `on_complete` callbacks
|
77
|
-
def end!(reason =
|
83
|
+
def end!(reason = ::Caffeinate.config.default_ended_reason)
|
78
84
|
raise ::Caffeinate::InvalidState, 'CampaignSubscription is already unsubscribed.' if unsubscribed?
|
79
85
|
|
80
86
|
update!(ended_at: ::Caffeinate.config.time_now, ended_reason: reason)
|
@@ -84,7 +90,7 @@ module Caffeinate
|
|
84
90
|
end
|
85
91
|
|
86
92
|
# Updates `ended_at` and runs `on_complete` callbacks
|
87
|
-
def end(reason =
|
93
|
+
def end(reason = ::Caffeinate.config.default_ended_reason)
|
88
94
|
return false if unsubscribed?
|
89
95
|
|
90
96
|
result = update(ended_at: ::Caffeinate.config.time_now, ended_reason: reason)
|
@@ -94,7 +100,7 @@ module Caffeinate
|
|
94
100
|
end
|
95
101
|
|
96
102
|
# Updates `unsubscribed_at` and runs `on_subscribe` callbacks
|
97
|
-
def unsubscribe!(reason =
|
103
|
+
def unsubscribe!(reason = ::Caffeinate.config.default_unsubscribe_reason)
|
98
104
|
raise ::Caffeinate::InvalidState, 'CampaignSubscription is already ended.' if ended?
|
99
105
|
|
100
106
|
update!(unsubscribed_at: ::Caffeinate.config.time_now, unsubscribe_reason: reason)
|
@@ -104,7 +110,7 @@ module Caffeinate
|
|
104
110
|
end
|
105
111
|
|
106
112
|
# Updates `unsubscribed_at` and runs `on_subscribe` callbacks
|
107
|
-
def unsubscribe(reason =
|
113
|
+
def unsubscribe(reason = ::Caffeinate.config.default_unsubscribe_reason)
|
108
114
|
return false if ended?
|
109
115
|
|
110
116
|
result = update(unsubscribed_at: ::Caffeinate.config.time_now, unsubscribe_reason: reason)
|
@@ -143,6 +149,10 @@ module Caffeinate
|
|
143
149
|
|
144
150
|
private
|
145
151
|
|
152
|
+
def call_dripper_before_subscribe_blocks!
|
153
|
+
caffeinate_campaign.to_dripper.run_callbacks(:before_subscribe, self)
|
154
|
+
end
|
155
|
+
|
146
156
|
def on_complete
|
147
157
|
caffeinate_campaign.to_dripper.run_callbacks(:on_complete, self)
|
148
158
|
end
|
@@ -30,6 +30,8 @@ module Caffeinate
|
|
30
30
|
scope :skipped, -> { where.not(skipped_at: nil) }
|
31
31
|
scope :unskipped, -> { where(skipped_at: nil) }
|
32
32
|
|
33
|
+
after_touch :end_if_no_mailings!
|
34
|
+
|
33
35
|
def initialize_dup(args)
|
34
36
|
super
|
35
37
|
self.send_at = nil
|
@@ -119,5 +121,9 @@ module Caffeinate
|
|
119
121
|
raise NoMethodError, "Neither perform_later or perform_async are defined on #{klass}."
|
120
122
|
end
|
121
123
|
end
|
124
|
+
|
125
|
+
def end_if_no_mailings!
|
126
|
+
end! if future_mailings.empty?
|
127
|
+
end
|
122
128
|
end
|
123
129
|
end
|
@@ -5,7 +5,6 @@ module Caffeinate
|
|
5
5
|
# Handles the evaluation of a drip against a mailing to determine if it ultimately gets delivered.
|
6
6
|
# Also invokes the `before_send` callbacks.
|
7
7
|
class Interceptor
|
8
|
-
# Handles `before_send` callbacks for a `Caffeinate::Dripper`
|
9
8
|
def self.delivering_email(message)
|
10
9
|
mailing = message.caffeinate_mailing
|
11
10
|
return unless mailing
|
@@ -3,15 +3,19 @@
|
|
3
3
|
module Caffeinate
|
4
4
|
# Global configuration
|
5
5
|
class Configuration
|
6
|
-
attr_accessor :now, :async_delivery, :mailing_job, :batch_size, :drippers_path, :implicit_campaigns
|
6
|
+
attr_accessor :now, :async_delivery, :deliver_later,:mailing_job, :batch_size, :drippers_path, :implicit_campaigns,
|
7
|
+
:default_ended_reason, :default_unsubscribe_reason
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
@now = -> { Time.current }
|
10
11
|
@async_delivery = false
|
12
|
+
@deliver_later = false
|
11
13
|
@mailing_job = nil
|
12
14
|
@batch_size = 1_000
|
13
15
|
@drippers_path = 'app/drippers'
|
14
16
|
@implicit_campaigns = true
|
17
|
+
@default_ended_reason = nil
|
18
|
+
@default_unsubscribe_reason = nil
|
15
19
|
end
|
16
20
|
|
17
21
|
def now=(val)
|
@@ -35,6 +39,11 @@ module Caffeinate
|
|
35
39
|
@async_delivery
|
36
40
|
end
|
37
41
|
|
42
|
+
# If we should use `#deliver_later` instead of `#deliver`
|
43
|
+
def deliver_later?
|
44
|
+
@deliver_later
|
45
|
+
end
|
46
|
+
|
38
47
|
# The @mailing_job constantized. Only used if `async_delivery = true`
|
39
48
|
def mailing_job_class
|
40
49
|
@mailing_job.constantize
|
data/lib/caffeinate/drip.rb
CHANGED
@@ -6,6 +6,26 @@ module Caffeinate
|
|
6
6
|
#
|
7
7
|
# Handles the block and provides convenience methods for the drip
|
8
8
|
class Drip
|
9
|
+
class OptionEvaluator
|
10
|
+
def initialize(thing, drip, mailing)
|
11
|
+
@thing = thing
|
12
|
+
@drip = drip
|
13
|
+
@mailing = mailing
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
if @thing.is_a?(Symbol)
|
18
|
+
@drip.dripper.new.send(@thing, @drip, @mailing)
|
19
|
+
elsif @thing.is_a?(Proc)
|
20
|
+
@mailing.instance_exec(&@thing)
|
21
|
+
elsif @thing.is_a?(String)
|
22
|
+
Time.parse(@thing)
|
23
|
+
else
|
24
|
+
@thing
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
9
29
|
attr_reader :dripper, :action, :options, :block
|
10
30
|
|
11
31
|
def initialize(dripper, action, options, &block)
|
@@ -26,13 +46,16 @@ module Caffeinate
|
|
26
46
|
start += options[:every] if mailing.caffeinate_campaign_subscription.caffeinate_mailings.count.positive?
|
27
47
|
date = start.from_now
|
28
48
|
elsif options[:on]
|
29
|
-
date =
|
49
|
+
date = OptionEvaluator.new(options[:on], self, mailing).call
|
30
50
|
else
|
31
|
-
date = options[:delay].
|
51
|
+
date = OptionEvaluator.new(options[:delay], self, mailing).call
|
52
|
+
if date.respond_to?(:from_now)
|
53
|
+
date = date.from_now
|
54
|
+
end
|
32
55
|
end
|
33
56
|
|
34
57
|
if options[:at]
|
35
|
-
time =
|
58
|
+
time = OptionEvaluator.new(options[:at], self, mailing).call
|
36
59
|
return date.change(hour: time.hour, min: time.min, sec: time.sec)
|
37
60
|
end
|
38
61
|
|
@@ -44,10 +67,18 @@ module Caffeinate
|
|
44
67
|
end
|
45
68
|
|
46
69
|
# Checks if the drip is enabled
|
70
|
+
#
|
71
|
+
# This is kind of messy and could use some love.
|
72
|
+
# todo: better.
|
47
73
|
def enabled?(mailing)
|
48
|
-
|
49
|
-
|
50
|
-
|
74
|
+
catch(:abort) do
|
75
|
+
if dripper.run_callbacks(:before_drip, self, mailing)
|
76
|
+
return DripEvaluator.new(mailing).call(&@block)
|
77
|
+
else
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
false
|
51
82
|
end
|
52
83
|
end
|
53
84
|
end
|
@@ -13,12 +13,33 @@ module Caffeinate
|
|
13
13
|
self.class.run_callbacks(name, *args)
|
14
14
|
end
|
15
15
|
|
16
|
+
def callbacks_for(name)
|
17
|
+
self.class.callbacks_for(name)
|
18
|
+
end
|
19
|
+
|
16
20
|
module ClassMethods
|
17
21
|
# :nodoc:
|
18
22
|
def run_callbacks(name, *args)
|
19
|
-
|
20
|
-
|
23
|
+
catch(:abort) do
|
24
|
+
callbacks_for(name).each do |callback|
|
25
|
+
callback.call(*args)
|
26
|
+
end
|
27
|
+
return true
|
21
28
|
end
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
# :nodoc:
|
33
|
+
def callbacks_for(name)
|
34
|
+
send("#{name}_blocks")
|
35
|
+
end
|
36
|
+
|
37
|
+
def before_subscribe(&block)
|
38
|
+
before_subscribe_blocks << block
|
39
|
+
end
|
40
|
+
|
41
|
+
def before_subscribe_blocks
|
42
|
+
@before_subscribe_blocks ||= []
|
22
43
|
end
|
23
44
|
|
24
45
|
# Callback after a Caffeinate::CampaignSubscription is created, and after the Caffeinate::Mailings have
|
@@ -106,10 +127,19 @@ module Caffeinate
|
|
106
127
|
|
107
128
|
# Callback before a Drip has called the mailer.
|
108
129
|
#
|
109
|
-
# before_drip do |
|
130
|
+
# before_drip do |drip, mailing|
|
110
131
|
# Slack.notify(:caffeinate, "#{drip.action_name} is starting")
|
111
132
|
# end
|
112
133
|
#
|
134
|
+
# Note: If you want to bail on the mailing for some reason, you need invoke `throw(:abort)`
|
135
|
+
#
|
136
|
+
# before_drip do |drip, mailing|
|
137
|
+
# if mailing.caffeinate_campaign_subscription.subscriber.trial_ended?
|
138
|
+
# unsubscribe!("Trial ended")
|
139
|
+
# throw(:abort)
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
#
|
113
143
|
# @yield Caffeinate::Drip current drip
|
114
144
|
# @yield Caffeinate::Mailing
|
115
145
|
def before_drip(&block)
|
@@ -20,7 +20,12 @@ module Caffeinate
|
|
20
20
|
mailing.mailer_class.constantize.send(mailing.mailer_action, mailing)
|
21
21
|
end
|
22
22
|
message.caffeinate_mailing = mailing
|
23
|
-
|
23
|
+
if ::Caffeinate.config.deliver_later?
|
24
|
+
message.deliver_later
|
25
|
+
else
|
26
|
+
message.deliver
|
27
|
+
end
|
28
|
+
|
24
29
|
end
|
25
30
|
end
|
26
31
|
end
|
@@ -16,11 +16,7 @@ module Caffeinate
|
|
16
16
|
# @return nil
|
17
17
|
def perform!
|
18
18
|
run_callbacks(:before_perform, self)
|
19
|
-
|
20
|
-
.upcoming
|
21
|
-
.unsent
|
22
|
-
.joins(:caffeinate_campaign_subscription)
|
23
|
-
.merge(Caffeinate::CampaignSubscription.active.where(caffeinate_campaign: self.campaign))
|
19
|
+
self.class.upcoming_mailings
|
24
20
|
.in_batches(of: self.class.batch_size)
|
25
21
|
.each do |batch|
|
26
22
|
run_callbacks(:on_perform, self, batch)
|
@@ -35,6 +31,14 @@ module Caffeinate
|
|
35
31
|
def perform!
|
36
32
|
new.perform!
|
37
33
|
end
|
34
|
+
|
35
|
+
def upcoming_mailings
|
36
|
+
Caffeinate::Mailing
|
37
|
+
.upcoming
|
38
|
+
.unsent
|
39
|
+
.joins(:caffeinate_campaign_subscription)
|
40
|
+
.merge(Caffeinate::CampaignSubscription.active.where(caffeinate_campaign: campaign))
|
41
|
+
end
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
data/lib/caffeinate/version.rb
CHANGED
@@ -5,7 +5,6 @@ module Caffeinate
|
|
5
5
|
# Installs Caffeinate
|
6
6
|
class InstallGenerator < Rails::Generators::Base
|
7
7
|
source_root File.expand_path('templates', __dir__)
|
8
|
-
include ::Rails::Generators::Migration
|
9
8
|
|
10
9
|
desc 'Creates a Caffeinate initializer and copies migrations to your application.'
|
11
10
|
|
@@ -33,12 +32,21 @@ module Caffeinate
|
|
33
32
|
@prev_migration_nr.to_s
|
34
33
|
end
|
35
34
|
|
35
|
+
def migration_version
|
36
|
+
if rails5_and_up?
|
37
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def rails5_and_up?
|
42
|
+
Rails::VERSION::MAJOR >= 5
|
43
|
+
end
|
44
|
+
|
36
45
|
# :nodoc:
|
37
46
|
def copy_migrations
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
Rake::Task['caffeinate:install:migrations'].invoke
|
47
|
+
template 'migrations/create_caffeinate_campaigns.rb', "db/migrate/#{self.class.next_migration_number("")}_create_caffeinate_campaigns.rb"
|
48
|
+
template 'migrations/create_caffeinate_campaign_subscriptions.rb', "db/migrate/#{self.class.next_migration_number("")}_create_caffeinate_campaign_subscriptions.rb"
|
49
|
+
template 'migrations/create_caffeinate_mailings.rb', "db/migrate/#{self.class.next_migration_number("")}_create_caffeinate_mailings.rb"
|
42
50
|
end
|
43
51
|
end
|
44
52
|
end
|
@@ -41,4 +41,15 @@ Caffeinate.setup do |config|
|
|
41
41
|
# config.implicit_campaigns = true
|
42
42
|
#
|
43
43
|
# config.implicit_campaigns = false
|
44
|
+
#
|
45
|
+
# == Default reasons
|
46
|
+
#
|
47
|
+
# The default unsubscribe and end reasons.
|
48
|
+
#
|
49
|
+
# Default:
|
50
|
+
# config.default_unsubscribe_reason = nil
|
51
|
+
# config.default_ended_reason = nil
|
52
|
+
#
|
53
|
+
# config.default_unsubscribe_reason = "User unsubscribed"
|
54
|
+
# config.default_ended_reason = "User ended"
|
44
55
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class CreateCaffeinateCampaignSubscriptions < ActiveRecord::Migration
|
3
|
+
class CreateCaffeinateCampaignSubscriptions < ActiveRecord::Migration<%= migration_version %>
|
4
4
|
def change
|
5
5
|
drop_table :caffeinate_campaign_subscriptions if table_exists?(:caffeinate_campaign_subscriptions)
|
6
6
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class CreateCaffeinateCampaigns < ActiveRecord::Migration
|
3
|
+
class CreateCaffeinateCampaigns < ActiveRecord::Migration<%= migration_version %>
|
4
4
|
def change
|
5
|
-
drop_table :caffeinate_campaigns if table_exists?(:caffeinate_campaigns)
|
6
5
|
create_table :caffeinate_campaigns do |t|
|
7
6
|
t.string :name, null: false
|
8
7
|
t.string :slug, null: false
|
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class CreateCaffeinateMailings < ActiveRecord::Migration
|
3
|
+
class CreateCaffeinateMailings < ActiveRecord::Migration<%= migration_version %>
|
4
4
|
def change
|
5
|
-
drop_table :caffeinate_mailings if table_exists?(:caffeinate_mailings)
|
6
|
-
|
7
5
|
create_table :caffeinate_mailings do |t|
|
8
6
|
t.references :caffeinate_campaign_subscription, null: false, foreign_key: true, index: { name: 'index_caffeinate_mailings_on_campaign_subscription' }
|
9
7
|
t.datetime :send_at
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caffeinate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Brody
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -143,9 +143,6 @@ files:
|
|
143
143
|
- app/views/layouts/_caffeinate.html.erb
|
144
144
|
- config/locales/en.yml
|
145
145
|
- config/routes.rb
|
146
|
-
- db/migrate/20201124183102_create_caffeinate_campaigns.rb
|
147
|
-
- db/migrate/20201124183303_create_caffeinate_campaign_subscriptions.rb
|
148
|
-
- db/migrate/20201124183419_create_caffeinate_mailings.rb
|
149
146
|
- lib/caffeinate.rb
|
150
147
|
- lib/caffeinate/action_mailer.rb
|
151
148
|
- lib/caffeinate/action_mailer/extension.rb
|
@@ -179,6 +176,9 @@ files:
|
|
179
176
|
- lib/generators/caffeinate/templates/application_dripper.rb
|
180
177
|
- lib/generators/caffeinate/templates/caffeinate.rb
|
181
178
|
- lib/generators/caffeinate/templates/mailer.rb.tt
|
179
|
+
- lib/generators/caffeinate/templates/migrations/create_caffeinate_campaign_subscriptions.rb.tt
|
180
|
+
- lib/generators/caffeinate/templates/migrations/create_caffeinate_campaigns.rb.tt
|
181
|
+
- lib/generators/caffeinate/templates/migrations/create_caffeinate_mailings.rb.tt
|
182
182
|
- lib/generators/caffeinate/views_generator.rb
|
183
183
|
homepage: https://github.com/joshmn/caffeinate
|
184
184
|
licenses:
|