caffeinate 0.8.5 → 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f2ea2b9f5f03fdeb85614ae404df166822797d18861bd1c6ea5c4c867f44618
4
- data.tar.gz: 38b8cd78227cc1eecffe4e09ee01fd8e26a7fc46314d30956ce29230b1209f7f
3
+ metadata.gz: 80e462cd99749a8f1352d481fec74d6a053a1660d200888a93844205eafb984c
4
+ data.tar.gz: 1c709353c202c7a97eb2fd8067f31ee40ee2753d93f8771232494867d47ce9c0
5
5
  SHA512:
6
- metadata.gz: 3fd342f50ad5394c5253d1c7c00efa7ce25e1d1f90571fe16658bed9f0d251893f4953d746daf4db48728541d13fc67ee505529aae18711e491ca0819f66d6a1
7
- data.tar.gz: 8ec91a5b080a1420f090a4c538c1fd1cd05def80c5aa5395ce2a2d985063e4ef7d26e935ec1131496525d44bd745abbac90cbbc749e9ec6b55d1c0f3a99d8e49
6
+ metadata.gz: 9d4261e176299efc975f8d11791c95a58740bd94eaa6ae2e931fb262240f9e24a27cc1c88230965386ff4658e037dcbeb00ebf2100399d077872be0196e3ba8c
7
+ data.tar.gz: 208bc056825501459d0247e4fa91f64b3d9c4408f8219d7f47aa6dd492e9b183e614adeb97b5746f3e9c686d31aa38035667cc003142c93417f1d6cb8935b906
@@ -54,6 +54,14 @@ module Caffeinate
54
54
  #
55
55
  # Just... mintier.
56
56
  def unsubscribe(subscriber, **args)
57
+ reason = args.delete(:reason)
58
+ subscription = subscriber(subscriber, **args)
59
+ return false if subscription.nil?
60
+
61
+ subscription.unsubscribe(reason)
62
+ end
63
+
64
+ def unsubscribe!(subscriber, **args)
57
65
  reason = args.delete(:reason)
58
66
  subscription = subscriber(subscriber, **args)
59
67
  raise ::ActiveRecord::RecordInvalid, subscription if subscription.nil?
@@ -64,12 +72,12 @@ module Caffeinate
64
72
  # Creates a `CampaignSubscription` object for the present Campaign. Allows passing `**args` to
65
73
  # delegate additional arguments to the record. Uses `find_or_create_by`.
66
74
  def subscribe(subscriber, **args)
67
- caffeinate_campaign_subscriptions.find_or_create_by(subscriber: subscriber, **args)
75
+ caffeinate_campaign_subscriptions.create(subscriber: subscriber, **args)
68
76
  end
69
77
 
70
78
  # Subscribes an object to a campaign. Raises `ActiveRecord::RecordInvalid` if the record was invalid.
71
79
  def subscribe!(subscriber, **args)
72
- subscribe(subscriber, **args)
80
+ caffeinate_campaign_subscriptions.create!(subscriber: subscriber, **args)
73
81
  end
74
82
  end
75
83
  end
@@ -28,9 +28,10 @@ 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
34
35
 
35
36
  belongs_to :caffeinate_campaign, class_name: 'Caffeinate::Campaign', foreign_key: :caffeinate_campaign_id
36
37
  alias_attribute :campaign, :caffeinate_campaign
@@ -49,6 +50,8 @@ module Caffeinate
49
50
  before_validation :set_token!, on: [:create]
50
51
  validates :token, uniqueness: true, on: [:create]
51
52
 
53
+ before_validation :call_dripper_before_subscribe_blocks!, on: :create
54
+
52
55
  after_commit :create_mailings!, on: :create
53
56
 
54
57
  after_commit :on_complete, if: :completed?
@@ -83,6 +86,16 @@ module Caffeinate
83
86
  true
84
87
  end
85
88
 
89
+ # Updates `ended_at` and runs `on_complete` callbacks
90
+ def end(reason = nil)
91
+ return false if unsubscribed?
92
+
93
+ result = update(ended_at: ::Caffeinate.config.time_now, ended_reason: reason)
94
+
95
+ caffeinate_campaign.to_dripper.run_callbacks(:on_end, self)
96
+ result
97
+ end
98
+
86
99
  # Updates `unsubscribed_at` and runs `on_subscribe` callbacks
