Class: ContactPoint

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable, PgSearch::Model
Defined in:
app/models/contact_point.rb

Overview

rubocop:disable Metrics/ClassLength
== Schema Information

Table name: contact_points
Database name: primary

id :integer not null, primary key
area_code :string(10)
category :string(255)
country_code :integer
detail :string(255)
extension :string(10)
notes :string(255)
position :integer
restricted_notes :text
skip_validation :boolean default(FALSE), not null
sms_status :integer default("sms_none"), not null
state :string(25)
system_notes :text
timezone_name :string
created_at :datetime
updated_at :datetime
locator_record_id :integer
party_id :integer

Indexes

by_pid_category_w (party_id,category) WHERE (detail IS NOT NULL)
contact_points_party_id_index (party_id)
idx_category_party_id_detail (category,party_id,detail)
idx_detail_party_id (detail,party_id)
idx_trigram_detail (detail) USING gin
index_contact_points_locator_record_id (locator_record_id)
index_contact_points_on_position_and_party_id (position,party_id)

Foreign Keys

contact_points_party_id_fk (party_id => parties.id) ON DELETE => nullify

Defined Under Namespace

Classes: AddressBookBuilder

Constant Summary collapse

HOUZZ_REGEX =

Platform-specific regexes — require https:// (normalise_format ensures this before validation)

