Module: Webhookdb::Postgres::ModelUtilities::InstanceMethods

Defined in:
lib/webhookdb/postgres/model_utilities.rb

Instance Method Summary collapse

Instance Method Details

#after_createObject

Sequel hook – send an asynchronous event after the model is saved.



333
334
335
336
# File 'lib/webhookdb/postgres/model_utilities.rb', line 333

def after_create
  super
  self.publish_deferred("created", self.id, prep_amigo_payload(self.values))
end

#after_destroyObject

Sequel hook – send an event after a transaction that destroys the object is committed.



345
346
347
348
# File 'lib/webhookdb/postgres/model_utilities.rb', line 345

def after_destroy
  super
  self.publish_deferred("destroyed", self.id, prep_amigo_payload(self.values))
end

#after_updateObject

Sequel hook – send an asynchronous event after the save is committed.



339
340
341
342
# File 'lib/webhookdb/postgres/model_utilities.rb', line 339

def after_update
  super
  self.publish_deferred("updated", self.id, prep_amigo_payload(self.previous_changes))
end

#error_messagesObject

Return the objects validation errors as full messages joined with commas.



252
253
254
# File 'lib/webhookdb/postgres/model_utilities.rb', line 252

def error_messages
  return self.errors.full_messages.join(", ")
end

#event_prefixObject

Return the string used as a topic for events sent from the receiving object.



257
258
259
260
# File 'lib/webhookdb/postgres/model_utilities.rb', line 257

def event_prefix
  prefix = self.class.name or return # No events for anonymous classes
  return prefix.gsub("::", ".").downcase
end

#inspectObject

Return a human-readable representation of the object as a String suitable for debugging.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/webhookdb/postgres/model_utilities.rb', line 202

def inspect
  values = self.values.reject do |k, v|
    v.blank? || k.to_s.end_with?("_currency")
  end
  begin
    encrypted = self.class.send(:column_encryption_metadata).to_set { |(col, _)| col.to_s }
  rescue NoMethodError
    encrypted = Set.new
  end
  values = values.map do |(k, v)|
    k = k.to_s
    v = if v.is_a?(Time)
          self.inspect_time(v)
    elsif v.respond_to?(:db_type) && v.db_type.to_s == "tstzrange"
      "%s%s...%s%s" % [
        v.exclude_begin? ? "(" : "[",
        v.begin ? self.inspect_time(v.begin) : "nil",
        v.end ? self.inspect_time(v.end) : "nil",
        v.exclude_end? ? ")" : "]",
      ]
    elsif k.end_with?("_cents")
      accessor = k.match(/^([a-z_]+)_cents/)[1]
      k = accessor
      self.send(accessor).format
    elsif encrypted.include?(k)
      # Render encrypted fields as xyz...abc, or if a URL, hide the user/password.
      unenc = self.send(k)
      if unenc.length < 10
        "\"...\""
      elsif unenc.include?("://")
        uri = URI(unenc)
        uri.user = "*"
        uri.password = "*"
        uri.to_s.inspect
      else
        "\"#{unenc[..2]}...#{unenc[-3..]}\""
      end
    else
      v.inspect
    end
    "#{k}: #{v}"
  end
  return "#<%p %s>" % [self.class, values.join(", ")]
end

#inspect_time(t) ⇒ Object



247
248
249
# File 'lib/webhookdb/postgres/model_utilities.rb', line 247

def inspect_time(t)
  return t.in_time_zone(Time.zone).strftime("%Y-%m-%d %H:%M:%S")
end

#lock?Boolean

Run a SELECT FOR UPDATE SKIP LOCKED. If the model row is already locked, return false. Otherwise, acquire the lock and return true.

If the lock is acquired, callers may want to refresh the receiver to make sure it has the newest values.

Returns:

  • (Boolean)

Raises:



300
301
302
303
304
305
306
307
308
309
310
# File 'lib/webhookdb/postgres/model_utilities.rb', line 300

def lock?
  raise Webhookdb::LockFailed, "must be in a transaction" unless self.db.in_transaction?
  pk = self[self.class.primary_key]
  got_lock = self.class.dataset.
    select(1).
    where(self.class.primary_key => pk).
    for_update.
    skip_locked.
    first
  return !got_lock.nil?
end

#publish_deferred(type, *payload) ⇒ Object

Publish an event in the current db’s/transaction’s after_commit hook.



271
272
273
274
275
# File 'lib/webhookdb/postgres/model_utilities.rb', line 271

def publish_deferred(type, *payload)
  Webhookdb::Postgres.defer_after_commit(self.db) do
    self.publish_immediate(type, *payload)
  end
end

#publish_immediate(type, *payload) ⇒ Object

Publish an event from the receiving object of the specified type and with the given payload. This does not wait for the transaction to complete, so subscribers may not be able to observe any model changes in the database. You probably want to use published_deferred.



265
266
267
268
# File 'lib/webhookdb/postgres/model_utilities.rb', line 265

def publish_immediate(type, *payload)
  prefix = self.event_prefix or return
  Amigo.publish(prefix + "." + type.to_s, *payload)
end

#resource_lock!Object

Take an exclusive lock on the receiver, ensuring nothing else has updated the object in the meantime. If the updated_at changed from what’s on the receiver, to after it acquired the lock, raise LockFailed. Save changes and touch updated_at after calling the given block.



280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/webhookdb/postgres/model_utilities.rb', line 280

def resource_lock!
  self.db.transaction do
    old_updated = self.round_time(self.updated_at)
    self.lock!
    new_updated = self.round_time(self.updated_at)
    raise Webhookdb::LockFailed if old_updated != new_updated
    result = yield(self)
    self.updated_at = Time.now
    self.save_changes
    return result
  end
end