87
100
  def unsubscribe!(reason = nil)
88
101
  raise ::Caffeinate::InvalidState, 'CampaignSubscription is already ended.' if ended?
@@ -93,6 +106,16 @@ module Caffeinate
93
106
  true
94
107
  end
95
108
 
109
+ # Updates `unsubscribed_at` and runs `on_subscribe` callbacks
110
+ def unsubscribe(reason = nil)
111
+ return false if ended?
112
+
113
+ result = update(unsubscribed_at: ::Caffeinate.config.time_now, unsubscribe_reason: reason)
114
+
115
+ caffeinate_campaign.to_dripper.run_callbacks(:on_unsubscribe, self)
116
+ result
117
+ end
118
+
96
119
  # Updates `unsubscribed_at` to nil and runs `on_subscribe` callbacks.
97
120
  # Use `force` to forcefully reset. Does not create the mailings.
98
121
  def resubscribe!(force = false)
@@ -105,12 +128,28 @@ module Caffeinate
105
128
  true
106
129
  end
107
130
 
131
+ # Updates `unsubscribed_at` to nil and runs `on_subscribe` callbacks.
132
+ # Use `force` to forcefully reset. Does not create the mailings.
133
+ def resubscribe!(force = false)
134
+ return false if ended? && !force
135
+ return false if unsubscribed? && !force
136
+
137
+ result = update(unsubscribed_at: nil, resubscribed_at: ::Caffeinate.config.time_now)
138
+
139
+ caffeinate_campaign.to_dripper.run_callbacks(:on_resubscribe, self)
140
+ result
141
+ end
142
+
108
143
  def completed?
109
144
  caffeinate_mailings.unsent.count.zero?
110
145
  end
111
146
 
112
147
  private
113
148
 
149
+ def call_dripper_before_subscribe_blocks!
150
+ caffeinate_campaign.to_dripper.run_callbacks(:before_subscribe, self)
151
+ end
152
+
114
153
  def on_complete
115
154
  caffeinate_campaign.to_dripper.run_callbacks(:on_complete, self)
116
155
  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
@@ -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
 
@@ -11,26 +11,29 @@ module Caffeinate
11
11
 
12
12
  def call(&block)
13
13
  return true unless block
14
-
15
- instance_eval(&block)
14
+ catch(:abort) do
15
+ result = instance_eval(&block)
16
+ return result.nil? || result === true
17
+ end
18
+ false
16
19
  end
17
20
 
18
21
  # Ends the CampaignSubscription
19
- def end!(msg)
20
- mailing.caffeinate_campaign_subscription.end!(msg)
21
- false
22
+ def end!(*args)
23
+ mailing.caffeinate_campaign_subscription.end!(*args)
24
+ throw(:abort)
22
25
  end
23
26
 
24
27
  # Unsubscribes the CampaignSubscription
25
- def unsubscribe!(msg)
26
- mailing.caffeinate_campaign_subscription.unsubscribe!(msg)
27
- false
28
+ def unsubscribe!(*args)
29
+ mailing.caffeinate_campaign_subscription.unsubscribe!(*args)
30
+ throw(:abort)
28
31
  end
29
32
 
30
33
  # Skips the mailing
31
34
  def skip!
32
35
  mailing.skip!
33
- false
36
+ throw(:abort)
34
37
  end
35
38
  end
36
39
  end
@@ -21,6 +21,14 @@ module Caffeinate
21
21
  end
22
22
  end
23
23
 
24
+ def before_subscribe(&block)
25
+ before_subscribe_blocks << block
26
+ end
27
+
28
+ def before_subscribe_blocks
29
+ @before_subscribe_blocks ||= []
30
+ end
31
+
24
32
  # Callback after a Caffeinate::CampaignSubscription is created, and after the Caffeinate::Mailings have
25
33
  # been created.
26
34
  #
@@ -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
@@ -31,10 +31,10 @@ module Caffeinate
31
31
  #
