Class: MailyHerald::Sequence

Inherits:
Dispatch
  • Object
show all
Includes:
Autonaming
Defined in:
app/models/maily_herald/sequence.rb

Instance Attribute Summary

Attributes inherited from Dispatch

#absolute_delay, #conditions, #from, #list_id, #mailer_name, #name, #override_subscription, #period, #sequence_id, #state, #subject, #template, #title, #type

Instance Method Summary collapse

Methods included from Autonaming

included

Methods inherited from Dispatch

#archive, #archive!, #archived?, #disable, #disable!, #disabled?, #enable, #enable!, #enabled?, #has_start_at_proc?, #in_scope?, #list=, #locked?, #processable?, #start_at, #start_at=, #start_at_changed?, #subscription_valid?

Instance Method Details

#calculate_processing_time_for(entity, mailing = nil) ⇒ Object

Calculates processing time for given entity.

[View source]

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'app/models/maily_herald/sequence.rb', line 181

def calculate_processing_time_for entity, mailing = nil
  mailing ||= next_mailing(entity)
  ls = processed_logs(entity)

  if ls.first
    ls.last.processing_at + (mailing.absolute_delay - ls.last.mailing.absolute_delay)
  else
    subscription = self.list.subscription_for(entity)

    if has_start_at_proc?
      evaluated_start = start_at.call(entity, subscription)
    else
      evaluator = Utils::MarkupEvaluator.new(self.list.context.drop_for(entity, subscription))

      evaluated_start = evaluator.evaluate_start_at(self.start_at)
    end

    evaluated_start ? evaluated_start + mailing.absolute_delay : nil
  end
end

#last_processed_mailing(entity) ⇒ Object

Gets last MailyHerald::SequenceMailing object delivered to user.

[View source]

113
114
115
# File 'app/models/maily_herald/sequence.rb', line 113

def last_processed_mailing entity
  processed_mailings(entity).last
end

#last_processing_time(entity) ⇒ Object

Gets the timestamp of last processed email for given entity.

[View source]

95
96
97
98
# File 'app/models/maily_herald/sequence.rb', line 95

def last_processing_time entity
  ls = processed_logs(entity)
  ls.last.processing_at if ls.last
end

#mailing(name, options = {}) ⇒ Object

Fetches or defines an MailyHerald::SequenceMailing.

If no block provided, MailyHerald::SequenceMailing with given name is returned.

If block provided, MailyHerald::SequenceMailing with given name is created or edited and block is evaluated within that mailing.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :locked (true, false) — default: false

    Determines whether Mailing is locked.

See Also:

[View source]

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'app/models/maily_herald/sequence.rb', line 41

def mailing name, options = {}
  if SequenceMailing.table_exists?
    mailing = SequenceMailing.find_by_name(name)
    lock = options.delete(:locked)

    if block_given? && !MailyHerald.dispatch_locked?(name) && (!mailing || lock)
      mailing ||= self.mailings.build(name: name)
      mailing.sequence = self
      yield(mailing)
      mailing.skip_updating_schedules = true if self.new_record?
      mailing.save!

      MailyHerald.lock_dispatch(name) if lock
    end

    mailing
  end
end

#mailing_processing_log_for(entity, mailing) ⇒ Object

[View source]

122
123
124
# File 'app/models/maily_herald/sequence.rb', line 122

def mailing_processing_log_for entity, mailing
  Log.ordered.processed.for_entity(entity).for_mailing(mailing).last
end

#next_mailing(entity) ⇒ Object

Gets next MailyHerald::SequenceMailing object to be delivered to user.

[View source]

118
119
120
# File 'app/models/maily_herald/sequence.rb', line 118

def next_mailing entity
  pending_mailings(entity).first
end

#next_processing_time(entity) ⇒ Object

Get next email processing time for given entity.

[View source]

203
204
205
# File 'app/models/maily_herald/sequence.rb', line 203

def next_processing_time entity
  schedule_for(entity).try(:processing_at)
end

#pending_mailings(entity) ⇒ Object

Gets collection of MailyHerald::SequenceMailing objects that are to be sent to entity.

[View source]

101
102
103
104
# File 'app/models/maily_herald/sequence.rb', line 101

