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
data/lib/caffeinate.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rails/all'
|
4
|
+
require 'caffeinate/mail_ext'
|
4
5
|
require 'caffeinate/engine'
|
5
6
|
require 'caffeinate/drip'
|
7
|
+
require 'caffeinate/url_helpers'
|
6
8
|
require 'caffeinate/configuration'
|
7
9
|
require 'caffeinate/dripper/base'
|
8
10
|
require 'caffeinate/deliver_async'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
require
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
# Includes all files in `caffeinate/action_mailer`
|
6
|
+
Dir["#{__dir__}/action_mailer/*"].each { |path| require "caffeinate/action_mailer/#{File.basename(path)}" }
|
@@ -2,11 +2,27 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module ActionMailer
|
5
|
+
# Convenience for setting `@mailing`, and convenience methods for inferred `caffeinate_unsubscribe_url` and
|
6
|
+
# `caffeinate_subscribe_url`.
|
5
7
|
module Extension
|
6
8
|
def self.included(klass)
|
7
9
|
klass.before_action do
|
8
|
-
@mailing =
|
10
|
+
@mailing = params[:mailing] if params
|
9
11
|
end
|
12
|
+
|
13
|
+
klass.helper_method :caffeinate_unsubscribe_url, :caffeinate_subscribe_url
|
14
|
+
end
|
15
|
+
|
16
|
+
# Assumes `@mailing` is set
|
17
|
+
def caffeinate_unsubscribe_url(mailing: nil, **options)
|
18
|
+
mailing ||= @mailing
|
19
|
+
Caffeinate::UrlHelpers.caffeinate_unsubscribe_url(mailing.caffeinate_campaign_subscription, **options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Assumes `@mailing` is set
|
23
|
+
def caffeinate_subscribe_url(mailing: nil, **options)
|
24
|
+
mailing ||= @mailing
|
25
|
+
Caffeinate::UrlHelpers.caffeinate_subscribe_url(mailing.caffeinate_campaign_subscription, **options)
|
10
26
|
end
|
11
27
|
end
|
12
28
|
end
|
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module ActionMailer
|
5
|
+
# Handles the evaluation of a drip against a mailing to determine if it ultimately gets delivered.
|
6
|
+
# Also invokes the `before_send` callbacks.
|
5
7
|
class Interceptor
|
6
8
|
# Handles `before_send` callbacks for a `Caffeinate::Dripper`
|
7
9
|
def self.delivering_email(message)
|
8
|
-
mailing =
|
10
|
+
mailing = message.caffeinate_mailing
|
9
11
|
return unless mailing
|
10
12
|
|
11
|
-
mailing.caffeinate_campaign.to_dripper.run_callbacks(:before_send, mailing
|
13
|
+
mailing.caffeinate_campaign.to_dripper.run_callbacks(:before_send, mailing, message)
|
12
14
|
drip = mailing.drip
|
13
15
|
message.perform_deliveries = drip.enabled?(mailing)
|
14
16
|
end
|
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module ActionMailer
|
5
|
-
# Handles updating the Caffeinate::Message if it's available in
|
5
|
+
# Handles updating the Caffeinate::Message if it's available in Mail::Message.caffeinate_mailing
|
6
6
|
# and runs any associated callbacks
|
7
7
|
class Observer
|
8
8
|
def self.delivered_email(message)
|
9
|
-
mailing =
|
9
|
+
mailing = message.caffeinate_mailing
|
10
10
|
return unless mailing
|
11
11
|
|
12
|
-
mailing.update!(sent_at: Caffeinate.config.time_now) if message.perform_deliveries
|
13
|
-
mailing.caffeinate_campaign.to_dripper.run_callbacks(:after_send, mailing
|
12
|
+
mailing.update!(sent_at: Caffeinate.config.time_now, skipped_at: nil) if message.perform_deliveries
|
13
|
+
mailing.caffeinate_campaign.to_dripper.run_callbacks(:after_send, mailing, message)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -2,14 +2,15 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module ActiveRecord
|
5
|
+
# Includes the ActiveRecord association and relevant scopes for an ActiveRecord-backed model
|
5
6
|
module Extension
|
6
7
|
# Adds the associations for a subscriber
|
7
8
|
def caffeinate_subscriber
|
8
|
-
has_many :caffeinate_campaign_subscriptions, as: :subscriber, class_name: '::Caffeinate::CampaignSubscription'
|
9
|
+
has_many :caffeinate_campaign_subscriptions, as: :subscriber, class_name: '::Caffeinate::CampaignSubscription', dependent: :destroy
|
9
10
|
has_many :caffeinate_campaigns, through: :caffeinate_campaign_subscriptions, class_name: '::Caffeinate::Campaign'
|
10
11
|
has_many :caffeinate_mailings, through: :caffeinate_campaign_subscriptions, class_name: '::Caffeinate::Mailing'
|
11
12
|
|
12
|
-
scope :
|
13
|
+
scope :not_subscribed_to_campaign, lambda { |list|
|
13
14
|
subscribed = ::Caffeinate::CampaignSubscription.select(:subscriber_id).joins(:caffeinate_campaign).where(caffeinate_campaigns: { slug: list }, subscriber_type: name)
|
14
15
|
where.not(id: subscribed)
|
15
16
|
}
|
@@ -1,13 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Caffeinate
|
4
|
+
# Global configuration
|
4
5
|
class Configuration
|
5
|
-
attr_accessor :now, :async_delivery, :mailing_job
|
6
|
+
attr_accessor :now, :async_delivery, :mailing_job, :batch_size, :drippers_path
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
@now = -> { Time.current }
|
9
10
|
@async_delivery = false
|
10
11
|
@mailing_job = nil
|
12
|
+
@batch_size = 1_000
|
13
|
+
@drippers_path = "app/drippers"
|
11
14
|
end
|
12
15
|
|
13
16
|
def now=(val)
|
data/lib/caffeinate/drip.rb
CHANGED
@@ -1,40 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'caffeinate/drip_evaluator'
|
3
4
|
module Caffeinate
|
4
5
|
# A Drip object
|
6
|
+
#
|
7
|
+
# Handles the block and provides convenience methods for the drip
|
5
8
|
class Drip
|
6
|
-
# Handles the block and provides convenience methods for the drip
|
7
|
-
class Evaluator
|
8
|
-
attr_reader :mailing
|
9
|
-
def initialize(mailing)
|
10
|
-
@mailing = mailing
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(&block)
|
14
|
-
return true unless block
|
15
|
-
|
16
|
-
instance_eval(&block)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Ends the CampaignSubscription
|
20
|
-
def end!
|
21
|
-
mailing.caffeinate_campaign_subscription.end!
|
22
|
-
false
|
23
|
-
end
|
24
|
-
|
25
|
-
# Unsubscribes the CampaignSubscription
|
26
|
-
def unsubscribe!
|
27
|
-
mailing.caffeinate_campaign_subscription.unsubscribe!
|
28
|
-
false
|
29
|
-
end
|
30
|
-
|
31
|
-
# Skips the mailing
|
32
|
-
def skip!
|
33
|
-
mailing.skip!
|
34
|
-
false
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
9
|
attr_reader :dripper, :action, :options, :block
|
39
10
|
def initialize(dripper, action, options, &block)
|
40
11
|
@dripper = dripper
|
@@ -43,14 +14,30 @@ module Caffeinate
|
|
43
14
|
@block = block
|
44
15
|
end
|
45
16
|
|
46
|
-
# If the associated ActionMailer uses `ActionMailer::Parameterized` initialization
|
17
|
+
# If the associated ActionMailer uses `ActionMailer::Parameterized` initialization instead of argument-based initialization
|
47
18
|
def parameterized?
|
48
19
|
options[:using] == :parameterized
|
49
20
|
end
|
50
21
|
|
51
|
-
|
22
|
+
def send_at(mailing = nil)
|
23
|
+
if periodical?
|
24
|
+
start = mailing.instance_exec(&options[:start])
|
25
|
+
if mailing.caffeinate_campaign_subscription.caffeinate_mailings.count > 0
|
26
|
+
start += options[:every]
|
27
|
+
end
|
28
|
+
start.from_now
|
29
|
+
else
|
30
|
+
options[:delay].from_now
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def periodical?
|
35
|
+
options[:every].present?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Checks if the drip is enabled
|
52
39
|
def enabled?(mailing)
|
53
|
-
|
40
|
+
DripEvaluator.new(mailing).call(&@block)
|
54
41
|
end
|
55
42
|
end
|
56
43
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
# Handles evaluating the `drip` block and provides convenience methods for handling the mailing or its campaign.
|
5
|
+
class DripEvaluator
|
6
|
+
attr_reader :mailing
|
7
|
+
def initialize(mailing)
|
8
|
+
@mailing = mailing
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(&block)
|
12
|
+
return true unless block
|
13
|
+
|
14
|
+
instance_eval(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Ends the CampaignSubscription
|
18
|
+
def end!
|
19
|
+
mailing.caffeinate_campaign_subscription.end!
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Unsubscribes the CampaignSubscription
|
24
|
+
def unsubscribe!
|
25
|
+
mailing.caffeinate_campaign_subscription.unsubscribe!
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
# Skips the mailing
|
30
|
+
def skip!
|
31
|
+
mailing.skip!
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,33 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'caffeinate/dripper/
|
3
|
+
require 'caffeinate/dripper/batching'
|
4
4
|
require 'caffeinate/dripper/callbacks'
|
5
|
-
require 'caffeinate/dripper/defaults'
|
6
|
-
require 'caffeinate/dripper/subscriber'
|
7
5
|
require 'caffeinate/dripper/campaign'
|
8
|
-
require 'caffeinate/dripper/
|
6
|
+
require 'caffeinate/dripper/defaults'
|
9
7
|
require 'caffeinate/dripper/delivery'
|
8
|
+
require 'caffeinate/dripper/drip'
|
9
|
+
require 'caffeinate/dripper/inferences'
|
10
|
+
require 'caffeinate/dripper/perform'
|
11
|
+
require 'caffeinate/dripper/periodical'
|
12
|
+
require 'caffeinate/dripper/subscriber'
|
10
13
|
|
11
14
|
module Caffeinate
|
12
15
|
module Dripper
|
16
|
+
# Base class
|
13
17
|
class Base
|
18
|
+
include Batching
|
14
19
|
include Callbacks
|
15
20
|
include Campaign
|
16
21
|
include Defaults
|
17
22
|
include Delivery
|
18
23
|
include Drip
|
24
|
+
include Inferences
|
19
25
|
include Perform
|
26
|
+
include Periodical
|
20
27
|
include Subscriber
|
21
|
-
|
22
|
-
# The inferred mailer class
|
23
|
-
def self.inferred_mailer_class
|
24
|
-
klass_name = "#{name.delete_suffix('Dripper')}Mailer"
|
25
|
-
klass = klass_name.safe_constantize
|
26
|
-
return nil unless klass
|
27
|
-
return klass_name if klass < ::ActionMailer::Base
|
28
|
-
|
29
|
-
nil
|
30
|
-
end
|
31
28
|
end
|
32
29
|
end
|
33
30
|
end
|
31
|
+
|
32
|
+
ActiveSupport.run_load_hooks :caffeinate, Caffeinate::Dripper::Base
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Caffeinate
|
4
|
+
module Dripper
|
5
|
+
# Includes batch support for setting the batch size for Perform
|
6
|
+
module Batching
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def batch_size=(num)
|
13
|
+
@batch_size = num
|
14
|
+
end
|
15
|
+
|
16
|
+
def batch_size
|
17
|
+
@batch_size || ::Caffeinate.config.batch_size
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,12 +2,17 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Dripper
|
5
|
+
# Callbacks for a Dripper.
|
5
6
|
module Callbacks
|
6
7
|
# :nodoc:
|
7
8
|
def self.included(klass)
|
8
9
|
klass.extend ClassMethods
|
9
10
|
end
|
10
11
|
|
12
|
+
def run_callbacks(name, *args)
|
13
|
+
self.class.run_callbacks(name, *args)
|
14
|
+
end
|
15
|
+
|
11
16
|
module ClassMethods
|
12
17
|
# :nodoc:
|
13
18
|
def run_callbacks(name, *args)
|
@@ -33,15 +38,64 @@ module Caffeinate
|
|
33
38
|
@on_subscribe_blocks ||= []
|
34
39
|
end
|
35
40
|
|
41
|
+
# Callback before the mailings get processed.
|
42
|
+
#
|
43
|
+
# before_process do |dripper|
|
44
|
+
# Slack.notify(:caffeinate, "Dripper is getting ready for mailing! #{dripper.caffeinate_campaign.name}!")
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# @yield Caffeinate::Dripper
|
48
|
+
def before_process(&block)
|
49
|
+
before_process_blocks << block
|
50
|
+
end
|
51
|
+
|
52
|
+
# :nodoc:
|
53
|
+
def before_process_blocks
|
54
|
+
@before_process_blocks ||= []
|
55
|
+
end
|
56
|
+
|
57
|
+
# Callback before the mailings get processed in a batch.
|
58
|
+
#
|
59
|
+
# after_process do |dripper, mailings|
|
60
|
+
# Slack.notify(:caffeinate, "Dripper #{dripper.name} sent #{mailings.size} mailings! Whoa!")
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# @yield Caffeinate::Dripper
|
64
|
+
# @yield Caffeinate::Mailing [Array]
|
65
|
+
def on_process(&block)
|
66
|
+
on_process_blocks << block
|
67
|
+
end
|
68
|
+
|
69
|
+
# :nodoc:
|
70
|
+
def on_process_blocks
|
71
|
+
@on_process_blocks ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
# Callback after the all the mailings have been sent.
|
75
|
+
#
|
76
|
+
# after_process do |dripper|
|
77
|
+
# Slack.notify(:caffeinate, "Dripper #{dripper.name} sent #{mailings.size} mailings! Whoa!")
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# @yield Caffeinate::Dripper
|
81
|
+
# @yield Caffeinate::Mailing [Array]
|
82
|
+
def after_process(&block)
|
83
|
+
after_process_blocks << block
|
84
|
+
end
|
85
|
+
|
86
|
+
# :nodoc:
|
87
|
+
def after_process_blocks
|
88
|
+
@after_process_blocks ||= []
|
89
|
+
end
|
90
|
+
|
36
91
|
# Callback before a Drip has called the mailer.
|
37
92
|
#
|
38
93
|
# before_drip do |campaign_subscription, mailing, drip|
|
39
94
|
# Slack.notify(:caffeinate, "#{drip.action_name} is starting")
|
40
95
|
# end
|
41
96
|
#
|
42
|
-
# @yield Caffeinate::CampaignSubscription
|
43
|
-
# @yield Caffeinate::Mailing
|
44
97
|
# @yield Caffeinate::Drip current drip
|
98
|
+
# @yield Caffeinate::Mailing
|
45
99
|
def before_drip(&block)
|
46
100
|
before_drip_blocks << block
|
47
101
|
end
|
@@ -57,7 +111,6 @@ module Caffeinate
|
|
57
111
|
# Slack.notify(:caffeinate, "A new subscriber to #{campaign_subscription.campaign.name}!")
|
58
112
|
# end
|
59
113
|
#
|
60
|
-
# @yield Caffeinate::CampaignSubscription
|
61
114
|
# @yield Caffeinate::Mailing
|
62
115
|
# @yield Mail::Message
|
63
116
|
def before_send(&block)
|
@@ -75,7 +128,6 @@ module Caffeinate
|
|
75
128
|
# Slack.notify(:caffeinate, "A new subscriber to #{campaign_subscription.campaign.name}!")
|
76
129
|
# end
|
77
130
|
#
|
78
|
-
# @yield Caffeinate::CampaignSubscription
|
79
131
|
# @yield Caffeinate::Mailing
|
80
132
|
# @yield Mail::Message
|
81
133
|
def after_send(&block)
|
@@ -125,8 +177,7 @@ module Caffeinate
|
|
125
177
|
# Slack.notify(:caffeinate, "#{campaign_sub.id} has unsubscribed... sad day.")
|
126
178
|
# end
|
127
179
|
#
|
128
|
-
# @yield Caffeinate::
|
129
|
-
# @yield Caffeinate::Mailing
|
180
|
+
# @yield `Caffeinate::Mailing`
|
130
181
|
def on_skip(&block)
|
131
182
|
on_skip_blocks << block
|
132
183
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Caffeinate
|
4
4
|
module Dripper
|
5
|
+
# Campaign methods for `Caffeinate::Dripper`.
|
5
6
|
module Campaign
|
6
7
|
# :nodoc:
|
7
8
|
def self.included(klass)
|
@@ -10,7 +11,7 @@ module Caffeinate
|
|
10
11
|
|
11
12
|
# The campaign for this Dripper
|
12
13
|
#
|
13
|
-
# @return Caffeinate::Campaign
|
14
|
+
# @return `Caffeinate::Campaign`
|
14
15
|
def campaign
|
15
16
|
self.class.caffeinate_campaign
|
16
17
|
end
|
@@ -18,7 +19,7 @@ module Caffeinate
|
|
18
19
|
module ClassMethods
|
19
20
|
# Sets the campaign on the Dripper and resets any existing `@caffeinate_campaign`
|
20
21
|
#
|
21
|
-
# class OrdersDripper
|
22
|
+
# class OrdersDripper < ApplicationDripper
|
22
23
|
# campaign :order_drip
|
23
24
|
# end
|
24
25
|
#
|
@@ -27,25 +28,23 @@ module Caffeinate
|
|
27
28
|
# self.name.delete_suffix("Campaign").underscore
|
28
29
|
#
|
29
30
|
# @param [Symbol] slug The slug of a persisted `Caffeinate::Campaign`.
|
30
|
-
def campaign(slug)
|
31
|
+
def campaign=(slug)
|
31
32
|
@caffeinate_campaign = nil
|
32
33
|
@_campaign_slug = slug.to_sym
|
33
34
|
Caffeinate.register_dripper(@_campaign_slug, name)
|
34
35
|
end
|
35
36
|
|
36
|
-
# Returns the `Caffeinate::Campaign` object for the
|
37
|
+
# Returns the `Caffeinate::Campaign` object for the Dripper
|
37
38
|
def caffeinate_campaign
|
38
39
|
return @caffeinate_campaign if @caffeinate_campaign.present?
|
39
40
|
|
40
|
-
@caffeinate_campaign = ::Caffeinate::Campaign
|
41
|
-
return @caffeinate_campaign if @caffeinate_campaign
|
42
|
-
|
43
|
-
raise(::ActiveRecord::RecordNotFound, "Unable to find ::Caffeinate::Campaign with slug #{campaign_slug}.")
|
41
|
+
@caffeinate_campaign = ::Caffeinate::Campaign[campaign_slug]
|
44
42
|
end
|
43
|
+
alias campaign caffeinate_campaign
|
45
44
|
|
46
45
|
# The defined slug or the inferred slug
|
47
46
|
def campaign_slug
|
48
|
-
@_campaign_slug ||
|
47
|
+
@_campaign_slug || inferred_campaign_slug
|
49
48
|
end
|
50
49
|
end
|
51
50
|
end
|