Module: Familia::Features::Relationships::InstanceMethods

Defined in:
lib/familia/features/relationships.rb

Instance Method Summary collapse

Instance Method Details

#cleanup_all_relationships!Object

Comprehensive cleanup - remove from all relationships



321
322
323
324
325
326
327
328
329
330
# File 'lib/familia/features/relationships.rb', line 321

def cleanup_all_relationships!
  # Remove from tracking collections
  remove_from_all_tracking_collections if respond_to?(:remove_from_all_tracking_collections)

  # Remove from membership collections
  remove_from_all_memberships if respond_to?(:remove_from_all_memberships)

  # Remove from indexes
  remove_from_all_indexes if respond_to?(:remove_from_all_indexes)
end

#cleanup_previewObject

Dry run for relationship cleanup (preview what would be affected)



333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/familia/features/relationships.rb', line 333

def cleanup_preview
  preview = {
    tracking_collections: [],
    membership_collections: [],
    index_entries: []
  }

  if respond_to?(:cascade_dry_run)
    cascade_preview = cascade_dry_run
    preview.merge!(cascade_preview)
  end

  preview
end

#cleanup_temp_keys(pattern = 'temp:*', batch_size = 100) ⇒ Object

Instance method wrapper for cleanup_temp_keys



409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/familia/features/relationships.rb', line 409

def cleanup_temp_keys(pattern = 'temp:*', batch_size = 100)
  cursor = 0

  loop do
    cursor, keys = redis.scan(cursor, match: pattern, count: batch_size)

    if keys.any?
      # Check TTL and remove keys that should have expired
      keys.each_slice(batch_size) do |key_batch|
        redis.pipelined do |pipeline|
          key_batch.each do |key|
            ttl = redis.ttl(key)
            pipeline.del(key) if ttl == -1 # Key exists but has no TTL
          end
        end
      end
    end

    break if cursor.zero?
  end
end

#create_temp_key(base_name, ttl = 300) ⇒ Object

Instance method wrapper for create_temp_key



397
398
399
400
401
402
403
404
405
406
# File 'lib/familia/features/relationships.rb', line 397

def create_temp_key(base_name, ttl = 300)
  timestamp = Time.now.to_i
  random_suffix = SecureRandom.hex(3)
  temp_key = "temp:#{base_name}:#{timestamp}:#{random_suffix}"

  # Set immediate expiry to ensure cleanup even if operation fails
  redis.expire(temp_key, ttl)

  temp_key
end

#destroy!Object

Override destroy to handle cascade operations



290
291
292
293
294
295
# File 'lib/familia/features/relationships.rb', line 290

def destroy!
  # Execute cascade operations before destroying the object
  execute_cascade_operations if respond_to?(:execute_cascade_operations)

  super
end

#identifierObject

Get the identifier value for this instance Uses the existing Horreum identifier infrastructure



251
252
253
254
# File 'lib/familia/features/relationships.rb', line 251

def identifier
  id_field = self.class.identifier_field
  send(id_field) if respond_to?(id_field)
end

#identifier=(value) ⇒ Object

Set the identifier value for this instance



257
258
259
260
# File 'lib/familia/features/relationships.rb', line 257

def identifier=(value)
  id_field = self.class.identifier_field
  send("#{id_field}=", value) if respond_to?("#{id_field}=")
end

#initialize_relationshipsObject

Initialize relationships (called after object creation)



263
264
265
# File 'lib/familia/features/relationships.rb', line 263

def initialize_relationships
  # This can be overridden by subclasses to set up initial relationships
end

#redisObject

Direct Redis access for instance methods



392
393
394
# File 'lib/familia/features/relationships.rb', line 392

def redis
  self.class.dbclient
end

#refresh_relationships!Object

Refresh relationship data from Redis (useful after external changes)



369
370
371
372
373
374
375
376
377
378
# File 'lib/familia/features/relationships.rb', line 369

def refresh_relationships!
  # Clear any cached relationship data
  @relationship_status = nil
  @tracking_memberships = nil
  @membership_collections = nil
  @index_memberships = nil

  # Reload fresh data
  relationship_status
end

#relationship_snapshotObject

Create a snapshot of current relationship state (for debugging)



381
382
383
384
385
386
387
388
389
# File 'lib/familia/features/relationships.rb', line 381

def relationship_snapshot
  {
    timestamp: Time.now,
    identifier: identifier,
    class: self.class.name,
    status: relationship_status,
    redis_keys: find_related_redis_keys
  }
end

#relationship_statusObject

Get comprehensive relationship status for this object



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/familia/features/relationships.rb', line 298

def relationship_status
  status = {
    identifier: identifier,
    tracking_memberships: [],
    membership_collections: [],
    index_memberships: []
  }

  # Get tracking memberships
  if respond_to?(:tracking_collections_membership)
    status[:tracking_memberships] = tracking_collections_membership
  end

  # Get membership collections
  status[:membership_collections] = membership_collections if respond_to?(:membership_collections)

  # Get index memberships
  status[:index_memberships] = indexing_memberships if respond_to?(:indexing_memberships)

  status
end

#save(update_expiration: true) ⇒ Object

Override save to update relationships automatically



268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/familia/features/relationships.rb', line 268

def save(update_expiration: true)
  result = super

  if result
    # Automatically update all indexes when object is saved
    if respond_to?(:update_all_indexes)
      update_all_indexes
    end

    # Auto-add to class-level tracking collections
    if respond_to?(:add_to_class_tracking_collections)
      add_to_class_tracking_collections
    end

    # NOTE: Relationship-specific membership and tracking updates are done explicitly
    # since we need to know which specific collections this object should be in
  end

  result
end

#validate_relationships!Object

Validate that this object’s relationships are consistent

Raises:



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/familia/features/relationships.rb', line 349

def validate_relationships!
  errors = []

  # Validate identifier exists
  errors << 'Object identifier is nil' unless identifier

  # Validate tracking memberships
  if respond_to?(:tracking_collections_membership)
    tracking_collections_membership.each do |membership|
      score = membership[:score]
      errors << "Invalid score in tracking membership: #{membership}" if score && !score.is_a?(Numeric)
    end
  end

  raise RelationshipError, "Relationship validation failed for #{self}: #{errors.join('; ')}" if errors.any?

  true
end