def pending_mailings entity
  ls = processed_logs(entity)
  ls.empty? ? self.mailings.enabled : self.mailings.enabled.where("id not in (?)", ls.map(&:mailing_id))
end

#processed_logs(entity) ⇒ Object

Returns collection of processed Logs for given entity.

[View source]

82
83
84
# File 'app/models/maily_herald/sequence.rb', line 82

def processed_logs entity
  Log.ordered.processed.for_entity(entity).for_mailings(self.mailings.select(:id))
end

#processed_logs_for(entity, mailing) ⇒ Object

Returns collection of processed Logs for given entity and mailing.

Parameters:

[View source]

90
91
92
# File 'app/models/maily_herald/sequence.rb', line 90

def processed_logs_for entity, mailing
  Log.ordered.processed.for_entity(entity).for_mailing(self.mailings.find(mailing))
end

#processed_mailings(entity) ⇒ Object

Gets collection of MailyHerald::SequenceMailing objects that were sent to entity.

[View source]

107
108
109
110
# File 'app/models/maily_herald/sequence.rb', line 107

def processed_mailings entity
  ls = processed_logs(entity)
  ls.empty? ? self.mailings.where(id: nil) : self.mailings.where("id in (?)", ls.map(&:mailing_id))
end

#runObject

Sends sequence mailings to all subscribed entities.

Performs actual sending of emails; should be called in background.

Returns array of Log with actual ‘Mail::Message` objects stored in Log.mail attributes.

[View source]

66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'app/models/maily_herald/sequence.rb', line 66

def run
  # TODO better scope here to exclude schedules for users outside context scope
  schedules.where("processing_at <= (?)", Time.now).each do |schedule|
    if schedule.entity
      mail = schedule.mailing.send(:deliver, schedule)
      schedule.reload
      schedule.mail = mail
      schedule
    else
      MailyHerald.logger.log_processing(schedule.mailing, {class: schedule.entity_type, id: schedule.entity_id}, prefix: "Removing schedule for non-existing entity") 
      schedule.destroy
    end
  end
end

#schedule_for(entity) ⇒ Object

Returns Log object which is the delivery schedule for given entity.

[View source]

171
172
173
# File 'app/models/maily_herald/sequence.rb', line 171

def schedule_for entity
  schedules.for_entity(entity).first
end

#schedulesObject

Returns collection of all delivery schedules (Log collection).

[View source]

176
177
178
# File 'app/models/maily_herald/sequence.rb', line 176

def schedules
  Log.ordered.scheduled.for_mailings(self.mailings.select(:id))
end

#set_schedule_for(entity) ⇒ Object

Sets the delivery schedule for given entity

New schedule will be created or existing one updated.

Schedule is Log object of type “schedule”.

[View source]

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'app/models/maily_herald/sequence.rb', line 131

def set_schedule_for entity
  # TODO handle override subscription?

  subscribed = self.list.subscribed?(entity)
  mailing = next_mailing(entity)
  start_time = calculate_processing_time_for(entity, mailing) if mailing

  if !subscribed || !self.start_at || !enabled? || !mailing || !start_time 
    log = schedule_for(entity)
    log.try(:destroy)
    return
  end

  log = schedule_for(entity)
  log ||= Log.new
  log.with_lock do
    log.set_attributes_for(mailing, entity, {
      status: :scheduled,
      processing_at: start_time,
    })
    log.save!
  end
  log
end

#set_schedulesObject

Sets delivery schedules of all entities in mailing scope.

New schedules will be created or existing ones updated.

[View source]

159
160
161
162
163
164
# File 'app/models/maily_herald/sequence.rb', line 159

def set_schedules
  self.list.context.scope_with_subscription(self.list, :outer).each do |entity|
    MailyHerald.logger.debug "Updating schedule of #{self} sequence for entity ##{entity.id} #{entity}"
    set_schedule_for entity
  end
end

#to_sObject

[View source]

207
208
209
# File 'app/models/maily_herald/sequence.rb', line 207

def to_s
  "<Sequence: #{self.title || self.name}>"
end

#update_schedules_callbackObject

[View source]

166
167
168
# File 'app/models/maily_herald/sequence.rb', line 166

def update_schedules_callback
  Rails.env.test? ? set_schedules : MailyHerald::ScheduleUpdater.perform_in(10.seconds, self.id)
end