Class: Subscriber
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Subscriber
- Includes:
- Models::Auditable
- Defined in:
- app/models/subscriber.rb
Overview
== Schema Information
Table name: subscribers
Database name: primary
id :integer not null, primary key
active :boolean default(TRUE), not null
email_address :string
reconciled :boolean default(FALSE), not null
created_at :datetime not null
updated_at :datetime not null
creator_id :integer
customer_id :integer
subscriber_list_id :integer
updater_id :integer
Indexes
idx_unique_subscribers (subscriber_list_id,customer_id) UNIQUE
index_subscribers_on_active (active)
index_subscribers_on_customer_id (customer_id)
index_subscribers_on_email_address (email_address)
Foreign Keys
fk_rails_... (customer_id => parties.id) ON DELETE => cascade
fk_rails_... (subscriber_list_id => subscriber_lists.id) ON DELETE => cascade
Constant Summary collapse
- ACTIVITY_DELIVERY_STATES =
campaign_delivery states that mean an email was actually transmitted or
attempted — the bar for "has seen activity" that forces archival over
deletion. pending/queued/suppressed/duplicate/exception sent nothing. %w[sent bounced].freeze
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Constants included from Schedulable
Schedulable::SIMPLE_FORM_OPTIONS
Instance Attribute Summary collapse
- #customer_id ⇒ Object readonly
-
#email_address ⇒ Object
readonly
subscriber_list is optional (and nil once a subscriber is archived/detached), so guard the list-scoped validations with &.
-
#update_customer ⇒ Object
Returns the value of attribute update_customer.
Belongs to collapse
Methods included from Models::Auditable
Has many collapse
-
#campaign_deliveries ⇒ ActiveRecord::Relation<CampaignDelivery>
dependent: :destroy so a subscriber with NO delivery activity takes its (pending/suppressed/…) deliveries with it on hard-delete.
- #campaigns ⇒ ActiveRecord::Relation<Campaign>
- #contact_points ⇒ ActiveRecord::Relation<ContactPoint>
Class Method Summary collapse
-
.active ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are active.
-
.archived ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are archived.
-
.inactive ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are inactive.
-
.unarchived ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are unarchived.
-
.with_campaign_activity ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are with campaign activity.
-
.with_customer ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are with customer.
Instance Method Summary collapse
- #activate ⇒ Object
-
#archive! ⇒ Boolean
Retain the subscriber for record-keeping: stamp archived_at and detach it from its list (the list is being removed; subscriber_list_id is nullable).
-
#archived? ⇒ Boolean
Whether this subscriber has been archived for record-keeping.
- #deactivate ⇒ Object
- #determined_customer ⇒ Object
- #name ⇒ Object
- #party ⇒ Object
- #party_customer ⇒ Object
- #populate_email_if_missing ⇒ Object
-
#remove! ⇒ Boolean
The safe "remove a subscriber" entry point: archive it for record-keeping if it has delivery activity, otherwise hard-delete it (cascading its inert pending/suppressed deliveries).
-
#seen_campaign_activity? ⇒ Boolean
True once an email was actually transmitted to / attempted for this subscriber (a
sentorbounceddelivery exists). - #update_linked_contact_points ⇒ Object
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#customer_id ⇒ Object (readonly)
50 |
# File 'app/models/subscriber.rb', line 50 validates :customer_id, presence: true, uniqueness: { scope: :subscriber_list_id, message: 'can only be present once on each subscriber list' }, if: proc { |s| s.subscriber_list&.customer? } |
#email_address ⇒ Object (readonly)
subscriber_list is optional (and nil once a subscriber is archived/detached),
so guard the list-scoped validations with &. — a nil list has no scope to
enforce uniqueness against.
Validations (if => proc { |s| s.subscriber_list&.email? } ):
- Email_format
- Presence
- Uniqueness ({ scope: :subscriber_list_id, message: 'can only be present once on each subscriber list' })
49 |
# File 'app/models/subscriber.rb', line 49 validates :email_address, email_format: true, presence: true, uniqueness: { scope: :subscriber_list_id, message: 'can only be present once on each subscriber list' }, if: proc { |s| s.subscriber_list&.email? } |
#update_customer ⇒ Object
Returns the value of attribute update_customer.
70 71 72 |
# File 'app/models/subscriber.rb', line 70 def update_customer @update_customer end |
Class Method Details
.active ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are active. Active Record Scope
57 |
# File 'app/models/subscriber.rb', line 57 scope :active, -> { where(active: true) } |
.archived ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are archived. Active Record Scope
59 |
# File 'app/models/subscriber.rb', line 59 scope :archived, -> { where.not(archived_at: nil) } |
.inactive ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are inactive. Active Record Scope
58 |
# File 'app/models/subscriber.rb', line 58 scope :inactive, -> { where(active: false) } |
.unarchived ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are unarchived. Active Record Scope
60 |
# File 'app/models/subscriber.rb', line 60 scope :unarchived, -> { where(archived_at: nil) } |
.with_campaign_activity ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are with campaign activity. Active Record Scope
62 |
# File 'app/models/subscriber.rb', line 62 scope :with_campaign_activity, -> { where(id: CampaignDelivery.where(state: ACTIVITY_DELIVERY_STATES).select(:subscriber_id)) } |
.with_customer ⇒ ActiveRecord::Relation<Subscriber>
A relation of Subscribers that are with customer. Active Record Scope
64 65 66 67 68 |
# File 'app/models/subscriber.rb', line 64 scope :with_customer, -> { joins("left outer JOIN contact_points on contact_points.id = (select max(id) from contact_points where contact_points.detail = subscribers.email_address) left outer JOIN parties contacts on contacts.id = contact_points.party_id and contacts.type = 'Contact' left outer JOIN parties customers on customers.id = coalesce(subscribers.customer_id, contacts.customer_id, contact_points.party_id)") } |
Instance Method Details
#activate ⇒ Object
85 86 87 |
# File 'app/models/subscriber.rb', line 85 def activate update(active: true) end |
#archive! ⇒ Boolean
Retain the subscriber for record-keeping: stamp archived_at and detach it
from its list (the list is being removed; subscriber_list_id is nullable).
update_columns bypasses validations/callbacks — a detached subscriber has no
list to scope the email/customer uniqueness checks against.
112 113 114 |
# File 'app/models/subscriber.rb', line 112 def archive! update_columns(archived_at: Time.current, subscriber_list_id: nil) end |
#archived? ⇒ Boolean
Returns whether this subscriber has been archived for record-keeping.
102 103 104 |
# File 'app/models/subscriber.rb', line 102 def archived? archived_at.present? end |
#campaign_deliveries ⇒ ActiveRecord::Relation<CampaignDelivery>
dependent: :destroy so a subscriber with NO delivery activity takes its
(pending/suppressed/…) deliveries with it on hard-delete. Subscribers WITH
activity are archived instead (archive_instead_of_destroy_if_activity, which
aborts the destroy before this cascade runs), so their deliveries are kept.
42 |
# File 'app/models/subscriber.rb', line 42 has_many :campaign_deliveries, dependent: :destroy |
#campaigns ⇒ ActiveRecord::Relation<Campaign>
44 |
# File 'app/models/subscriber.rb', line 44 has_many :campaigns, through: :subscriber_list |
#contact_points ⇒ ActiveRecord::Relation<ContactPoint>
43 |
# File 'app/models/subscriber.rb', line 43 has_many :contact_points, foreign_key: :detail, primary_key: :email_address |
#customer ⇒ Customer
35 |
# File 'app/models/subscriber.rb', line 35 belongs_to :customer, optional: true |
#deactivate ⇒ Object
89 90 91 |
# File 'app/models/subscriber.rb', line 89 def deactivate update(active: false) end |
#determined_customer ⇒ Object
143 144 145 |
# File 'app/models/subscriber.rb', line 143 def determined_customer customer || party_customer end |
#email_preference ⇒ EmailPreference
36 |
# File 'app/models/subscriber.rb', line 36 belongs_to :email_preference, optional: true, primary_key: :email, foreign_key: :email_address |
#name ⇒ Object
127 128 129 |
# File 'app/models/subscriber.rb', line 127 def name party&.full_name end |
#party ⇒ Object
131 132 133 |
# File 'app/models/subscriber.rb', line 131 def party contact_points.where.not(party_id: nil).first&.party end |
#party_customer ⇒ Object
135 136 137 138 139 140 141 |
# File 'app/models/subscriber.rb', line 135 def party_customer if party.respond_to?(:customer) party.customer else party end end |
#populate_email_if_missing ⇒ Object
151 152 153 154 155 156 157 |
# File 'app/models/subscriber.rb', line 151 def populate_email_if_missing return if email_address.present? return unless subscriber_list&.email? return unless customer self.email_address = customer.email end |
#remove! ⇒ Boolean
The safe "remove a subscriber" entry point: archive it for record-keeping if
it has delivery activity, otherwise hard-delete it (cascading its inert
pending/suppressed deliveries). A bare #destroy is blocked for subscribers
with activity (see prevent_hard_delete_with_activity), so callers that mean
"remove from the list" should use this.
123 124 125 |
# File 'app/models/subscriber.rb', line 123 def remove! seen_campaign_activity? ? archive! : destroy! end |
#seen_campaign_activity? ⇒ Boolean
True once an email was actually transmitted to / attempted for this
subscriber (a sent or bounced delivery exists).
97 98 99 |
# File 'app/models/subscriber.rb', line 97 def seen_campaign_activity? campaign_deliveries.where(state: ACTIVITY_DELIVERY_STATES).exists? end |
#subscriber_list ⇒ SubscriberList
34 |
# File 'app/models/subscriber.rb', line 34 belongs_to :subscriber_list, inverse_of: :subscribers, optional: true |
#update_linked_contact_points ⇒ Object
147 148 149 |
# File 'app/models/subscriber.rb', line 147 def update_linked_contact_points ContactPoint.where(detail: email_address_was).update_all(detail: email_address) end |