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



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

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)



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

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



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

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



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

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



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

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



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

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



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

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)



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

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

#redisObject

Direct Redis access for instance methods



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

def redis
  self.class.dbclient
end

#refresh_relationships!Object

Refresh relationship data from Redis (useful after external changes)



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

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)



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

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



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

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



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

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:



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

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