Module: ContextualConfig::Concern::Lookupable::ClassMethods

Defined in:
lib/contextual_config/concern/lookupable.rb

Overview

--- Class Methods ---

Instance Method Summary collapse

Instance Method Details

#find_all_applicable_configs(context:) ⇒ Array<ActiveRecord::Base>

Finds all configurations that apply to a given context, without filtering by a specific key. Useful if multiple configurations of the same type (but different keys or scopes) can apply simultaneously, or if you want to see all potentially relevant configurations.

Parameters:

  • context (Hash)

    A hash representing the current context.

Returns:

  • (Array<ActiveRecord::Base>)

    An array of all matching configuration instances, ordered by priority.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/contextual_config/concern/lookupable.rb', line 110

def find_all_applicable_configs(context:)
  unless respond_to?(:active) && respond_to?(:order_by_priority)
    raise(NoMethodError, "#{name} must include ContextualConfig::Concern::Configurable to use Lookupable.")
  end

  log_lookup_attempt('all_configs', context, 'all_configs_lookup')

  # Fetch all active configurations of this type, ordered by priority.
  all_candidates = lookup_all_candidates(context: context)

  # Delegate to the ContextualMatcher service.
  ContextualConfig::Services::ContextualMatcher.find_all_matches(
    candidates: all_candidates,
    context: context
  )
end

#find_applicable_config(key:, context:, fetch_all_of_key: false) ⇒ ActiveRecord::Base?

Finds the single most applicable configuration based on a key and context.

Parameters:

  • key (String, Symbol)

    The specific key of the configuration to look for.

  • context (Hash)

    A hash representing the current context (e.g., { employee_id: 'x', current_date: Date.today }).

  • fetch_all_of_key (Boolean) (defaults to: false)

    If true, fetches all configs with the given key before matching. If false (default), assumes key is unique within the relevant scope (e.g., STI type) and fetches only those.

Returns:

  • (ActiveRecord::Base, nil)

    The instance of the best matching configuration, or nil if no match.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/contextual_config/concern/lookupable.rb', line 21

def find_applicable_config(key:, context:, fetch_all_of_key: false) # rubocop:disable Lint/UnusedMethodArgument
  # Check result cache first if enabled
  if cache_results?(key: key, context: context)
    cache_key = generate_cache_key(key, context)
    begin
      cached_result = ContextualConfig.configuration.cache_store.read(cache_key)
      if cached_result
        log_lookup_attempt(key, context, 'result_cache_hit')
        return cached_result
      end
    rescue StandardError => e
      log_lookup_attempt(key, context, "result_cache_error: #{e.message}")
    end
  end

  # `self` here refers to the specific class this method is called on
  # (e.g., Finance::TaxConfig or a more generic YourTeam::Configuration).

  # Start with active configurations, ordered by priority (higher priority first).
  # The `Configurable` concern is expected to provide `active` and `order_by_priority` scopes.
  unless respond_to?(:active) && respond_to?(:order_by_priority)
    raise(NoMethodError, "#{name} must include ContextualConfig::Concern::Configurable to use Lookupable.")
  end

  # Check candidates cache if enabled
  candidates = nil
  if cache_candidates?(key: key, context: context)
    candidates_cache_key = generate_candidates_cache_key(key, context)
    begin
      candidates = ContextualConfig.configuration.cache_store.read(candidates_cache_key)
      log_lookup_attempt(key, context, 'candidates_cache_hit') if candidates
    rescue StandardError => e
      log_lookup_attempt(key, context, "candidates_cache_error: #{e.message}")
    end
  end

  # Fetch candidates from database if not cached
  if candidates.nil?
    log_lookup_attempt(key, context, 'database_lookup')
    candidates = lookup_candidates(key: key, context: context)
    # Cache candidates if enabled
    if cache_candidates?(key: key, context: context)
      candidates_cache_key = generate_candidates_cache_key(key, context)
      begin
        ContextualConfig.configuration.cache_store.write(
          candidates_cache_key,
          candidates.to_a, # Convert to array to avoid caching AR relations
          expires_in: ContextualConfig.configuration.cache_ttl
        )
        log_lookup_attempt(key, context, 'candidates_cache_stored')
      rescue StandardError => e
        log_lookup_attempt(key, context, "candidates_cache_write_error: #{e.message}")
      end
    end
  end

  # Delegate to the ContextualMatcher service to find the best match from the candidates.
  # The service will handle the logic of iterating through candidates, evaluating scoping_rules,
  # calculating specificity, and applying priority rules.
  result = ContextualConfig::Services::ContextualMatcher.find_best_match(
    candidates: candidates,
    context: context
  )

  # Cache the result if caching is enabled
  if cache_results?(key: key, context: context) && result
    cache_key = generate_cache_key(key, context)
    begin
      ContextualConfig.configuration.cache_store.write(
        cache_key,
        result,
        expires_in: ContextualConfig.configuration.cache_ttl
      )
      log_lookup_attempt(key, context, 'result_cache_stored')
    rescue StandardError => e
      log_lookup_attempt(key, context, "result_cache_write_error: #{e.message}")
    end
  end

  result
  # Return the full record, the caller can decide to use .config_data or other attributes
end