Module: ApiKeys::Models::Concerns::HasApiKeys::ClassMethods

Defined in:
lib/api_keys/models/concerns/has_api_keys.rb

Overview

Module containing class methods to be extended onto ActiveRecord::Base

Instance Method Summary collapse

Instance Method Details

#has_api_keys(**options, &block) ⇒ Object

Defines the association and allows configuration for the specific owner model.

Example:

class User < ApplicationRecord
# Using keyword arguments:
has_api_keys max_keys: 5, require_name: true

# Or using a block:
has_api_keys do
  max_keys 10
  require_name false
  default_scopes %w[read write]
end
end


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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/api_keys/models/concerns/has_api_keys.rb', line 73

def has_api_keys(**options, &block)
  unknown_settings = options.keys - SUPPORTED_SETTINGS
  if unknown_settings.any?
    raise ArgumentError, "Unknown has_api_keys setting(s): #{unknown_settings.join(', ')}"
  end

  # Initialize settings for this specific class, merging defaults and options
  current_settings = {
    # Default to global config values first
    max_keys: ApiKeys.configuration&.default_max_keys_per_owner,
    require_name: ApiKeys.configuration&.require_key_name,
    default_scopes: ApiKeys.configuration&.default_scopes || []
  }.merge(options) # Merge keyword arguments first

  # Apply DSL block if provided, allowing overrides
  if block_given?
    dsl = DslProvider.new(current_settings)
    dsl.instance_eval(&block)
  end

  validated_settings = HasApiKeys.validate_and_freeze_settings(current_settings)

  # Include the concern's instance methods into the calling class (e.g., User)
  # Ensures any instance-level helpers in HasApiKeys are available on the owner.
  include ApiKeys::Models::Concerns::HasApiKeys unless included_modules.include?(ApiKeys::Models::Concerns::HasApiKeys)

  # Define the core association on the specific class calling this method
  has_many :api_keys,
           class_name: "ApiKeys::ApiKey",
           as: :owner,
           # An owner deletion is an administrative lifecycle event and
           # must remove every credential, including key types that users
           # cannot revoke individually through the normal API.
           dependent: :delete_all

  # Define class_attribute for settings if not already defined.
  # This ensures inheritance works correctly (subclasses get their own copy).
  unless respond_to?(:api_keys_settings)
    class_attribute :api_keys_settings, instance_writer: false, default: {}
  end

  # Assign an immutable copy so later mutations cannot silently change
  # quota, naming, or permission policy at runtime.
  self.api_keys_settings = validated_settings

  # TODO: Add validation hook to check key limit on create?
  # validates_with ApiKeys::Validators::MaxKeysValidator, on: :create, if: -> { api_keys_settings[:max_keys].present? }
end