maily_herald 0.8.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +5 -13
- data/README.md +186 -69
- data/app/mailers/maily_herald/mailer.rb +44 -26
- data/app/models/maily_herald/ad_hoc_mailing.rb +109 -0
- data/app/models/maily_herald/dispatch.rb +97 -4
- data/app/models/maily_herald/list.rb +36 -7
- data/app/models/maily_herald/log.rb +79 -0
- data/app/models/maily_herald/mailing.rb +149 -24
- data/app/models/maily_herald/one_time_mailing.rb +114 -9
- data/app/models/maily_herald/periodical_mailing.rb +76 -47
- data/app/models/maily_herald/sequence.rb +57 -18
- data/app/models/maily_herald/sequence_mailing.rb +29 -20
- data/app/models/maily_herald/subscription.rb +23 -2
- data/config/routes.rb +2 -2
- data/lib/maily_herald.rb +57 -18
- data/lib/maily_herald/autonaming.rb +8 -0
- data/lib/maily_herald/context.rb +6 -2
- data/lib/maily_herald/logging.rb +2 -0
- data/lib/maily_herald/manager.rb +15 -31
- data/lib/maily_herald/utils.rb +65 -24
- data/lib/maily_herald/version.rb +1 -1
- data/maily_herald.gemspec +1 -1
- data/spec/controllers/maily_herald/tokens_controller_spec.rb +13 -13
- data/spec/dummy/app/mailers/ad_hoc_mailer.rb +11 -0
- data/spec/dummy/app/mailers/custom_one_time_mailer.rb +11 -0
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/config/environments/test.rb +1 -1
- data/spec/dummy/config/initializers/maily_herald.rb +3 -69
- data/spec/dummy/config/maily_herald.yml +3 -0
- data/spec/dummy/db/seeds.rb +73 -0
- data/spec/lib/context_spec.rb +7 -7
- data/spec/lib/maily_herald_spec.rb +7 -8
- data/spec/lib/utils_spec.rb +65 -25
- data/spec/mailers/maily_herald/mailer_spec.rb +20 -13
- data/spec/models/maily_herald/ad_hoc_mailing_spec.rb +169 -0
- data/spec/models/maily_herald/list_spec.rb +2 -1
- data/spec/models/maily_herald/log_spec.rb +10 -10
- data/spec/models/maily_herald/mailing_spec.rb +9 -8
- data/spec/models/maily_herald/one_time_mailing_spec.rb +212 -39
- data/spec/models/maily_herald/periodical_mailing_spec.rb +158 -92
- data/spec/models/maily_herald/sequence_mailing_spec.rb +2 -2
- data/spec/models/maily_herald/sequence_spec.rb +152 -139
- data/spec/models/maily_herald/subscription_spec.rb +21 -4
- metadata +17 -8
- data/lib/maily_herald/condition_evaluator.rb +0 -82
- data/lib/maily_herald/config.rb +0 -5
- data/spec/dummy/app/mailers/test_mailer.rb +0 -11
@@ -1,4 +1,21 @@
|
|
1
1
|
module MailyHerald
|
2
|
+
# Stores information about email delivery to entity.
|
3
|
+
#
|
4
|
+
# It is associated with entity object and {Dispatch}.
|
5
|
+
# Log can have following statuses:
|
6
|
+
# - +scheduled+ - email hasn't been processed yet,
|
7
|
+
# - +delivered+ - email was sent to entity,
|
8
|
+
# - +skipped+ - email deliver was skipped (i.e. due to conditions not met),
|
9
|
+
# - +error+ - there was an error during email delivery.
|
10
|
+
#
|
11
|
+
# @attr [Fixnum] entity_id Entity association id.
|
12
|
+
# @attr [String] entity_type Entity association type.
|
13
|
+
# @attr [String] entity_email Delivery email. Stored in case associated entity gets deleted.
|
14
|
+
# @attr [Fixnum] mailing_id {Dispatch} association id.
|
15
|
+
# @attr [Sumbol] status
|
16
|
+
# @attr [Hash] data Custom log data.
|
17
|
+
# @attr [DateTime] processing_at Timestamp of {Dispatch} processing.
|
18
|
+
# Can be either future (when in +scheduled+ state) or past.
|
2
19
|
class Log < ActiveRecord::Base
|
3
20
|
AVAILABLE_STATUSES = [:scheduled, :delivered, :skipped, :error]
|
4
21
|
|
@@ -20,13 +37,29 @@ module MailyHerald
|
|
20
37
|
scope :error, lambda { where(status: :error) }
|
21
38
|
scope :scheduled, lambda { where(status: :scheduled) }
|
22
39
|
scope :processed, lambda { where(status: [:delivered, :skipped, :error]) }
|
40
|
+
scope :not_skipped, lambda { where("status != 'skipped'") }
|
41
|
+
scope :like_email, lambda {|query| where("maily_herald_logs.entity_email LIKE (?)", "%#{query}%") }
|
23
42
|
|
24
43
|
serialize :data, Hash
|
25
44
|
|
45
|
+
# Contains `Mail::Message` object that was delivered.
|
46
|
+
#
|
47
|
+
# Present only in logs of state `delivered` and obtained via
|
48
|
+
# `Mailing.run` method.
|
49
|
+
attr_accessor :mail
|
50
|
+
|
26
51
|
if Rails::VERSION::MAJOR == 3
|
27
52
|
attr_accessible :status, :data
|
28
53
|
end
|
29
54
|
|
55
|
+
# Creates Log object for given {Dispatch} and entity.
|
56
|
+
#
|
57
|
+
# @param mailing [Dispatch]
|
58
|
+
# @param entity [ActiveRecord::Base]
|
59
|
+
# @param attributes [Hash] log attributes
|
60
|
+
# @option attributes [Time] :processing_at (DateTime.now)
|
61
|
+
# @option attributes [Symbol] :status
|
62
|
+
# @option attributes [Hash] :data
|
30
63
|
def self.create_for mailing, entity, attributes = {}
|
31
64
|
log = Log.new
|
32
65
|
log.set_attributes_for mailing, entity, attributes
|
@@ -34,6 +67,14 @@ module MailyHerald
|
|
34
67
|
log
|
35
68
|
end
|
36
69
|
|
70
|
+
# Sets Log instance attributes.
|
71
|
+
#
|
72
|
+
# @param mailing [Dispatch]
|
73
|
+
# @param entity [ActiveRecord::Base]
|
74
|
+
# @param attributes [Hash] log attributes
|
75
|
+
# @option attributes [Time] :processing_at (DateTime.now)
|
76
|
+
# @option attributes [Symbol] :status
|
77
|
+
# @option attributes [Hash] :data
|
37
78
|
def set_attributes_for mailing, entity, attributes = {}
|
38
79
|
self.mailing = mailing
|
39
80
|
self.entity = entity
|
@@ -63,5 +104,43 @@ module MailyHerald
|
|
63
104
|
def scheduled?
|
64
105
|
self.status == :scheduled
|
65
106
|
end
|
107
|
+
|
108
|
+
def processed?
|
109
|
+
[:delivered, :skipped, :error].include?(self.status)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Set attributes of a schedule so it has 'skipped' status.
|
113
|
+
def skip reason
|
114
|
+
if self.status == :scheduled
|
115
|
+
self.status = :skipped
|
116
|
+
self.data[:skip_reason] = reason
|
117
|
+
true
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Set attributes of a schedule so it is postponed.
|
122
|
+
def postpone_delivery
|
123
|
+
if !self.data[:delivery_attempts] || self.data[:delivery_attempts].length < 3
|
124
|
+
self.data[:original_processing_at] ||= self.processing_at
|
125
|
+
self.data[:delivery_attempts] ||= []
|
126
|
+
self.data[:delivery_attempts].push(date_at: Time.now, action: :postpone, reason: :not_processable)
|
127
|
+
self.processing_at = Time.now + 1.day
|
128
|
+
true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Set attributes of a schedule so it has 'delivered' status.
|
133
|
+
# @param content Body of delivered email.
|
134
|
+
def deliver content
|
135
|
+
self.status = :delivered
|
136
|
+
self.data[:content] = content
|
137
|
+
end
|
138
|
+
|
139
|
+
# Set attributes of a schedule so it has 'error' status.
|
140
|
+
# @param msg Error description.
|
141
|
+
def error msg
|
142
|
+
self.status = :error
|
143
|
+
self.data[:msg] = msg
|
144
|
+
end
|
66
145
|
end
|
67
146
|
end
|
@@ -12,11 +12,14 @@ module MailyHerald
|
|
12
12
|
|
13
13
|
validates :subject, presence: true, if: :generic_mailer?
|
14
14
|
validates :template, presence: true, if: :generic_mailer?
|
15
|
+
validate :mailer_validity
|
15
16
|
validate :template_syntax
|
16
17
|
validate :validate_conditions
|
17
18
|
|
18
19
|
before_validation do
|
19
20
|
write_attribute(:name, self.title.downcase.gsub(/\W/, "_")) if self.title && (!self.name || self.name.empty?)
|
21
|
+
write_attribute(:conditions, nil) if !self.has_conditions_proc? && self.conditions.try(:empty?)
|
22
|
+
write_attribute(:from, nil) if self.from.try(:empty?)
|
20
23
|
end
|
21
24
|
|
22
25
|
after_initialize do
|
@@ -24,6 +27,58 @@ module MailyHerald
|
|
24
27
|
self.override_subscription = false
|
25
28
|
self.mailer_name = :generic
|
26
29
|
end
|
30
|
+
|
31
|
+
if @conditions_proc
|
32
|
+
self.conditions = "proc"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
after_save do
|
37
|
+
if @conditions_proc
|
38
|
+
MailyHerald.conditions_procs[self.id] = @conditions_proc
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sets mailing conditions.
|
43
|
+
#
|
44
|
+
# @param v String with Liquid expression or `Proc` that evaluates to `true` or `false`.
|
45
|
+
def conditions= v
|
46
|
+
if v.respond_to? :call
|
47
|
+
@conditions_proc = v
|
48
|
+
else
|
49
|
+
write_attribute(:conditions, v)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns time as string with Liquid expression or Proc.
|
54
|
+
def conditions
|
55
|
+
@conditions_proc || MailyHerald.conditions_procs[self.id] || read_attribute(:conditions)
|
56
|
+
end
|
57
|
+
|
58
|
+
def has_conditions_proc?
|
59
|
+
@conditions_proc || MailyHerald.conditions_procs[self.id]
|
60
|
+
end
|
61
|
+
|
62
|
+
def conditions_changed?
|
63
|
+
if has_conditions_proc?
|
64
|
+
@conditions_proc != MailyHerald.conditions_procs[self.id]
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def general_scheduling?
|
71
|
+
self.start_at.is_a?(String) && Time.parse(self.start_at).is_a?(Time)
|
72
|
+
rescue
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
def individual_scheduling?
|
77
|
+
!general_scheduling?
|
78
|
+
end
|
79
|
+
|
80
|
+
def ad_hoc?
|
81
|
+
self.class == AdHocMailing
|
27
82
|
end
|
28
83
|
|
29
84
|
def periodical?
|
@@ -42,6 +97,7 @@ module MailyHerald
|
|
42
97
|
read_attribute(:mailer_name).to_sym
|
43
98
|
end
|
44
99
|
|
100
|
+
# Returns {Mailer} class used by this Mailing.
|
45
101
|
def mailer
|
46
102
|
if generic_mailer?
|
47
103
|
MailyHerald::Mailer
|
@@ -50,29 +106,51 @@ module MailyHerald
|
|
50
106
|
end
|
51
107
|
end
|
52
108
|
|
53
|
-
|
54
|
-
self.conditions && !self.conditions.empty?
|
55
|
-
end
|
56
|
-
|
109
|
+
# Checks whether Mailing uses generic mailer.
|
57
110
|
def generic_mailer?
|
58
111
|
self.mailer_name == :generic
|
59
112
|
end
|
60
113
|
|
114
|
+
# Checks whether Mailig has conditions defined.
|
115
|
+
def has_conditions?
|
116
|
+
self.conditions && (has_conditions_proc? || !self.conditions.empty?)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Checks whether entity meets conditions of this Mailing.
|
120
|
+
#
|
121
|
+
# @raise [ArgumentError] if the conditions do not evaluate to boolean.
|
61
122
|
def conditions_met? entity
|
62
|
-
subscription = self.list.subscription_for(entity)
|
123
|
+
subscription = Subscription.get_from(entity) || self.list.subscription_for(entity)
|
63
124
|
|
64
|
-
if
|
65
|
-
|
66
|
-
evaluator.evaluate_conditions(self.conditions)
|
125
|
+
if has_conditions_proc?
|
126
|
+
!!conditions.call(entity, subscription)
|
67
127
|
else
|
68
|
-
|
128
|
+
if self.list.context.attributes
|
129
|
+
evaluator = Utils::MarkupEvaluator.new(self.list.context.drop_for(entity, subscription))
|
130
|
+
evaluator.evaluate_conditions(self.conditions)
|
131
|
+
else
|
132
|
+
true
|
133
|
+
end
|
69
134
|
end
|
70
135
|
end
|
71
136
|
|
137
|
+
# Checks whether conditions evaluate properly for given entity.
|
138
|
+
def test_conditions entity
|
139
|
+
conditions_met?(entity)
|
140
|
+
true
|
141
|
+
rescue StandardError => e
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns destination email address for given entity.
|
72
146
|
def destination entity
|
73
147
|
self.list.context.destination_for(entity)
|
74
148
|
end
|
75
149
|
|
150
|
+
# Renders email body for given entity.
|
151
|
+
#
|
152
|
+
# Reads {#template} attribute and renders it using Liquid within the context
|
153
|
+
# for provided entity.
|
76
154
|
def render_template entity
|
77
155
|
subscription = self.list.subscription_for(entity)
|
78
156
|
return unless subscription
|
@@ -81,6 +159,10 @@ module MailyHerald
|
|
81
159
|
perform_template_rendering drop, self.template
|
82
160
|
end
|
83
161
|
|
162
|
+
# Renders email subject line for given entity.
|
163
|
+
#
|
164
|
+
# Reads {#subject} attribute and renders it using Liquid within the context
|
165
|
+
# for provided entity.
|
84
166
|
def render_subject entity
|
85
167
|
subscription = self.list.subscription_for(entity)
|
86
168
|
return unless subscription
|
@@ -89,40 +171,63 @@ module MailyHerald
|
|
89
171
|
perform_template_rendering drop, self.subject
|
90
172
|
end
|
91
173
|
|
92
|
-
|
174
|
+
# Builds `Mail::Message` object for given entity.
|
175
|
+
#
|
176
|
+
# Depending on {#mailer_name} value it uses either generic mailer (from {Mailer} class)
|
177
|
+
# or custom mailer.
|
178
|
+
def build_mail schedule
|
93
179
|
if generic_mailer?
|
94
|
-
Mailer.generic(
|
180
|
+
Mailer.generic(schedule, self)
|
95
181
|
else
|
96
|
-
self.mailer.send(self.name,
|
182
|
+
self.mailer.send(self.name, schedule)
|
97
183
|
end
|
98
184
|
end
|
99
185
|
|
100
|
-
def deliver_to entity
|
101
|
-
build_mail(entity).deliver
|
102
|
-
end
|
103
|
-
|
104
186
|
protected
|
105
187
|
|
188
|
+
# Sends mailing to given entity.
|
189
|
+
#
|
190
|
+
# Performs actual sending of emails; should be called in background.
|
191
|
+
#
|
192
|
+
# Returns `Mail::Message`.
|
193
|
+
def deliver schedule
|
194
|
+
build_mail(schedule).deliver
|
195
|
+
rescue StandardError => e
|
196
|
+
MailyHerald.logger.log_processing(self, schedule.entity, prefix: "Error", level: :error)
|
197
|
+
schedule.update_attributes(status: :error, data: {msg: "#{e.to_s}\n\n#{e.backtrace.join("\n")}"})
|
198
|
+
return nil
|
199
|
+
end
|
200
|
+
|
106
201
|
# Called from Mailer, block required
|
107
|
-
def
|
202
|
+
def deliver_with_mailer schedule
|
203
|
+
entity = schedule.entity
|
204
|
+
|
108
205
|
unless processable?(entity)
|
109
|
-
|
110
|
-
|
206
|
+
# Most likely the entity went out of the context scope.
|
207
|
+
# Let's leave the log for now just in case it comes back into the scope.
|
208
|
+
MailyHerald.logger.log_processing(self, entity, prefix: "Not processable. Delaying schedule by one day", level: :debug)
|
209
|
+
skip_reason = in_scope?(entity) ? :not_processable : :not_in_scope
|
210
|
+
schedule.skip(skip_reason) unless schedule.postpone_delivery
|
211
|
+
return schedule
|
111
212
|
end
|
112
213
|
|
113
214
|
unless conditions_met?(entity)
|
114
215
|
MailyHerald.logger.log_processing(self, entity, prefix: "Conditions not met", level: :debug)
|
115
|
-
|
216
|
+
schedule.skip(:conditions_unmet)
|
217
|
+
return schedule
|
116
218
|
end
|
117
219
|
|
118
220
|
mail = yield # Let mailer do his job
|
119
221
|
|
120
222
|
MailyHerald.logger.log_processing(self, entity, mail, prefix: "Processed")
|
223
|
+
schedule.deliver(mail.to_s)
|
121
224
|
|
122
|
-
return
|
225
|
+
return schedule
|
123
226
|
rescue StandardError => e
|
124
|
-
MailyHerald.logger.log_processing(self, entity, prefix: "Error", level: :error)
|
125
|
-
|
227
|
+
MailyHerald.logger.log_processing(self, schedule.entity, prefix: "Error", level: :error)
|
228
|
+
schedule.error("#{e.to_s}\n\n#{e.backtrace.join("\n")}")
|
229
|
+
|
230
|
+
return schedule
|
126
231
|
end
|
127
232
|
|
128
233
|
private
|
@@ -136,9 +241,29 @@ module MailyHerald
|
|
136
241
|
end
|
137
242
|
|
138
243
|
def validate_conditions
|
139
|
-
|
244
|
+
return true if has_conditions_proc?
|
245
|
+
|
246
|
+
result = Utils::MarkupEvaluator.test_conditions(self.conditions)
|
247
|
+
|
248
|
+
errors.add(:conditions, "is not a boolean value") unless result
|
140
249
|
rescue StandardError => e
|
141
250
|
errors.add(:conditions, e.to_s)
|
142
251
|
end
|
252
|
+
|
253
|
+
def validate_start_at
|
254
|
+
return true if has_start_at_proc?
|
255
|
+
|
256
|
+
result = Utils::MarkupEvaluator.test_start_at(self.start_at)
|
257
|
+
|
258
|
+
errors.add(:start_at, "is not a time value") unless result
|
259
|
+
rescue StandardError => e
|
260
|
+
errors.add(:start_at, e.to_s)
|
261
|
+
end
|
262
|
+
|
263
|
+
def mailer_validity
|
264
|
+
!!mailer unless generic_mailer?
|
265
|
+
rescue
|
266
|
+
errors.add(:mailer_name, :invalid)
|
267
|
+
end
|
143
268
|
end
|
144
269
|
end
|
@@ -1,26 +1,131 @@
|
|
1
1
|
module MailyHerald
|
2
2
|
class OneTimeMailing < Mailing
|
3
3
|
validates :list, presence: true
|
4
|
+
validates :start_at, presence: true
|
5
|
+
validate :validate_start_at
|
4
6
|
|
5
|
-
|
7
|
+
after_save :update_schedules_callback, if: Proc.new{|m| m.state_changed? || m.start_at_changed? || m.override_subscription?}
|
8
|
+
|
9
|
+
# Sends mailing to all subscribed entities.
|
10
|
+
#
|
11
|
+
# Performs actual sending of emails; should be called in background.
|
12
|
+
#
|
13
|
+
# Returns array of {MailyHerald::Log} with actual `Mail::Message` objects stored
|
14
|
+
# in {MailyHerald::Log.mail} attributes.
|
6
15
|
def run
|
7
|
-
|
8
|
-
|
16
|
+
# TODO better scope here to exclude schedules for users outside context scope
|
17
|
+
schedules.where("processing_at <= (?)", Time.now).collect do |schedule|
|
18
|
+
if schedule.entity
|
19
|
+
mail = deliver schedule
|
20
|
+
schedule.reload
|
21
|
+
schedule.mail = mail
|
22
|
+
schedule
|
23
|
+
else
|
24
|
+
MailyHerald.logger.log_processing(schedule.mailing, {class: schedule.entity_type, id: schedule.entity_id}, prefix: "Removing schedule for non-existing entity")
|
25
|
+
schedule.destroy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns collection of processed {Log}s for given entity.
|
31
|
+
def processed_logs entity
|
32
|
+
Log.ordered.for_entity(entity).for_mailing(self).processed
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets the delivery schedule for given entity
|
36
|
+
#
|
37
|
+
# New schedule will be created or existing one updated.
|
38
|
+
# Schedule is {Log} object of type "schedule".
|
39
|
+
def set_schedule_for entity
|
40
|
+
if processed_logs(entity).last
|
41
|
+
# this mailing is sent only once
|
42
|
+
log = schedule_for(entity)
|
43
|
+
log.try(:destroy)
|
44
|
+
return
|
45
|
+
end
|
9
46
|
|
10
|
-
|
47
|
+
subscribed = self.list.subscribed?(entity)
|
48
|
+
start_time = start_processing_time(entity)
|
11
49
|
|
12
|
-
|
50
|
+
if !self.start_at || !enabled? || !start_time || !(self.override_subscription? || subscribed)
|
51
|
+
log = schedule_for(entity)
|
52
|
+
log.try(:destroy)
|
53
|
+
return
|
13
54
|
end
|
55
|
+
|
56
|
+
log = schedule_for(entity)
|
57
|
+
|
58
|
+
log ||= Log.new
|
59
|
+
log.with_lock do
|
60
|
+
log.set_attributes_for(self, entity, {
|
61
|
+
status: :scheduled,
|
62
|
+
processing_at: start_time,
|
63
|
+
})
|
64
|
+
log.save!
|
65
|
+
end
|
66
|
+
log
|
14
67
|
end
|
15
68
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
69
|
+
# Sets delivery schedules of all entities in mailing scope.
|
70
|
+
#
|
71
|
+
# New schedules will be created or existing ones updated.
|
72
|
+
def set_schedules
|
73
|
+
self.list.context.scope_with_subscription(self.list, :outer).each do |entity|
|
74
|
+
MailyHerald.logger.debug "Updating schedule of #{self} one-time for entity ##{entity.id} #{entity}"
|
75
|
+
set_schedule_for entity
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns {Log} object which is the delivery schedule for given entity.
|
80
|
+
def schedule_for entity
|
81
|
+
schedules.for_entity(entity).first
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns collection of all delivery schedules ({Log} collection).
|
85
|
+
def schedules
|
86
|
+
Log.ordered.scheduled.for_mailing(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns processing time for given entity.
|
90
|
+
#
|
91
|
+
# This is the time when next mailing should be sent based on
|
92
|
+
# {#start_at} mailing attribute.
|
93
|
+
def start_processing_time entity
|
94
|
+
subscription = self.list.subscription_for(entity)
|
95
|
+
|
96
|
+
if has_start_at_proc?
|
97
|
+
start_at.call(entity, subscription)
|
98
|
+
else
|
99
|
+
evaluator = Utils::MarkupEvaluator.new(self.list.context.drop_for(entity, subscription))
|
100
|
+
|
101
|
+
evaluator.evaluate_start_at(self.start_at)
|
102
|
+
end
|
20
103
|
end
|
21
104
|
|
22
105
|
def to_s
|
23
106
|
"<OneTimeMailing: #{self.title || self.name}>"
|
24
107
|
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def deliver_with_mailer schedule
|
112
|
+
current_time = Time.now
|
113
|
+
|
114
|
+
schedule.with_lock do
|
115
|
+
# make sure schedule hasn't been processed in the meantime
|
116
|
+
if schedule && schedule.processing_at <= current_time && schedule.scheduled?
|
117
|
+
schedule = super(schedule)
|
118
|
+
if schedule
|
119
|
+
schedule.processing_at = current_time if schedule.processed?
|
120
|
+
schedule.save!
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end if schedule
|
124
|
+
end
|
125
|
+
|
126
|
+
def update_schedules_callback
|
127
|
+
Rails.env.test? ? set_schedules : MailyHerald::ScheduleUpdater.perform_in(10.seconds, self.id)
|
128
|
+
end
|
129
|
+
|
25
130
|
end
|
26
131
|
end
|