32
32
  # @return [Caffeinate::CampaignSubscriber] the created CampaignSubscriber
33
33
  def subscribe(subscriber, **args)
34
- caffeinate_campaign.subscribe(subscriber, **args)
34
+ caffeinate_campaign.subscribe!(subscriber, **args)
35
35
  end
36
36
 
37
- # Unsubscribes from the campaign.
37
+ # Unsubscribes from the campaign. Returns false if something's wrong.
38
38
  #
39
39
  # OrderDripper.unsubscribe(order, user: order.user)
40
40
  #
@@ -46,6 +46,18 @@ module Caffeinate
46
46
  caffeinate_campaign.unsubscribe(subscriber, **args)
47
47
  end
48
48
 
49
+ # Unsubscribes from the campaign. Raises error if somerthing's wrong.
50
+ #
51
+ # OrderDripper.unsubscribe(order, user: order.user)
52
+ #
53
+ # @param [ActiveRecord::Base] subscriber The object subscribing
54
+ # @option [ActiveRecord::Base] :user The associated user (optional)
55
+ #
56
+ # @return [Caffeinate::CampaignSubscriber] the CampaignSubscriber
57
+ def unsubscribe!(subscriber, **args)
58
+ caffeinate_campaign.unsubscribe!(subscriber, **args)
59
+ end
60
+
49
61
  # :nodoc:
50
62
  def subscribes_block
51
63
  raise(NotImplementedError, 'Define subscribes') unless @subscribes_block
@@ -9,6 +9,13 @@ module Mail
9
9
  class Message
10
10
  attr_accessor :caffeinate_mailing
11
11
 
12
+ def caffeinate_mailing=(mailing)
13
+ @caffeinate_mailing = mailing
14
+ if mailing.is_a?(::Caffeinate::Mailing)
15
+ header['List-Unsubscribe'] = "<#{Caffeinate::UrlHelpers.caffeinate_subscribe_url(mailing.subscription)}>"
16
+ end
17
+ end
18
+
12
19
  def caffeinate?
13
20
  caffeinate_mailing.present?
14
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Caffeinate
4
- VERSION = '0.8.5'
4
+ VERSION = '0.12.0'
5
5
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caffeinate
4
+ module Generators
5
+ # Creates a mailer from a dripper.
6
+ class MailerGenerator < Rails::Generators::Base
7
+ source_root File.expand_path('templates', __dir__)
8
+ include ::Rails::Generators::Migration
9
+ argument :dripper, banner: "dripper"
10
+
11
+ desc 'Creates a Mailer class from a dripper.'
12
+
13
+ def create_mailer
14
+ @dripper_klass = @dripper.safe_constantize
15
+ if @dripper_klass.nil?
16
+ raise ArgumentError, "Unknown dripper #{@dripper}"
17
+ end
18
+ @mailer_class = @dripper_klass.defaults[:mailer_class] || @dripper_klass.defaults[:mailer]
19
+ template 'mailer.rb', "app/mailers/#{@mailer_class.underscore}.rb"
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ class <%= @mailer_class %> < ApplicationMailer
2
+ <% @dripper_klass.drips.each do |action| -%>
3
+ def <%= action.action %><% if action.options[:using].nil? %>(mailing)<% end %>
4
+ <% if action.options[:using].nil? -%>
5
+ @mailing = mailing
6
+ <% end -%>
7
+ end
8
+ <% end -%>
9
+ 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.8.5
4
+ version: 0.12.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-23 00:00:00.000000000 Z
11
+ date: 2021-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -175,8 +175,10 @@ files:
175
175
  - lib/caffeinate/url_helpers.rb
176
176
  - lib/caffeinate/version.rb
177
177
  - lib/generators/caffeinate/install_generator.rb
178
+ - lib/generators/caffeinate/mailer_generator.rb
178
179
  - lib/generators/caffeinate/templates/application_dripper.rb
179
180
  - lib/generators/caffeinate/templates/caffeinate.rb
181
+ - lib/generators/caffeinate/templates/mailer.rb.tt
180
182
  - lib/generators/caffeinate/views_generator.rb
181
183
  homepage: https://github.com/joshmn/caffeinate
182
184
  licenses: