Module: Familia::Features::Relationships::Participation::ModelInstanceMethods
- Defined in:
- lib/familia/features/relationships/participation.rb
Overview
Instance methods available on objects that participate in collections.
These methods provide the core functionality for participation management, including score calculation, membership tracking, and participation queries.
Instance Method Summary collapse
-
#calculate_participation_score(target_class, collection_name) ⇒ Float
Calculate the appropriate score for a participation relationship based on configured scoring strategy.
-
#current_participations ⇒ Array<Hash>
Get comprehensive information about all collections this object participates in.
-
#track_participation_in(collection_key) ⇒ Object
Add participation tracking to the reverse index.
-
#untrack_participation_in(collection_key) ⇒ Object
Remove participation tracking from the reverse index.
Instance Method Details
#calculate_participation_score(target_class, collection_name) ⇒ Float
Calculate the appropriate score for a participation relationship based on configured scoring strategy.
This method serves as the single source of truth for participation scoring across the entire relationship lifecycle. It supports multiple scoring strategies and provides robust fallback behavior for edge cases and error conditions.
The calculated score determines the object's position within sorted collections and can be dynamically recalculated as object state changes, enabling responsive collection ordering based on real-time business logic.
=== Scoring Strategies
[Symbol] Field name or method name - calls +send(symbol)+ on the instance
- +:priority_level+ - Uses value of priority_level field
- +:created_at+ - Uses timestamp for chronological ordering
- +:calculate_importance+ - Calls custom method for complex logic
[Proc] Dynamic calculation executed in instance context using +instance_exec+
- +-> { skill_level * experience_years }+ - Combines multiple fields
- +-> { active? ? 100 : 0 }+ - Conditional scoring based on state
- +-> { Rails.cache.fetch("score:#id") { expensive_calculation } }+ - Cached computations
[Numeric] Static score applied uniformly to all instances
- +50.0+ - All instances get same floating-point score
- +100+ - All instances get same integer score (converted to float)
[nil] Uses +current_score+ method as fallback if available
=== Performance Considerations
- Score calculations are performed on-demand during collection operations
- Proc-based calculations should be efficient as they may be called frequently
- Consider caching expensive calculations within the Proc itself
- Static numeric scores have no performance overhead
=== Thread Safety
Score calculations should be idempotent and thread-safe since they may be called concurrently during collection updates. Avoid modifying instance state within scoring Procs.
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
# File 'lib/familia/features/relationships/participation.rb', line 404 def calculate_participation_score(target_class, collection_name) # Find the participation configuration with robust type comparison participation_config = self.class.participation_relationships.find do |details| # Normalize both sides for comparison to handle Class, Symbol, and String types config_target = details.target_class config_target = config_target.name if config_target.is_a?(Class) config_target = config_target.to_s comparison_target = target_class comparison_target = comparison_target.name if comparison_target.is_a?(Class) comparison_target = comparison_target.to_s config_target == comparison_target && details.collection_name == collection_name end return current_score unless participation_config score_calculator = participation_config.score # Get the raw result based on calculator type result = case score_calculator when Symbol # Field name or method name respond_to?(score_calculator) ? send(score_calculator) : nil when Proc # Execute proc in context of this instance instance_exec(&score_calculator) when Numeric # Static numeric value return score_calculator.to_f else # Unrecognized type return current_score end # Convert result to appropriate score with unified logic convert_to_score(result) end |
#current_participations ⇒ Array<Hash>
Get comprehensive information about all collections this object participates in.
This method leverages the reverse index to efficiently retrieve membership details across all collections without requiring expensive scans. For each membership, it provides collection metadata, membership details, and type-specific information like scores or positions.
The method handles missing target objects gracefully and validates membership using the actual DataType collections to ensure accuracy.
=== Return Format
Returns an array of hashes, each containing:
- +:target_class+ - Name of the class owning the collection
- +:target_id+ - Identifier of the specific target instance
- +:collection_name+ - Name of the collection within the target
- +:type+ - Collection type (:sorted_set, :set, :list)
Additional fields based on collection type:
- +:score+ - Current score (sorted_set only)
- +:decoded_score+ - Human-readable score if decode_score method exists
- +:position+ - Zero-based position in the list (list only)
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
# File 'lib/familia/features/relationships/participation.rb', line 545 def current_participations return [] unless self.class.respond_to?(:participation_relationships) # Use the reverse index as the single source of truth collection_keys = participations.members return [] if collection_keys.empty? memberships = [] # Check membership in each tracked collection using DataType methods collection_keys.each do |collection_key| # Parse the collection key to extract target info # Expected format: "targetclass:targetid:collectionname" key_parts = collection_key.split(':') next unless key_parts.length >= 3 target_class_config = key_parts[0] target_id = key_parts[1] collection_name_from_key = key_parts[2] # Find the matching participation configuration # Note: target_class_config from key is snake_case config = self.class.participation_relationships.find do |cfg| cfg.target_class_config_name == target_class_config && cfg.collection_name.to_s == collection_name_from_key end next unless config # Find the target instance and check membership using Horreum DataTypes begin target_class = Familia.resolve_class(config.target_class) target_instance = target_class.find_by_id(target_id) next unless target_instance # Use Horreum's DataType accessor to get the collection collection = target_instance.send(config.collection_name) # Check membership using DataType methods membership_data = { target_class: config.target_class.familia_name, target_id: target_id, collection_name: config.collection_name, type: config.type, } case config.type when :sorted_set score = collection.score(identifier) next unless score membership_data[:score] = score membership_data[:decoded_score] = decode_score(score) if respond_to?(:decode_score) when :set is_member = collection.member?(identifier) next unless is_member when :list position = collection.to_a.index(identifier) next unless position membership_data[:position] = position end memberships << membership_data rescue StandardError => e Familia.ld "[#{collection_key}] Error checking membership: #{e.}" next end end memberships end |
#track_participation_in(collection_key) ⇒ Object
Add participation tracking to the reverse index.
This method maintains the reverse index that tracks which collections this object participates in. The reverse index enables efficient lookup of all memberships via +current_participations+ without requiring expensive scans.
The collection key follows the pattern: +"targetclass:targetid:collectionname"+
457 458 459 460 |
# File 'lib/familia/features/relationships/participation.rb', line 457 def track_participation_in(collection_key) # Use Horreum's DataType field instead of manual key construction participations.add(collection_key) end |
#untrack_participation_in(collection_key) ⇒ Object
Remove participation tracking from the reverse index.
This method removes the collection key from the reverse index when the object is removed from a collection. This keeps the reverse index accurate and prevents stale references from appearing in +current_participations+ results.
474 475 476 477 |
# File 'lib/familia/features/relationships/participation.rb', line 474 def untrack_participation_in(collection_key) # Use Horreum's DataType field instead of manual key construction participations.remove(collection_key) end |