Module: Familia::Features::Relationships::Participation::ModelClassMethods
- Defined in:
- lib/familia/features/relationships/participation.rb
Overview
Class methods for defining participation relationships.
These methods are available on any class that includes the Participation module, allowing definition of both instance-level and class-level participation relationships.
Instance Method Summary collapse
-
#class_participates_in(collection_name, score: nil, type: :sorted_set, generate_participant_methods: true, through: nil) ⇒ Object
Define a class-level participation collection where all instances automatically participate.
-
#participates_in(target, collection_name, score: nil, type: :sorted_set, generate_participant_methods: true, as: nil, through: nil, method_prefix: nil) ⇒ Object
Define an instance-level participation relationship between two classes.
-
#participation_relationships ⇒ Array<ParticipationRelationship>
Get all participation relationships defined for this class.
Instance Method Details
#class_participates_in(collection_name, score: nil, type: :sorted_set, generate_participant_methods: true, through: nil) ⇒ Object
Define a class-level participation collection where all instances automatically participate.
Class-level participation creates a global collection containing all instances of the class, with automatic management of membership based on object lifecycle events. This is useful for maintaining global indexes, leaderboards, or categorical groupings.
The collection is created at the class level (e.g., User.all_users) rather than on individual instances, providing a centralized view of all objects matching the criteria.
=== Generated Methods
==== On the Class (Target Methods)
- +ClassName.collection_name+ - Access the collection DataType
- +ClassName.add_to_collection_name(instance)+ - Add instance to collection
- +ClassName.remove_from_collection_name(instance)+ - Remove instance from collection
==== On Instances (Participant Methods, if generate_participant_methods)
- +instance.in_class_collection_name?+ - Check membership in class collection
- +instance.add_to_class_collection_name+ - Add self to class collection
- +instance.remove_from_class_collection_name+ - Remove self from class collection
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/familia/features/relationships/participation.rb', line 165 def class_participates_in(collection_name, score: nil, type: :sorted_set, generate_participant_methods: true, through: nil) # Store metadata for this participation relationship participation_relationships << ParticipationRelationship.new( _original_target: self, # For class-level, original and resolved are the same target_class: self, # The class itself collection_name: collection_name, score: score, type: type, generate_participant_methods: generate_participant_methods, through: through, method_prefix: nil, # Not applicable for class-level participation ) # STEP 1: Add collection management methods to the class itself # e.g., User.all_users, User.add_to_all_users(user) TargetMethods::Builder.build_class_level(self, collection_name, type) # STEP 2: Add participation methods to instances (if generate_participant_methods) # e.g., user.in_class_all_users?, user.add_to_class_all_users return unless generate_participant_methods # Pass the string 'class' as target to distinguish class-level from instance-level # This prevents generating reverse collection methods (user can't have "all_users") # See ParticipantMethods::Builder.build for handling of this special case ParticipantMethods::Builder.build(self, 'class', collection_name, type, nil, through, nil) end |
#participates_in(target, collection_name, score: nil, type: :sorted_set, generate_participant_methods: true, as: nil, through: nil, method_prefix: nil) ⇒ Object
Define an instance-level participation relationship between two classes.
This method creates a bidirectional relationship where instances of the calling class (participants) can join collections owned by instances of the target class. This enables flexible multi-membership scenarios where objects can belong to multiple collections simultaneously with different scoring and management strategies.
The relationship automatically handles reverse index tracking, allowing efficient lookup of all collections a participant belongs to via the +current_participations+ method.
=== Generated Methods
==== On Target Class (Collection Owner)
- +target.collection_name+ - Access the collection DataType
- +target.add_participant_class_name(participant)+ - Add participant to collection
- +target.remove_participant_class_name(participant)+ - Remove participant from collection
- +target.add_participant_class_names([participants])+ - Bulk add multiple participants
==== On Participant Class (if generate_participant_methods)
- +participant.in_target_collection_name?(target)+ - Check membership in target's collection
- +participant.add_to_target_collection_name(target)+ - Add self to target's collection
- +participant.remove_from_target_collection_name(target)+ - Remove self from target's collection
=== Reverse Index Tracking
Automatically creates a +:participations+ set field on the participant class to track all collections the instance belongs to. This enables efficient membership queries and cleanup operations without scanning all possible collections.
- +Symbol+: Field name or method name (e.g., +:priority+, +:created_at+)
- +Proc+: Dynamic calculation executed in participant instance context
- +Numeric+: Static score applied to all participants
- +nil+: Use +current_score+ method as fallback
- +:remove+: Remove from all collections on destruction (default)
- +:ignore+: Leave in collections when destroyed
- Must use +feature :object_identifier+
- Gets auto-created when adding to collection (via +through_attrs:+ param)
- Gets auto-destroyed when removing from collection
- Uses deterministic keys: +target:id:participant:id:through+
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/familia/features/relationships/participation.rb', line 298 def participates_in(target, collection_name, score: nil, type: :sorted_set, generate_participant_methods: true, as: nil, through: nil, method_prefix: nil) # Normalize the target class parameter target_class = Familia.resolve_class(target) # Raise helpful error if target class can't be resolved if target_class.nil? raise ArgumentError, <<~ERROR Cannot resolve target class: #{target.inspect} The target class '#{target}' could not be found in Familia.members. This usually means: 1. The target class hasn't been loaded/required yet (load order issue) 2. The target class name is misspelled 3. The target class doesn't inherit from Familia::Horreum Current registered classes: #{Familia.members.filter_map(&:name).sort.join(', ')} Solution: Ensure #{target} is defined and loaded before #{self.name} ERROR end # Validate through class if provided if through through_class = Familia.resolve_class(through) raise ArgumentError, "Cannot resolve through class: #{through.inspect}" unless through_class unless through_class.respond_to?(:features_enabled) && through_class.features_enabled.include?(:object_identifier) raise ArgumentError, "Through model #{through_class} must use `feature :object_identifier`" end end # Store metadata for this participation relationship participation_relationships << ParticipationRelationship.new( _original_target: target, # Original value as passed (Symbol/String/Class) target_class: target_class, # Resolved Class object collection_name: collection_name, score: score, type: type, generate_participant_methods: generate_participant_methods, through: through, method_prefix: method_prefix, ) # STEP 0: Add participations tracking field to PARTICIPANT class (Domain) # This creates the proper key: "domain:123:participations" set :participations unless method_defined?(:participations) # STEP 1: Add collection management methods to TARGET class (Employee) # Employee gets: domains, add_domain, remove_domain, etc. TargetMethods::Builder.build(target_class, collection_name, type, through) # STEP 2: Add participation methods to PARTICIPANT class (Domain) - only if # generate_participant_methods. e.g. in_employee_domains?, add_to_employee_domains, etc. if generate_participant_methods # `as` parameter allows custom naming for reverse collections # If not provided, we'll let the builder use the pluralized target class name ParticipantMethods::Builder.build(self, target_class, collection_name, type, as, through, method_prefix) end end |
#participation_relationships ⇒ Array<ParticipationRelationship>
Get all participation relationships defined for this class.
Returns an array of ParticipationRelationship objects containing metadata about each participation relationship, including target class, collection name, scoring strategy, and configuration options.
368 369 370 |
# File 'lib/familia/features/relationships/participation.rb', line 368 def participation_relationships @participation_relationships ||= [] end |