%r{\Ahttps?://(www\.)?houzz\.com/}i
INSTAGRAM_REGEX =
%r{\Ahttps?://(www\.)?instagram\.com/}i
FACEBOOK_REGEX =
%r{\Ahttps?://(www\.)?facebook\.com/}i
PINTEREST_REGEX =
%r{\Ahttps?://(www\.)?pinterest\.com/}i
LINKEDIN_REGEX =
%r{\Ahttps?://(www\.)?linkedin\.com/}i
WEBSITE_REGEX =

Generic website: must start with https:// or http://, have a real host with a dot.
Full structural validation is done via Addressable::URI in valid_website_url?.

%r{\Ahttps?://[^/\s]+\.[^/\s]}i
TWITTER_REGEX =
/@.*/
EMAIL_REGEXP =
RFC822::EMAIL
PHONE =
'phone'.freeze
EMAIL =
'email'.freeze
FAX =
'fax'.freeze
CELL =
'cell'.freeze
WEBSITE =
'website'.freeze
FACEBOOK =
'facebook'.freeze
TWITTER =
'twitter'.freeze
PINTEREST =
'pinterest'.freeze
LINKEDIN =
'linkedin'.freeze
INSTAGRAM =
'instagram'.freeze
HOUZZ =
'houzz'.freeze
CAN_DIAL =
[CELL, FAX, PHONE].freeze
CAN_TRANSMIT =
[FAX, EMAIL].freeze
CAN_VOICE =
[CELL, PHONE].freeze
ALL_CATEGORIES =
[PHONE, CELL, FAX, EMAIL, WEBSITE, FACEBOOK, TWITTER, PINTEREST, LINKEDIN, INSTAGRAM, HOUZZ].freeze
CONTACTABLE =
[EMAIL, PHONE, CELL, FAX].freeze
INVALID_URL_MESSAGE =
'must be a valid URL starting with https:// (e.g. https://example.com)'.freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has one collapse

Has many collapse

Has and belongs to many collapse

Class Method Summary collapse

Instance Method Summary collapse

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 Models::EventPublishable

#publish_event

Instance Attribute Details

#categoryObject (readonly)

rubocop:disable Rails/I18nLocaleTexts -- validation messages intentionally inline; locale extraction deferred

Validations:



114
# File 'app/models/contact_point.rb', line 114

validates :category, :detail, presence: true

#detailObject (readonly)

rubocop:disable Rails/I18nLocaleTexts -- validation messages intentionally inline; locale extraction deferred

Validations:

  • Presence
  • Format ({ with: FACEBOOK_REGEX, if: :facebook?, message: 'must be a valid facebook.com url starting with http:// or https://' })
  • Format ({ with: TWITTER_REGEX, if: :twitter?, message: 'must be a valid twitter @username' })
  • Format ({ with: PINTEREST_REGEX, if: :pinterest?, message: 'must be a valid pinterest.com url starting with http:// or https://' })
  • Format ({ with: LINKEDIN_REGEX, if: :linkedin?, message: 'must be a valid linkedin.com url starting with http:// or https://' })
  • Length ({ maximum: 255 })

Validations (if => -> { !skip_validation && can_be_dialed? } ):

  • Phone_format

Validations (if => #email? ):

  • Email_format


114
# File 'app/models/contact_point.rb', line 114

validates :category, :detail, presence: true

#extensionObject (readonly)

rubocop:enable Rails/I18nLocaleTexts

Validations:



132
# File 'app/models/contact_point.rb', line 132

validates :extension, length: { maximum: 10 }

#requiredObject

Returns the value of attribute required.



157
158
159
# File 'app/models/contact_point.rb', line 157

def required
  @required
end

Class Method Details

.begins_withActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are begins with. Active Record Scope

Returns:

See Also:



148
# File 'app/models/contact_point.rb', line 148

scope :begins_with,      ->(term)  { where(ContactPoint[:detail].matches("#{term}%")) }

.belonging_to_partyActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are belonging to party. Active Record Scope

Returns:

See Also:



150
# File 'app/models/contact_point.rb', line 150

scope :belonging_to_party, -> { where ContactPoint[:party_id].not_eq(nil) }

.build_from_string(val, contact_points = nil, category = nil) ⇒ Object

Tries to build a contact point identifying the string.
Returns an existing contact point from the collection if detail matches,
otherwise builds a new one.



207
208
209
210
211
212
213
214
215
216
# File 'app/models/contact_point.rb', line 207

def self.build_from_string(val, contact_points = nil, category = nil)
  return if val.blank?

  found = contact_points.detect { |cp| cp.detail == val }
  return found if found

  ncp = (contact_points || ContactPoint).new(detail: val, category: category)
  ncp.normalize_format
  ncp
end

.by_categoryActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are by category. Active Record Scope

Returns:

See Also:



135
# File 'app/models/contact_point.rb', line 135

scope :by_category,      ->(categories) { where(category: categories).where.not(detail: nil) }

.category_options_for_selectObject



218
219
220
# File 'app/models/contact_point.rb', line 218

def self.category_options_for_select
  ALL_CATEGORIES.map { |cat| [cat.humanize, cat] }
end

.company_sms_numbersActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are company sms numbers. Active Record Scope

Returns:

See Also:



154
# File 'app/models/contact_point.rb', line 154

scope :company_sms_numbers, -> { joins(:employee_phone_status).sorted }

.contactableActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are contactable. Active Record Scope

Returns:

See Also:



143
# File 'app/models/contact_point.rb', line 143

scope :contactable,      -> { by_category(CAN_DIAL + [EMAIL]) }

.contactable_options_for_selectObject



222
223
224
# File 'app/models/contact_point.rb', line 222

def self.contactable_options_for_select
  CONTACTABLE.map { |cat| [cat.humanize, cat] }
end

.containsActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are contains. Active Record Scope

Returns:

See Also:



147
# File 'app/models/contact_point.rb', line 147

scope :contains,         ->(term)  { where(ContactPoint[:detail].matches("%#{term}%")) }

.dialableActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are dialable. Active Record Scope

Returns:

See Also:



141
# File 'app/models/contact_point.rb', line 141

scope :dialable,         -> { by_category(CAN_DIAL) }

.emailsActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are emails. Active Record Scope

Returns:

See Also:



137
# File 'app/models/contact_point.rb', line 137

scope :emails,           -> { by_category(EMAIL) }

.failedActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are failed. Active Record Scope

Returns:

See Also:



149
# File 'app/models/contact_point.rb', line 149

scope :failed,           -> { where(state: 'failed') }

.faxesActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are faxes. Active Record Scope

Returns:

See Also:



139
# File 'app/models/contact_point.rb', line 139

scope :faxes,            -> { by_category(FAX) }

.find_emailsActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are find emails. Active Record Scope

Returns:

See Also:



145
# File 'app/models/contact_point.rb', line 145

scope :find_emails,      ->(email) { emails.where(ContactPoint[:detail].matches(email)) }

.find_phonesActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are find phones. Active Record Scope

Returns:

See Also:



146
# File 'app/models/contact_point.rb', line 146

scope :find_phones,      ->(phone) { dialable.begins_with(phone) }

.mark_email_as_failed(email, notes = nil) ⇒ Object



170
171
172
173
174
175
# File 'app/models/contact_point.rb', line 170

def self.mark_email_as_failed(email, notes = nil)
  ContactPoint.find_emails(email).each do |cp|
    cp.system_notes = notes if notes.present?
    cp.mark_as_failed
  end
end

.mark_email_as_working(email) ⇒ Object



188
189
190
191
192
193
194
# File 'app/models/contact_point.rb', line 188

def self.mark_email_as_working(email)
  ContactPoint.find_emails(email).where(state: %w[unknown failed]).find_each do |cp|
    cp.system_notes = nil
    Sendgrid::Toolkit.delete_invalids(emails: [email])
    cp.mark_as_working
  end
end

.migrate_dataObject

One-time migration utility — run via rails runner, not in production request cycle.
rubocop:disable Rails/SkipsModelValidations, Metrics/AbcSize



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'app/models/contact_point.rb', line 363

def self.migrate_data
  contact_points = ContactPoint.dialable
  total_records = contact_points.size
  current_record = 0
  contact_points.find_each do |cp|
    Rails.logger.info "[#{current_record += 1}/#{total_records}] #{cp.id}"
    cp.send(:normalize_format)
    raise "Encountered anomaly: #{cp.id} #{cp.detail}" if cp.detail.blank?

    cp.update_columns(cp.attributes)
  end

  CallRecord.connection.execute "update call_records set origin_number = '+1' || origin_number where length(origin_number) = 10;"
  CallRecord.connection.execute "update call_records set destination_number = '+1' || destination_number where length(destination_number) = 10;"
  CallLog.connection.execute "update call_logs set from_number = '+1' || from_number where length(from_number) = 10;"
  CallLog.connection.execute "update call_logs set to_number = '+1' || to_number where length(to_number) = 10;"
  DoNotCall.connection.execute "update do_not_calls set number = '+1' || number where length(number) = 10"
  Order.connection.execute "update orders set shipping_phone = '+1' || shipping_phone where length(shipping_phone) = 10"
end

.pbx_didsActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are pbx dids. Active Record Scope

Returns:

See Also:



144
# File 'app/models/contact_point.rb', line 144

scope :pbx_dids,         -> { by_category(PHONE).joins(:employee_phone_status) }

.sms_numbersActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are sms numbers. Active Record Scope

Returns:

See Also:



155
# File 'app/models/contact_point.rb', line 155

scope :sms_numbers,         -> { where(sms_status: [1, 2]) }

.sms_statuses_for_selectObject



226
227
228
229
230
231
# File 'app/models/contact_point.rb', line 226

def self.sms_statuses_for_select
  [
    ['Not SMS capable', 'sms_none'],
    ['SMS enabled', 'sms_enabled']
  ]
end

.sortedActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are sorted. Active Record Scope

Returns:

See Also:



142
# File 'app/models/contact_point.rb', line 142

scope :sorted,           -> { order('contact_points.category, contact_points.position') }

.transmittableActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are transmittable. Active Record Scope

Returns:

See Also:



136
# File 'app/models/contact_point.rb', line 136

scope :transmittable,    -> { by_category(CAN_TRANSMIT) }

.under_customer_idsActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are under customer ids. Active Record Scope

Returns:

See Also:



151
152
153
# File 'app/models/contact_point.rb', line 151

scope :under_customer_ids, ->(customer_ids) {
  where('contact_points.party_id IN (:customer_ids) or exists(select 1 from parties cnt where cnt.customer_id IN (:customer_ids) and cnt.id = contact_points.party_id)', customer_ids: [customer_ids].flatten)
}

.voice_callableActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are voice callable. Active Record Scope

Returns:

See Also:



140
# File 'app/models/contact_point.rb', line 140

scope :voice_callable,   -> { by_category(CAN_VOICE) }

.website_category(uri) ⇒ Object

Returns the canonical ContactPoint category string for the given URL, or nil.



178
179
180
181
182
183
184
185
186
# File 'app/models/contact_point.rb', line 178

def self.website_category(uri)
  return HOUZZ     if HOUZZ_REGEX.match?(uri)
  return INSTAGRAM if INSTAGRAM_REGEX.match?(uri)
  return FACEBOOK  if FACEBOOK_REGEX.match?(uri)
  return PINTEREST if PINTEREST_REGEX.match?(uri)
  return LINKEDIN  if LINKEDIN_REGEX.match?(uri)

  WEBSITE if WEBSITE_REGEX.match?(uri)
end

.websitesActiveRecord::Relation<ContactPoint>

A relation of ContactPoints that are websites. Active Record Scope

Returns:

See Also:



138
# File 'app/models/contact_point.rb', line 138

scope :websites,         -> { by_category(WEBSITE) }

Instance Method Details

#area_code_objectAreaCode

Returns:

See Also:



81
# File 'app/models/contact_point.rb', line 81

belongs_to :area_code_object, class_name: 'AreaCode', inverse_of: :contact_points, foreign_key: :area_code, primary_key: :code, optional: true

#blocked?Boolean

Returns:

  • (Boolean)


312
313
314
# File 'app/models/contact_point.rb', line 312

def blocked?
  email_blocked? || dnc_blocked?
end

#call_blocked?Boolean

Returns:

  • (Boolean)


296
297
298
# File 'app/models/contact_point.rb', line 296

def call_blocked?
  callable? && do_not_call&.do_not_call&.to_b
end

#callable?Boolean

Returns:

  • (Boolean)


284
285
286
# File 'app/models/contact_point.rb', line 284

def callable?
  CAN_VOICE.include?(category)
end

#can_be_dialed?Boolean

Returns:

  • (Boolean)


248
249
250
# File 'app/models/contact_point.rb', line 248

def can_be_dialed?
  CAN_DIAL.include?(category)
end

#category_and_detailObject



244
245
246
# File 'app/models/contact_point.rb', line 244

def category_and_detail
  "#{category}: #{detail}"
end

#communication_recipientsActiveRecord::Relation<CommunicationRecipient>

Returns:

See Also:



90
# File 'app/models/contact_point.rb', line 90

has_many :communication_recipients, dependent: :nullify, inverse_of: :contact_point

#contactable?Boolean

Returns:

  • (Boolean)


196
197
198
# File 'app/models/contact_point.rb', line 196

def contactable?
  (CAN_DIAL + [EMAIL]).include?(category)
end

#dependents?Boolean

Returns:

  • (Boolean)


320
321
322
323
324
325
326
327
# File 'app/models/contact_point.rb', line 320

def dependents?
  quotes.exists? ||
    room_configurations.exists? ||
    communication_recipients.exists? ||
    opportunity_participants.exists? ||
    feeds.exists? ||
    service_job_dependent?
end

#dial_stringObject



333
334
335
336
337
# File 'app/models/contact_point.rb', line 333

def dial_string
  return phone_number.to_s if phone_number?

  detail
end

#dnc_blocked?Boolean

Returns:

  • (Boolean)


308
309
310
# File 'app/models/contact_point.rb', line 308

def dnc_blocked?
  call_blocked? || fax_blocked? || text_blocked?
end

#do_not_callDoNotCall

Returns:

See Also:



84
# File 'app/models/contact_point.rb', line 84

belongs_to :do_not_call, foreign_key: :detail, primary_key: :number, optional: true

#email?Boolean

Returns:

  • (Boolean)


252
253
254
# File 'app/models/contact_point.rb', line 252

def email?
  category == ContactPoint::EMAIL
end

#email_blocked?Boolean

Returns:

  • (Boolean)


292
293
294
# File 'app/models/contact_point.rb', line 292

def email_blocked?
  email? && email_preference&.any?
end

#email_preferenceEmailPreference

rubocop:disable Rails/InverseOf -- polymorphic FK/PK pair; inverse not supported



83
# File 'app/models/contact_point.rb', line 83

belongs_to :email_preference, foreign_key: :detail, primary_key: :email, optional: true

#email_support_case_participantsActiveRecord::Relation<SupportCaseParticipant>

Returns:

See Also:



96
# File 'app/models/contact_point.rb', line 96

has_many :email_support_case_participants, class_name: 'SupportCaseParticipant', dependent: :nullify, foreign_key: :email_id, inverse_of: :email

#employee_phone_statusEmployeePhoneStatus

rubocop:enable Rails/InverseOf



86
# File 'app/models/contact_point.rb', line 86

has_one :employee_phone_status, dependent: :destroy

#facebook?Boolean

Returns:

  • (Boolean)


260
261
262
# File 'app/models/contact_point.rb', line 260

def facebook?
  category == ContactPoint::FACEBOOK
end

#failed_before?Boolean

Returns:

  • (Boolean)


200
201
202
# File 'app/models/contact_point.rb', line 200

def failed_before?
  ContactPoint.exists?(detail: detail, state: 'failed')
end

#fax?Boolean

Returns:

  • (Boolean)


288
289
290
# File 'app/models/contact_point.rb', line 288

def fax?
  category == ContactPoint::FAX
end

#fax_blocked?Boolean

Returns:

  • (Boolean)


300
301
302
# File 'app/models/contact_point.rb', line 300

def fax_blocked?
  fax? && do_not_call&.do_not_fax&.to_b
end

#fax_support_case_participantsActiveRecord::Relation<SupportCaseParticipant>

Returns:

See Also:



97
# File 'app/models/contact_point.rb', line 97

has_many :fax_support_case_participants,   class_name: 'SupportCaseParticipant', dependent: :nullify, foreign_key: :fax_id,   inverse_of: :fax

#feedsActiveRecord::Relation<Feed>

Returns:

  • (ActiveRecord::Relation<Feed>)

See Also:



101
# File 'app/models/contact_point.rb', line 101

has_and_belongs_to_many :feeds,  inverse_of: :contact_points

#formatted_dial_stringObject



339
340
341
342
343
344
345
346
347
348
349
# File 'app/models/contact_point.rb', line 339

def formatted_dial_string
  if phone_number? && phone_number
    if party&.is_employee? && party.employee_record.hide_signature_pbx_extension
      phone_number.format(:crm_no_extension)
    else
      phone_number.format(:crm)
    end
  else
    detail
  end
end

#formatted_for_smsObject



351
352
353
354
355
# File 'app/models/contact_point.rb', line 351

def formatted_for_sms
  return unless phone_number? && sms_enabled?

  phone_number&.format(:pbx_dial)
end

#linkedin?Boolean

Returns:

  • (Boolean)


272
273
274
# File 'app/models/contact_point.rb', line 272

def linkedin?
  category == ContactPoint::LINKEDIN
end

#locator_recordLocatorRecord



80
# File 'app/models/contact_point.rb', line 80

belongs_to :locator_record, inverse_of: :contact_points, optional: true

#mark_as_working_if_detail_changedObject (protected)



415
416
417
# File 'app/models/contact_point.rb', line 415

protected def mark_as_working_if_detail_changed
  self.state = 'working' if (category == EMAIL) && detail_changed?
end

#normalize_formatObject

rubocop:enable Rails/SkipsModelValidations, Metrics/AbcSize



384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'app/models/contact_point.rb', line 384

def normalize_format
  return if detail.blank?

  normalize_detail_characters
  if RFC822::EMAIL.match?(detail)
    normalize_as_email
  elsif website?
    normalize_as_website
  elsif (parsed = PhoneNumber.parse(detail))
    normalize_as_phone(parsed)
  end

  true
end

#notification_channelsActiveRecord::Relation<NotificationChannel>

Returns:

See Also:



89
# File 'app/models/contact_point.rb', line 89

has_many :notification_channels, dependent: :destroy

#opportunity_participantsActiveRecord::Relation<OpportunityParticipant>

rubocop:enable Rails/InverseOf, Rails/HasManyOrHasOneDependent

Returns:

See Also:



94
# File 'app/models/contact_point.rb', line 94

has_many :opportunity_participants, dependent: :nullify, inverse_of: :contact_point

#partyParty

Returns:

See Also:



79
# File 'app/models/contact_point.rb', line 79

belongs_to :party, inverse_of: :contact_points, optional: true

#phone_numberObject



329
330
331
# File 'app/models/contact_point.rb', line 329

def phone_number
  PhoneNumber.parse(detail)
end

#phone_number?Boolean

Returns:

  • (Boolean)


280
281
282
# File 'app/models/contact_point.rb', line 280

def phone_number?
  CAN_DIAL.include?(category)
end

#phone_support_case_participantsActiveRecord::Relation<SupportCaseParticipant>

Returns:

See Also:



95
# File 'app/models/contact_point.rb', line 95

has_many :phone_support_case_participants, class_name: 'SupportCaseParticipant', dependent: :nullify, foreign_key: :phone_id, inverse_of: :phone

#pinterest?Boolean

Returns:

  • (Boolean)


268
269
270
# File 'app/models/contact_point.rb', line 268

def pinterest?
  category == ContactPoint::PINTEREST
end

#preferred?Boolean

Returns:

  • (Boolean)


316
317
318
# File 'app/models/contact_point.rb', line 316

def preferred?
  position == 1
end

#quotesActiveRecord::Relation<Quote>

rubocop:disable Rails/HasAndBelongsToMany -- join tables pre-date has_many :through; migrate when touched

Returns:

  • (ActiveRecord::Relation<Quote>)

See Also:



100
# File 'app/models/contact_point.rb', line 100

has_and_belongs_to_many :quotes, inverse_of: :contact_points

#room_configurationsActiveRecord::Relation<RoomConfiguration>

Returns:

  • (ActiveRecord::Relation<RoomConfiguration>)

See Also:



88
# File 'app/models/contact_point.rb', line 88

has_many :room_configurations, dependent: :nullify

#set_default_sms_statusObject



407
408
409
410
411
412
413
# File 'app/models/contact_point.rb', line 407

def set_default_sms_status
  if phone_number?
    self.sms_status = :sms_enabled if category == CELL
  else
    self.sms_status = :sms_none
  end
end

#subscribersActiveRecord::Relation<Subscriber>

rubocop:disable Rails/InverseOf, Rails/HasManyOrHasOneDependent -- string FK on detail; no cascade appropriate

Returns:

See Also:



92
# File 'app/models/contact_point.rb', line 92

has_many :subscribers, foreign_key: :email_address, primary_key: :detail

#text_blocked?Boolean

Returns:

  • (Boolean)


304
305
306
# File 'app/models/contact_point.rb', line 304

def text_blocked?
  phone_number? && sms_enabled? && do_not_call&.do_not_text&.to_b
end

#time_at_locationObject



238
239
240
241
242
# File 'app/models/contact_point.rb', line 238

def time_at_location
  return unless (tz = timezone)

  tz.utc_to_local(Time.current).strftime('%I:%M %P')
end

#timezoneObject



233
234
235
236
# File 'app/models/contact_point.rb', line 233

def timezone
  tzn = timezone_name || area_code_object&.timezone_name
  Timezone[tzn.to_s] # returns nil for invalid timezone strings
end

#to_sObject



357
358
359
# File 'app/models/contact_point.rb', line 357

def to_s
  "#{category} : #{formatted_dial_string}"
end

#transmittable?Boolean

Returns:

  • (Boolean)


276
277
278
# File 'app/models/contact_point.rb', line 276

def transmittable?
  CAN_TRANSMIT.include?(category)
end

#twitter?Boolean

Returns:

  • (Boolean)


264
265
266
# File 'app/models/contact_point.rb', line 264

def twitter?
  category == ContactPoint::TWITTER
end

#website?Boolean

Returns:

  • (Boolean)


256
257
258
# File 'app/models/contact_point.rb', line 256

def website?
  [ContactPoint::WEBSITE, ContactPoint::FACEBOOK, ContactPoint::PINTEREST, ContactPoint::LINKEDIN].include?(category)
end

#website_url_formatObject



399
400
401
402
403
404
405
# File 'app/models/contact_point.rb', line 399

def website_url_format
  return if detail.blank?

  errors.add(:detail, INVALID_URL_MESSAGE) unless valid_website_uri?
rescue Addressable::URI::InvalidURIError
  errors.add(:detail, INVALID_URL_MESSAGE)
end