caffeinate 0.1.2 → 0.4.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/README.md +58 -43
- data/app/controllers/caffeinate/campaign_subscriptions_controller.rb +17 -2
- data/app/models/caffeinate/campaign.rb +40 -1
- data/app/models/caffeinate/campaign_subscription.rb +34 -10
- data/app/models/caffeinate/mailing.rb +26 -3
- data/app/views/caffeinate/campaign_subscriptions/subscribe.html.erb +3 -0
- data/app/views/caffeinate/campaign_subscriptions/unsubscribe.html.erb +3 -0
- data/app/views/layouts/_caffeinate.html.erb +11 -0
- data/config/locales/en.yml +6 -0
- data/db/migrate/20201124183102_create_caffeinate_campaigns.rb +1 -0
- data/db/migrate/20201124183303_create_caffeinate_campaign_subscriptions.rb +3 -0
- data/db/migrate/20201124183419_create_caffeinate_mailings.rb +4 -1
- data/lib/caffeinate.rb +2 -0
- data/lib/caffeinate/action_mailer.rb +4 -4
- data/lib/caffeinate/action_mailer/extension.rb +17 -1
- data/lib/caffeinate/action_mailer/interceptor.rb +4 -2
- data/lib/caffeinate/action_mailer/observer.rb +4 -4
- data/lib/caffeinate/active_record/extension.rb +3 -2
- data/lib/caffeinate/configuration.rb +4 -1
- data/lib/caffeinate/drip.rb +22 -35
- data/lib/caffeinate/drip_evaluator.rb +35 -0
- data/lib/caffeinate/dripper/base.rb +13 -14
- data/lib/caffeinate/dripper/batching.rb +22 -0
- data/lib/caffeinate/dripper/callbacks.rb +57 -6
- data/lib/caffeinate/dripper/campaign.rb +8 -9
- data/lib/caffeinate/dripper/defaults.rb +4 -2
- data/lib/caffeinate/dripper/delivery.rb +8 -8
- data/lib/caffeinate/dripper/drip.rb +46 -15
- data/lib/caffeinate/dripper/inferences.rb +29 -0
- data/lib/caffeinate/dripper/perform.rb +24 -5
- data/lib/caffeinate/dripper/periodical.rb +24 -0
- data/lib/caffeinate/dripper/subscriber.rb +2 -2
- data/lib/caffeinate/engine.rb +13 -1
- data/lib/caffeinate/helpers.rb +24 -0
- data/lib/caffeinate/mail_ext.rb +12 -0
- data/lib/caffeinate/url_helpers.rb +10 -0
- data/lib/caffeinate/version.rb +1 -1
- data/lib/generators/caffeinate/install_generator.rb +5 -1
- data/lib/generators/caffeinate/templates/caffeinate.rb +11 -1
- metadata +13 -5
- data/app/views/layouts/caffeinate/application.html.erb +0 -15
- data/app/views/layouts/caffeinate/campaign_subscriptions/unsubscribe.html.erb +0 -1
- data/lib/caffeinate/action_mailer/helpers.rb +0 -12
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Dripper
|
5
|
+
# Handles the default DSL for a `Caffeinate::Dripper`.
|
5
6
|
module Defaults
|
6
7
|
# :nodoc:
|
7
8
|
def self.included(klass)
|
@@ -11,7 +12,7 @@ module Caffeinate
|
|
11
12
|
module ClassMethods
|
12
13
|
# The defaults set in the Campaign
|
13
14
|
def defaults
|
14
|
-
@defaults ||= { mailer_class: inferred_mailer_class }
|
15
|
+
@defaults ||= { mailer_class: inferred_mailer_class, batch_size: ::Caffeinate.config.batch_size }
|
15
16
|
end
|
16
17
|
|
17
18
|
# The default options for the Campaign
|
@@ -23,7 +24,8 @@ module Caffeinate
|
|
23
24
|
# @param [Hash] options The options to set defaults with
|
24
25
|
# @option options [String] :mailer_class The mailer class
|
25
26
|
def default(options = {})
|
26
|
-
options.
|
27
|
+
options.symbolize_keys!
|
28
|
+
options.assert_valid_keys(:mailer_class, :mailer, :using, :batch_size)
|
27
29
|
@defaults = options
|
28
30
|
end
|
29
31
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Dripper
|
5
|
-
# Handles delivery of a Caffeinate::Mailer for a Caffeinate::Dripper
|
5
|
+
# Handles delivery of a `Caffeinate::Mailer` for a `Caffeinate::Dripper`.
|
6
6
|
module Delivery
|
7
7
|
# :nodoc:
|
8
8
|
def self.included(klass)
|
@@ -14,13 +14,13 @@ module Caffeinate
|
|
14
14
|
#
|
15
15
|
# @param [Caffeinate::Mailing] mailing The mailing to deliver
|
16
16
|
def deliver!(mailing)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
message = if mailing.drip.parameterized?
|
18
|
+
mailing.mailer_class.constantize.with(mailing: mailing).send(mailing.mailer_action)
|
19
|
+
else
|
20
|
+
mailing.mailer_class.constantize.send(mailing.mailer_action, mailing)
|
21
|
+
end
|
22
|
+
message.caffeinate_mailing = mailing
|
23
|
+
message.deliver
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Dripper
|
5
|
-
# The Drip DSL for registering a drip
|
5
|
+
# The Drip DSL for registering a drip.
|
6
6
|
module Drip
|
7
7
|
# A collection of Drip objects for a `Caffeinate::Dripper`
|
8
8
|
class DripCollection
|
@@ -10,21 +10,55 @@ module Caffeinate
|
|
10
10
|
|
11
11
|
def initialize(dripper)
|
12
12
|
@dripper = dripper
|
13
|
-
@drips =
|
13
|
+
@drips = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def for(action)
|
17
|
+
@drips[action.to_sym]
|
14
18
|
end
|
15
19
|
|
16
20
|
# Register the drip
|
17
21
|
def register(action, options, &block)
|
18
|
-
|
22
|
+
options = validate_drip_options(action, options)
|
23
|
+
|
24
|
+
@drips[action.to_sym] = ::Caffeinate::Drip.new(@dripper, action, options, &block)
|
19
25
|
end
|
20
26
|
|
21
27
|
def each(&block)
|
22
|
-
@drips.each { |drip| block.call(drip) }
|
28
|
+
@drips.each { |action_name, drip| block.call(action_name, drip) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def values
|
32
|
+
@drips.values
|
23
33
|
end
|
24
34
|
|
25
35
|
def size
|
26
36
|
@drips.size
|
27
37
|
end
|
38
|
+
|
39
|
+
def [](val)
|
40
|
+
@drips[val]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def validate_drip_options(action, options)
|
46
|
+
options.symbolize_keys!
|
47
|
+
options.assert_valid_keys(:mailer_class, :step, :delay, :every, :start, :using, :mailer)
|
48
|
+
options[:mailer_class] ||= options[:mailer] || @dripper.defaults[:mailer_class]
|
49
|
+
options[:using] ||= @dripper.defaults[:using]
|
50
|
+
options[:step] ||= @dripper.drips.size + 1
|
51
|
+
|
52
|
+
if options[:mailer_class].nil?
|
53
|
+
raise ArgumentError, "You must define :mailer_class or :mailer in the options for #{action.inspect} on #{@dripper.inspect}"
|
54
|
+
end
|
55
|
+
|
56
|
+
if options[:every].nil? && options[:delay].nil?
|
57
|
+
raise ArgumentError, "You must define :delay in the options for #{action.inspect} on #{@dripper.inspect}"
|
58
|
+
end
|
59
|
+
|
60
|
+
options
|
61
|
+
end
|
28
62
|
end
|
29
63
|
|
30
64
|
# :nodoc:
|
@@ -33,9 +67,14 @@ module Caffeinate
|
|
33
67
|
end
|
34
68
|
|
35
69
|
module ClassMethods
|
70
|
+
# A collection of Drip objects associated with a given `Caffeinate::Dripper`
|
71
|
+
def drip_collection
|
72
|
+
@drip_collection ||= DripCollection.new(self)
|
73
|
+
end
|
74
|
+
|
36
75
|
# A collection of Drip objects associated with a given `Caffeinate::Dripper`
|
37
76
|
def drips
|
38
|
-
|
77
|
+
drip_collection.values
|
39
78
|
end
|
40
79
|
|
41
80
|
# Register a drip on the Dripper
|
@@ -47,17 +86,9 @@ module Caffeinate
|
|
47
86
|
# @option options [String] :mailer_class The mailer_class
|
48
87
|
# @option options [Integer] :step The order in which the drip is executed
|
49
88
|
# @option options [ActiveSupport::Duration] :delay When the drip should be ran
|
89
|
+
# @option options [Symbol] :using set to :parameters if the mailer action uses ActionMailer::Parameters
|
50
90
|
def drip(action_name, options = {}, &block)
|
51
|
-
|
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)
|
91
|
+
drip_collection.register(action_name, options, &block)
|
61
92
|
end
|
62
93
|
end
|
63
94
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
# Includes the inferred methods based on a Dripper name.
|
6
|
+
module Inferences
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
# The inferred mailer class
|
13
|
+
def inferred_mailer_class
|
14
|
+
klass_name = "#{name.delete_suffix('Dripper')}Mailer"
|
15
|
+
klass = klass_name.safe_constantize
|
16
|
+
return nil unless klass
|
17
|
+
return klass_name if klass < ::ActionMailer::Base
|
18
|
+
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# The inferred mailer class
|
23
|
+
def inferred_campaign_slug
|
24
|
+
name.delete_suffix('Dripper').to_s.underscore
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -2,23 +2,42 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Dripper
|
5
|
-
# Handles delivering a `Caffeinate::Mailing` for the `Caffeinate::Dripper
|
5
|
+
# Handles delivering a `Caffeinate::Mailing` for the `Caffeinate::Dripper`.
|
6
6
|
module Perform
|
7
7
|
# :nodoc:
|
8
8
|
def self.included(klass)
|
9
9
|
klass.extend ClassMethods
|
10
10
|
end
|
11
11
|
|
12
|
-
# Delivers the next_caffeinate_mailer for the campaign's subscribers.
|
12
|
+
# Delivers the next_caffeinate_mailer for the campaign's subscribers. Handles with batches based on `batch_size`.
|
13
13
|
#
|
14
14
|
# OrderDripper.new.perform!
|
15
15
|
#
|
16
16
|
# @return nil
|
17
17
|
def perform!
|
18
|
-
|
19
|
-
|
18
|
+
includes = [:next_caffeinate_mailing]
|
19
|
+
preloads = []
|
20
|
+
if ::Caffeinate.config.async_delivery?
|
21
|
+
# nothing
|
22
|
+
else
|
23
|
+
preloads += %i[subscriber user]
|
20
24
|
end
|
21
|
-
|
25
|
+
|
26
|
+
run_callbacks(:before_process, self)
|
27
|
+
campaign.caffeinate_campaign_subscriptions
|
28
|
+
.active
|
29
|
+
.joins(:next_caffeinate_mailing)
|
30
|
+
.preload(*preloads)
|
31
|
+
.includes(*includes)
|
32
|
+
.in_batches(of: self.class.batch_size)
|
33
|
+
.each do |batch|
|
34
|
+
run_callbacks(:on_process, self, batch)
|
35
|
+
batch.each do |subscriber|
|
36
|
+
subscriber.next_caffeinate_mailing.process!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
run_callbacks(:after_process, self)
|
40
|
+
nil
|
22
41
|
end
|
23
42
|
|
24
43
|
module ClassMethods
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Caffeinate
|
2
|
+
module Dripper
|
3
|
+
module Periodical
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def periodical(action_name, every:, start: -> { ::Caffeinate.config.time_now }, **options, &block)
|
10
|
+
options[:start] = start
|
11
|
+
options[:every] = every
|
12
|
+
drip(action_name, options, &block)
|
13
|
+
after_send do |mailing, _message|
|
14
|
+
if mailing.drip.action == action_name
|
15
|
+
next_mailing = mailing.dup
|
16
|
+
next_mailing.send_at = mailing.drip.send_at(mailing)
|
17
|
+
next_mailing.save!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -30,8 +30,8 @@ module Caffeinate
|
|
30
30
|
# @option [ActiveRecord::Base] :user The associated user (optional)
|
31
31
|
#
|
32
32
|
# @return [Caffeinate::CampaignSubscriber] the created CampaignSubscriber
|
33
|
-
def subscribe(subscriber,
|
34
|
-
caffeinate_campaign.subscribe(subscriber,
|
33
|
+
def subscribe(subscriber, **args)
|
34
|
+
caffeinate_campaign.subscribe(subscriber, **args)
|
35
35
|
end
|
36
36
|
|
37
37
|
# :nodoc:
|
data/lib/caffeinate/engine.rb
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'caffeinate/helpers'
|
3
4
|
require 'caffeinate/action_mailer'
|
4
5
|
require 'caffeinate/active_record/extension'
|
5
6
|
|
6
7
|
module Caffeinate
|
7
|
-
#
|
8
|
+
# Adds Caffeinate to Rails
|
8
9
|
class Engine < ::Rails::Engine
|
9
10
|
isolate_namespace Caffeinate
|
11
|
+
config.eager_load_namespaces << Caffeinate
|
12
|
+
|
13
|
+
config.to_prepare do
|
14
|
+
Dir.glob(Rails.root.join(Caffeinate.config.drippers_path, "**", "*.rb")).each do |dripper|
|
15
|
+
require dripper
|
16
|
+
end
|
17
|
+
end
|
10
18
|
|
11
19
|
ActiveSupport.on_load(:action_mailer) do
|
12
20
|
include ::Caffeinate::ActionMailer::Extension
|
@@ -17,5 +25,9 @@ module Caffeinate
|
|
17
25
|
ActiveSupport.on_load(:active_record) do
|
18
26
|
extend ::Caffeinate::ActiveRecord::Extension
|
19
27
|
end
|
28
|
+
|
29
|
+
ActiveSupport.on_load(:action_view) do
|
30
|
+
ActionView::Base.include ::Caffeinate::Helpers
|
31
|
+
end
|
20
32
|
end
|
21
33
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
# URL helpers for accessing the mounted Caffeinate instance.
|
5
|
+
module Helpers
|
6
|
+
def caffeinate_unsubscribe_url(subscription, **options)
|
7
|
+
opts = (::ActionMailer::Base.default_url_options || {}).merge(options)
|
8
|
+
Caffeinate::Engine.routes.url_helpers.unsubscribe_campaign_subscription_url(token: subscription.token, **opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def caffeinate_subscribe_url(subscription, **options)
|
12
|
+
opts = (::ActionMailer::Base.default_url_options || {}).merge(options)
|
13
|
+
Caffeinate::Engine.routes.url_helpers.subscribe_campaign_subscription_url(token: subscription.token, **opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
def caffeinate_unsubscribe_path(subscription, **options)
|
17
|
+
Caffeinate::Engine.routes.url_helpers.unsubscribe_campaign_subscription_path(token: subscription.token, **options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def caffeinate_subscribe_path(subscription, **options)
|
21
|
+
Caffeinate::Engine.routes.url_helpers.subscribe_campaign_subscription_path(token: subscription.token, **options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/caffeinate/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Generators
|
5
|
-
#
|
5
|
+
# Installs Caffeinate
|
6
6
|
class InstallGenerator < Rails::Generators::Base
|
7
7
|
source_root File.expand_path('templates', __dir__)
|
8
8
|
include ::Rails::Generators::Migration
|
@@ -19,6 +19,10 @@ module Caffeinate
|
|
19
19
|
template 'application_dripper.rb', 'app/drippers/application_dripper.rb'
|
20
20
|
end
|
21
21
|
|
22
|
+
def install_routes
|
23
|
+
inject_into_file 'config/routes.rb', "\n mount ::Caffeinate::Engine => '/caffeinate'", after: /Rails.application.routes.draw do/
|
24
|
+
end
|
25
|
+
|
22
26
|
# :nodoc:
|
23
27
|
def self.next_migration_number(_path)
|
24
28
|
if @prev_migration_nr
|
@@ -6,7 +6,7 @@ Caffeinate.setup do |config|
|
|
6
6
|
# Used for when we set a datetime column to "now" in the database
|
7
7
|
#
|
8
8
|
# Default:
|
9
|
-
# -> { Time.current }
|
9
|
+
# config.now = -> { Time.current }
|
10
10
|
#
|
11
11
|
# config.now = -> { DateTime.now }
|
12
12
|
#
|
@@ -21,4 +21,14 @@ Caffeinate.setup do |config|
|
|
21
21
|
#
|
22
22
|
# config.async_delivery = true
|
23
23
|
# config.mailing_job = 'MyCustomCaffeinateJob'
|
24
|
+
#
|
25
|
+
# == Batching
|
26
|
+
#
|
27
|
+
# When a Dripper is performed and sends the mails, we use `find_in_batches`. Use `batch_size` to set the batch size.
|
28
|
+
# You can set this on a dripper as well for more granular control.
|
29
|
+
#
|
30
|
+
# Default:
|
31
|
+
# config.batch_size = 1_000
|
32
|
+
#
|
33
|
+
# config.batch_size = 100
|
24
34
|
end
|
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: 0.
|
4
|
+
version: 0.4.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: 2020-
|
11
|
+
date: 2020-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -124,8 +124,10 @@ files:
|
|
124
124
|
- app/models/caffeinate/campaign.rb
|
125
125
|
- app/models/caffeinate/campaign_subscription.rb
|
126
126
|
- app/models/caffeinate/mailing.rb
|
127
|
-
- app/views/
|
128
|
-
- app/views/
|
127
|
+
- app/views/caffeinate/campaign_subscriptions/subscribe.html.erb
|
128
|
+
- app/views/caffeinate/campaign_subscriptions/unsubscribe.html.erb
|
129
|
+
- app/views/layouts/_caffeinate.html.erb
|
130
|
+
- config/locales/en.yml
|
129
131
|
- config/routes.rb
|
130
132
|
- db/migrate/20201124183102_create_caffeinate_campaigns.rb
|
131
133
|
- db/migrate/20201124183303_create_caffeinate_campaign_subscriptions.rb
|
@@ -133,22 +135,28 @@ files:
|
|
133
135
|
- lib/caffeinate.rb
|
134
136
|
- lib/caffeinate/action_mailer.rb
|
135
137
|
- lib/caffeinate/action_mailer/extension.rb
|
136
|
-
- lib/caffeinate/action_mailer/helpers.rb
|
137
138
|
- lib/caffeinate/action_mailer/interceptor.rb
|
138
139
|
- lib/caffeinate/action_mailer/observer.rb
|
139
140
|
- lib/caffeinate/active_record/extension.rb
|
140
141
|
- lib/caffeinate/configuration.rb
|
141
142
|
- lib/caffeinate/deliver_async.rb
|
142
143
|
- lib/caffeinate/drip.rb
|
144
|
+
- lib/caffeinate/drip_evaluator.rb
|
143
145
|
- lib/caffeinate/dripper/base.rb
|
146
|
+
- lib/caffeinate/dripper/batching.rb
|
144
147
|
- lib/caffeinate/dripper/callbacks.rb
|
145
148
|
- lib/caffeinate/dripper/campaign.rb
|
146
149
|
- lib/caffeinate/dripper/defaults.rb
|
147
150
|
- lib/caffeinate/dripper/delivery.rb
|
148
151
|
- lib/caffeinate/dripper/drip.rb
|
152
|
+
- lib/caffeinate/dripper/inferences.rb
|
149
153
|
- lib/caffeinate/dripper/perform.rb
|
154
|
+
- lib/caffeinate/dripper/periodical.rb
|
150
155
|
- lib/caffeinate/dripper/subscriber.rb
|
151
156
|
- lib/caffeinate/engine.rb
|
157
|
+
- lib/caffeinate/helpers.rb
|
158
|
+
- lib/caffeinate/mail_ext.rb
|
159
|
+
- lib/caffeinate/url_helpers.rb
|
152
160
|
- lib/caffeinate/version.rb
|
153
161
|
- lib/generators/caffeinate/install_generator.rb
|
154
162
|
- lib/generators/caffeinate/templates/application_dripper.rb
|