caffeinate 0.9 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa3c41f5c5af2ed5a8733c8c5f8614a99c2df6cfa6549bce48b6c521aedfc647
4
- data.tar.gz: b8585a93022bbf70657f8d81485d9691b6a0c90192fb9b632da2a11fde45579b
3
+ metadata.gz: a8ae5ac5375188ce5a7bc8960ac7d7b392ccc7dc7de71070e88a2285fa76e7af
4
+ data.tar.gz: dd06d837c2c1477bf8e48484e028016f00203066f0b2383c6f0a7a3cb5b5af1f
5
5
  SHA512:
6
- metadata.gz: 1cd7ab9dced54ac5cdc6be24498253bf95230d78301d1fecad21c9e74e325ad4bb05eea4cf22e50214448c304a25068f879f12ea2e9af5a6ce59b41e28aab654
7
- data.tar.gz: d066b26a02543079a9a9571d71042216e3ccb69d8440b6efccb93fb47bf78e3367b58d681c7312c0c806b96d829edca1114f67951eb2c97f0c7aec9c818a38c3
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 = nil)
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 = nil)
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 = nil)
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 = nil)
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
@@ -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 = mailing.instance_exec(&options[:on])
49
+ date = OptionEvaluator.new(options[:on], self, mailing).call
30
50
  else
31
- date = options[:delay].from_now
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 = Time.parse(options[:at])
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
- dripper.run_callbacks(:before_drip, self, mailing)
49
-
50
- DripEvaluator.new(mailing).call(&@block)
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
@@ -11,9 +11,9 @@ module Caffeinate
11
11
 
12
12
  def call(&block)
13
13
  return true unless block
14
-
15
14
  catch(:abort) do
16
- return instance_eval(&block)
15
+ result = instance_eval(&block)
16
+ return result.nil? || result === true
17
17
  end
18
18
  false
19
19
  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
- send("#{name}_blocks").each do |callback|
20
- callback.call(*args)
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 |campaign_subscription, mailing, drip|
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
- message.deliver
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
- Caffeinate::Mailing
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Caffeinate
4
- VERSION = '0.9'
4
+ VERSION = '0.14.0'
5
5
  end
@@ -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
- require 'rake'
39
- Rails.application.load_tasks
40
- Rake::Task['railties:install:migrations'].reenable
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[6.0]
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[6.0]
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[6.0]
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: '0.9'
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: 2020-12-24 00:00:00.000000000 Z
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: