caffeinate 0.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +125 -0
- data/Rakefile +24 -0
- data/app/controllers/caffeinate/application_controller.rb +8 -0
- data/app/controllers/caffeinate/campaign_subscriptions_controller.rb +19 -0
- data/app/models/caffeinate/application_record.rb +8 -0
- data/app/models/caffeinate/campaign.rb +20 -0
- data/app/models/caffeinate/campaign_subscription.rb +82 -0
- data/app/models/caffeinate/mailing.rb +99 -0
- data/app/views/layouts/caffeinate/application.html.erb +15 -0
- data/app/views/layouts/caffeinate/campaign_subscriptions/unsubscribe.html.erb +1 -0
- data/config/routes.rb +10 -0
- data/db/migrate/20201124183102_create_caffeinate_campaigns.rb +14 -0
- data/db/migrate/20201124183303_create_caffeinate_campaign_subscriptions.rb +22 -0
- data/db/migrate/20201124183419_create_caffeinate_mailings.rb +19 -0
- data/lib/caffeinate.rb +30 -0
- data/lib/caffeinate/action_mailer.rb +6 -0
- data/lib/caffeinate/action_mailer/extension.rb +13 -0
- data/lib/caffeinate/action_mailer/helpers.rb +12 -0
- data/lib/caffeinate/action_mailer/interceptor.rb +17 -0
- data/lib/caffeinate/action_mailer/observer.rb +17 -0
- data/lib/caffeinate/active_record/extension.rb +33 -0
- data/lib/caffeinate/configuration.rb +34 -0
- data/lib/caffeinate/deliver_async.rb +22 -0
- data/lib/caffeinate/drip.rb +56 -0
- data/lib/caffeinate/dripper/base.rb +33 -0
- data/lib/caffeinate/dripper/callbacks.rb +141 -0
- data/lib/caffeinate/dripper/campaign.rb +53 -0
- data/lib/caffeinate/dripper/defaults.rb +32 -0
- data/lib/caffeinate/dripper/delivery.rb +28 -0
- data/lib/caffeinate/dripper/drip.rb +65 -0
- data/lib/caffeinate/dripper/perform.rb +32 -0
- data/lib/caffeinate/dripper/subscriber.rb +66 -0
- data/lib/caffeinate/engine.rb +21 -0
- data/lib/caffeinate/version.rb +5 -0
- data/lib/generators/caffeinate/install_generator.rb +41 -0
- data/lib/generators/caffeinate/templates/application_campaign.rb +4 -0
- data/lib/generators/caffeinate/templates/caffeinate.rb +24 -0
- metadata +185 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
module Campaign
|
6
|
+
# :nodoc:
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
# The campaign for this Dripper
|
12
|
+
#
|
13
|
+
# @return Caffeinate::Campaign
|
14
|
+
def campaign
|
15
|
+
self.class.caffeinate_campaign
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
# Sets the campaign on the Dripper and resets any existing `@caffeinate_campaign`
|
20
|
+
#
|
21
|
+
# class OrdersDripper
|
22
|
+
# campaign :order_drip
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# If this is not explicitly set, we will infer it with
|
26
|
+
#
|
27
|
+
# self.name.delete_suffix("Campaign").underscore
|
28
|
+
#
|
29
|
+
# @param [Symbol] slug The slug of a persisted `Caffeinate::Campaign`.
|
30
|
+
def campaign(slug)
|
31
|
+
@caffeinate_campaign = nil
|
32
|
+
@_campaign_slug = slug.to_sym
|
33
|
+
Caffeinate.register_dripper(@_campaign_slug, name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the `Caffeinate::Campaign` object for the Campaign
|
37
|
+
def caffeinate_campaign
|
38
|
+
return @caffeinate_campaign if @caffeinate_campaign.present?
|
39
|
+
|
40
|
+
@caffeinate_campaign = ::Caffeinate::Campaign.find_by(slug: campaign_slug)
|
41
|
+
return @caffeinate_campaign if @caffeinate_campaign
|
42
|
+
|
43
|
+
raise(::ActiveRecord::RecordNotFound, "Unable to find ::Caffeinate::Campaign with slug #{campaign_slug}.")
|
44
|
+
end
|
45
|
+
|
46
|
+
# The defined slug or the inferred slug
|
47
|
+
def campaign_slug
|
48
|
+
@_campaign_slug || name.delete_suffix('Campaign')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
module Defaults
|
6
|
+
# :nodoc:
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# The defaults set in the Campaign
|
13
|
+
def defaults
|
14
|
+
@defaults ||= { mailer_class: inferred_mailer_class }
|
15
|
+
end
|
16
|
+
|
17
|
+
# The default options for the Campaign
|
18
|
+
#
|
19
|
+
# class OrderCampaign
|
20
|
+
# default mailer_class: "OrdersMailer"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @param [Hash] options The options to set defaults with
|
24
|
+
# @option options [String] :mailer_class The mailer class
|
25
|
+
def default(options = {})
|
26
|
+
options.assert_valid_keys(:mailer_class, :mailer)
|
27
|
+
@defaults = options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
# Handles delivery of a Caffeinate::Mailer for a Caffeinate::Dripper
|
6
|
+
module Delivery
|
7
|
+
# :nodoc:
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Delivers the given Caffeinate::Mailing
|
14
|
+
#
|
15
|
+
# @param [Caffeinate::Mailing] mailing The mailing to deliver
|
16
|
+
def deliver!(mailing)
|
17
|
+
Thread.current[:current_caffeinate_mailing] = mailing
|
18
|
+
|
19
|
+
if mailing.drip.parameterized?
|
20
|
+
mailing.mailer_class.constantize.with(mailing: mailing).send(mailing.mailer_action).deliver
|
21
|
+
else
|
22
|
+
mailing.mailer_class.constantize.send(mailing.mailer_action, mailing).deliver
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
# The Drip DSL for registering a drip
|
6
|
+
module Drip
|
7
|
+
# A collection of Drip objects for a `Caffeinate::Dripper`
|
8
|
+
class DripCollection
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def initialize(dripper)
|
12
|
+
@dripper = dripper
|
13
|
+
@drips = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Register the drip
|
17
|
+
def register(action, options, &block)
|
18
|
+
@drips << ::Caffeinate::Drip.new(@dripper, action, options, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def each(&block)
|
22
|
+
@drips.each { |drip| block.call(drip) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
@drips.size
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# :nodoc:
|
31
|
+
def self.included(klass)
|
32
|
+
klass.extend ClassMethods
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
# A collection of Drip objects associated with a given `Caffeinate::Dripper`
|
37
|
+
def drips
|
38
|
+
@drips ||= DripCollection.new(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Register a drip on the Dripper
|
42
|
+
#
|
43
|
+
# drip :mailer_action_name, mailer_class: "MailerClass", step: 1, delay: 1.hour
|
44
|
+
#
|
45
|
+
# @param action_name [Symbol] the name of the mailer action
|
46
|
+
# @param [Hash] options the options to create a drip with
|
47
|
+
# @option options [String] :mailer_class The mailer_class
|
48
|
+
# @option options [Integer] :step The order in which the drip is executed
|
49
|
+
# @option options [ActiveSupport::Duration] :delay When the drip should be ran
|
50
|
+
def drip(action_name, options = {}, &block)
|
51
|
+
options.assert_valid_keys(:mailer_class, :step, :delay, :using, :mailer)
|
52
|
+
options[:mailer_class] ||= options[:mailer] || defaults[:mailer_class]
|
53
|
+
options[:step] ||= drips.size + 1
|
54
|
+
|
55
|
+
if options[:mailer_class].nil?
|
56
|
+
raise ArgumentError, "You must define :mailer_class or :mailer in the options for :#{action_name}"
|
57
|
+
end
|
58
|
+
raise ArgumentError, "You must define :delay in the options for :#{action_name}" if options[:delay].nil?
|
59
|
+
|
60
|
+
drips.register(action_name, options, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
# Handles delivering a `Caffeinate::Mailing` for the `Caffeinate::Dripper`
|
6
|
+
module Perform
|
7
|
+
# :nodoc:
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Delivers the next_caffeinate_mailer for the campaign's subscribers.
|
13
|
+
#
|
14
|
+
# OrderDripper.new.perform!
|
15
|
+
#
|
16
|
+
# @return nil
|
17
|
+
def perform!
|
18
|
+
campaign.caffeinate_campaign_subscriptions.joins(:next_caffeinate_mailing).includes(:next_caffeinate_mailing).each do |subscriber|
|
19
|
+
subscriber.next_caffeinate_mailing.process!
|
20
|
+
end
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# Convenience method for Dripper::Base#perform
|
26
|
+
def perform!
|
27
|
+
new.perform!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
# Handles subscribing records to a campaign.
|
6
|
+
module Subscriber
|
7
|
+
# :nodoc:
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Runs the subscriber_block
|
14
|
+
#
|
15
|
+
# OrderDripper.subscribe!
|
16
|
+
def subscribe!
|
17
|
+
subscribes_block.call
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the campaign's `Caffeinate::CampaignSubscriber`
|
21
|
+
def subscribers
|
22
|
+
caffeinate_campaign.caffeinate_campaign_subscribers
|
23
|
+
end
|
24
|
+
|
25
|
+
# Subscribes to the campaign.
|
26
|
+
#
|
27
|
+
# OrderDripper.subscribe(order, user: order.user)
|
28
|
+
#
|
29
|
+
# @param [ActiveRecord::Base] subscriber The object subscribing
|
30
|
+
# @option [ActiveRecord::Base] :user The associated user (optional)
|
31
|
+
#
|
32
|
+
# @return [Caffeinate::CampaignSubscriber] the created CampaignSubscriber
|
33
|
+
def subscribe(subscriber, user:)
|
34
|
+
caffeinate_campaign.subscribe(subscriber, user: user)
|
35
|
+
end
|
36
|
+
|
37
|
+
# :nodoc:
|
38
|
+
def subscribes_block
|
39
|
+
raise(NotImplementedError, 'Define subscribes') unless @subscribes_block
|
40
|
+
|
41
|
+
@subscribes_block
|
42
|
+
end
|
43
|
+
|
44
|
+
# The subscriber block. Used to create `::Caffeinate::CampaignSubscribers` subscribers.
|
45
|
+
#
|
46
|
+
# subscribes do
|
47
|
+
# Cart.left_joins(:cart_items)
|
48
|
+
# .includes(:user)
|
49
|
+
# .where(completed_at: nil)
|
50
|
+
# .where(updated_at: 1.day.ago..2.days.ago)
|
51
|
+
# .having('count(cart_items.id) > 0').each do |cart|
|
52
|
+
# subscribe(cart, user: cart.user)
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# No need to worry about checking if the given subscriber being already subscribed.
|
57
|
+
# The `subscribe` method does that for you.
|
58
|
+
#
|
59
|
+
# Optionally, can subscribe a user manually via `Caffeinate::Campaign#subscribe`
|
60
|
+
def subscribes(&block)
|
61
|
+
@subscribes_block = block
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'caffeinate/action_mailer'
|
4
|
+
require 'caffeinate/active_record/extension'
|
5
|
+
|
6
|
+
module Caffeinate
|
7
|
+
# :nodoc:
|
8
|
+
class Engine < ::Rails::Engine
|
9
|
+
isolate_namespace Caffeinate
|
10
|
+
|
11
|
+
ActiveSupport.on_load(:action_mailer) do
|
12
|
+
include ::Caffeinate::ActionMailer::Extension
|
13
|
+
::ActionMailer::Base.register_interceptor(::Caffeinate::ActionMailer::Interceptor)
|
14
|
+
::ActionMailer::Base.register_observer(::Caffeinate::ActionMailer::Observer)
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveSupport.on_load(:active_record) do
|
18
|
+
extend ::Caffeinate::ActiveRecord::Extension
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Generators
|
5
|
+
# :nodoc:
|
6
|
+
class InstallGenerator < Rails::Generators::Base
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
include ::Rails::Generators::Migration
|
9
|
+
|
10
|
+
desc 'Creates a Caffeinate initializer and copies migrations to your application.'
|
11
|
+
|
12
|
+
# :nodoc:
|
13
|
+
def copy_initializer
|
14
|
+
template 'caffeinate.rb', 'config/initializers/caffeinate.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
# :nodoc:
|
18
|
+
def copy_application_campaign
|
19
|
+
template 'application_dripper.rb', 'app/drippers/application_dripper.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
# :nodoc:
|
23
|
+
def self.next_migration_number(_path)
|
24
|
+
if @prev_migration_nr
|
25
|
+
@prev_migration_nr += 1
|
26
|
+
else
|
27
|
+
@prev_migration_nr = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
|
28
|
+
end
|
29
|
+
@prev_migration_nr.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
# :nodoc:
|
33
|
+
def copy_migrations
|
34
|
+
require 'rake'
|
35
|
+
Rails.application.load_tasks
|
36
|
+
Rake::Task['railties:install:migrations'].reenable
|
37
|
+
Rake::Task['caffeinate:install:migrations'].invoke
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Caffeinate.setup do |config|
|
4
|
+
# == Current time
|
5
|
+
#
|
6
|
+
# Used for when we set a datetime column to "now" in the database
|
7
|
+
#
|
8
|
+
# Default:
|
9
|
+
# -> { Time.current }
|
10
|
+
#
|
11
|
+
# config.now = -> { DateTime.now }
|
12
|
+
#
|
13
|
+
# == Mailer delivery
|
14
|
+
#
|
15
|
+
# If you want to handle delivery of individual mails in the background. See lib/caffeinate/deliver_async.rb for
|
16
|
+
# implementation details
|
17
|
+
#
|
18
|
+
# Default:
|
19
|
+
# config.async_delivery = false
|
20
|
+
# config.mailing_job = nil
|
21
|
+
#
|
22
|
+
# config.async_delivery = true
|
23
|
+
# config.mailing_job = 'MyCustomCaffeinateJob'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: caffeinate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Brody
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.3
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 6.0.3.4
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 6.0.3
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 6.0.3.4
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: factory_bot_rails
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: pry
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: pry-rails
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec-rails
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: sqlite3
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: timecop
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
description: Opinionated drip campaign framework.
|
118
|
+
email:
|
119
|
+
- josh@josh.mn
|
120
|
+
executables: []
|
121
|
+
extensions: []
|
122
|
+
extra_rdoc_files: []
|
123
|
+
files:
|
124
|
+
- MIT-LICENSE
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- app/controllers/caffeinate/application_controller.rb
|
128
|
+
- app/controllers/caffeinate/campaign_subscriptions_controller.rb
|
129
|
+
- app/models/caffeinate/application_record.rb
|
130
|
+
- app/models/caffeinate/campaign.rb
|
131
|
+
- app/models/caffeinate/campaign_subscription.rb
|
132
|
+
- app/models/caffeinate/mailing.rb
|
133
|
+
- app/views/layouts/caffeinate/application.html.erb
|
134
|
+
- app/views/layouts/caffeinate/campaign_subscriptions/unsubscribe.html.erb
|
135
|
+
- config/routes.rb
|
136
|
+
- db/migrate/20201124183102_create_caffeinate_campaigns.rb
|
137
|
+
- db/migrate/20201124183303_create_caffeinate_campaign_subscriptions.rb
|
138
|
+
- db/migrate/20201124183419_create_caffeinate_mailings.rb
|
139
|
+
- lib/caffeinate.rb
|
140
|
+
- lib/caffeinate/action_mailer.rb
|
141
|
+
- lib/caffeinate/action_mailer/extension.rb
|
142
|
+
- lib/caffeinate/action_mailer/helpers.rb
|
143
|
+
- lib/caffeinate/action_mailer/interceptor.rb
|
144
|
+
- lib/caffeinate/action_mailer/observer.rb
|
145
|
+
- lib/caffeinate/active_record/extension.rb
|
146
|
+
- lib/caffeinate/configuration.rb
|
147
|
+
- lib/caffeinate/deliver_async.rb
|
148
|
+
- lib/caffeinate/drip.rb
|
149
|
+
- lib/caffeinate/dripper/base.rb
|
150
|
+
- lib/caffeinate/dripper/callbacks.rb
|
151
|
+
- lib/caffeinate/dripper/campaign.rb
|
152
|
+
- lib/caffeinate/dripper/defaults.rb
|
153
|
+
- lib/caffeinate/dripper/delivery.rb
|
154
|
+
- lib/caffeinate/dripper/drip.rb
|
155
|
+
- lib/caffeinate/dripper/perform.rb
|
156
|
+
- lib/caffeinate/dripper/subscriber.rb
|
157
|
+
- lib/caffeinate/engine.rb
|
158
|
+
- lib/caffeinate/version.rb
|
159
|
+
- lib/generators/caffeinate/install_generator.rb
|
160
|
+
- lib/generators/caffeinate/templates/application_campaign.rb
|
161
|
+
- lib/generators/caffeinate/templates/caffeinate.rb
|
162
|
+
homepage: https://github.com/joshmn/caffeinate
|
163
|
+
licenses:
|
164
|
+
- MIT
|
165
|
+
metadata: {}
|
166
|
+
post_install_message:
|
167
|
+
rdoc_options: []
|
168
|
+
require_paths:
|
169
|
+
- lib
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
requirements: []
|
181
|
+
rubygems_version: 3.0.3
|
182
|
+
signing_key:
|
183
|
+
specification_version: 4
|
184
|
+
summary: Opinionated drip campaign framework.
|
185
|
+
test_files: []
|