contextual_config 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CLAUDE.md +2 -2
- data/Gemfile +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -2
- data/Rakefile +2 -0
- data/contextual_config.gemspec +3 -1
- data/lib/contextual_config/concern/configurable.rb +9 -13
- data/lib/contextual_config/concern/lookupable.rb +10 -10
- data/lib/contextual_config/concern/schema_driven_validation.rb +5 -2
- data/lib/contextual_config/configuration.rb +2 -6
- data/lib/contextual_config/generators.rb +2 -0
- data/lib/contextual_config/module_registry.rb +1 -1
- data/lib/contextual_config/services/contextual_matcher.rb +2 -0
- data/lib/contextual_config/version.rb +3 -1
- data/lib/contextual_config.rb +3 -3
- data/lib/generators/contextual_config/configurable_table/configurable_table_generator.rb +2 -0
- data/lib/generators/contextual_config/configurable_table/templates/migration.rb.tt +6 -4
- metadata +1 -2
- data/lib/generators/contextual_config/configurable_table/USAGE +0 -33
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 7c7a19182d63d0f03712caf91b5b996bd99af38f3a6eed254d8497ff3c78af46
         | 
| 4 | 
            +
              data.tar.gz: a55ebca13917e3b7fe37f45a0d14a7279a860705ec3c1461eb0071cc1acf67c1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: dfb97ec675a0d72ce010f4e9f809e25cc01baff02dbafbfd83a17ae62eba7bd2a5cf9dfc39fe670075c09ef17f205297fc63c074ecac3fa7b422faa08811ba95
         | 
| 7 | 
            +
              data.tar.gz: 941b9502f8cbcaa055f57d668c87c622dcc0f3efd3d1ef929cf5dc2533a487820badd234557187080870d36a7c11460887c624944da9511091a8d769e85e0f7a
         | 
    
        data/CLAUDE.md
    CHANGED
    
    | @@ -18,7 +18,7 @@ ContextualConfig is a Ruby gem for context-aware configuration management built | |
| 18 18 |  | 
| 19 19 | 
             
            ### 1. Core Concerns (lib/contextual_config/concern/)
         | 
| 20 20 |  | 
| 21 | 
            -
            - **Configurable**: Provides validations, scopes, and basic configuration functionality. Expects database columns: `key`, `config_data` (jsonb), `scoping_rules` (jsonb), `priority` (integer), ` | 
| 21 | 
            +
            - **Configurable**: Provides validations, scopes, and basic configuration functionality. Expects database columns: `key`, `config_data` (jsonb), `scoping_rules` (jsonb), `priority` (integer), `deleted_at` (datetime)
         | 
| 22 22 | 
             
            - **Lookupable**: Handles configuration lookup logic with `find_applicable_config(key:, context:)` and `find_all_applicable_configs(context:)`  
         | 
| 23 23 | 
             
            - **SchemaDrivenValidation**: Optional JSON schema validation for `config_data` and `scoping_rules`
         | 
| 24 24 |  | 
| @@ -51,7 +51,7 @@ t.string :key, null: false | |
| 51 51 | 
             
            t.jsonb :config_data, null: false, default: {}
         | 
| 52 52 | 
             
            t.jsonb :scoping_rules, null: false, default: {}
         | 
| 53 53 | 
             
            t.integer :priority, null: false, default: 100
         | 
| 54 | 
            -
            t. | 
| 54 | 
            +
            t.datetime :deleted_at
         | 
| 55 55 | 
             
            t.text :description
         | 
| 56 56 | 
             
            t.string :type # For STI if needed
         | 
| 57 57 | 
             
            ```
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -111,7 +111,7 @@ Each configuration record contains: | |
| 111 111 | 
             
            - **config_data**: The actual configuration payload (JSONB)
         | 
| 112 112 | 
             
            - **scoping_rules**: Rules defining when this config applies (JSONB)
         | 
| 113 113 | 
             
            - **priority**: Lower numbers = higher priority (integer)
         | 
| 114 | 
            -
            - ** | 
| 114 | 
            +
            - **deleted_at**: Soft delete timestamp (datetime, null for active)
         | 
| 115 115 | 
             
            - **type**: For Single Table Inheritance (string, optional)
         | 
| 116 116 |  | 
| 117 117 | 
             
            ### Context Matching
         | 
| @@ -229,7 +229,7 @@ def change | |
| 229 229 | 
             
                t.jsonb :config_data, null: false, default: {}
         | 
| 230 230 | 
             
                t.jsonb :scoping_rules, null: false, default: {}
         | 
| 231 231 | 
             
                t.integer :priority, null: false, default: 100
         | 
| 232 | 
            -
                t. | 
| 232 | 
            +
                t.datetime :deleted_at
         | 
| 233 233 | 
             
                t.text :description
         | 
| 234 234 | 
             
                t.string :type
         | 
| 235 235 |  | 
    
        data/Rakefile
    CHANGED
    
    
    
        data/contextual_config.gemspec
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require_relative 'lib/contextual_config/version'
         | 
| 2 4 |  | 
| 3 5 | 
             
            Gem::Specification.new do |spec|
         | 
| @@ -10,7 +12,7 @@ Gem::Specification.new do |spec| | |
| 10 12 | 
             
              spec.description   = 'ContextualConfig provides a flexible framework for managing configurations that can be applied based on contextual rules, priorities, and scoping. Perfect for complex applications requiring dynamic configuration resolution.'
         | 
| 11 13 | 
             
              spec.homepage      = 'https://github.com/bazinga012/contextual_config'
         | 
| 12 14 | 
             
              spec.license       = 'MIT'
         | 
| 13 | 
            -
              spec.required_ruby_version = '>= 3.0.0' | 
| 15 | 
            +
              spec.required_ruby_version = '>= 3.0.0'
         | 
| 14 16 |  | 
| 15 17 | 
             
              spec.metadata['allowed_push_host'] = 'https://rubygems.org'
         | 
| 16 18 | 
             
              spec.metadata['homepage_uri'] = spec.homepage
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'active_support/concern'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module ContextualConfig
         | 
| @@ -13,7 +15,7 @@ module ContextualConfig | |
| 13 15 | 
             
                    # - config_data:jsonb
         | 
| 14 16 | 
             
                    # - scoping_rules:jsonb
         | 
| 15 17 | 
             
                    # - priority:integer (default should be a higher number for lower priority)
         | 
| 16 | 
            -
                    # -  | 
| 18 | 
            +
                    # - deleted_at:datetime (null for active records)
         | 
| 17 19 | 
             
                    # - type:string (if STI is to be used on the including model)
         | 
| 18 20 | 
             
                    # - created_at:datetime
         | 
| 19 21 | 
             
                    # - updated_at:datetime
         | 
| @@ -33,9 +35,6 @@ module ContextualConfig | |
| 33 35 | 
             
                    # It's often better to add this uniqueness validation directly in the consuming model
         | 
| 34 36 | 
             
                    # where the scope is clearer.
         | 
| 35 37 |  | 
| 36 | 
            -
                    # Ensures 'is_active' is either true or false.
         | 
| 37 | 
            -
                    validates :is_active, inclusion: { in: [true, false], message: 'is not included in the list' }
         | 
| 38 | 
            -
             | 
| 39 38 | 
             
                    # Ensures 'priority' is an integer.
         | 
| 40 39 | 
             
                    validates :priority, numericality: { only_integer: true, message: 'is not a number' }
         | 
| 41 40 |  | 
| @@ -43,7 +42,7 @@ module ContextualConfig | |
| 43 42 |  | 
| 44 43 | 
             
                    # Scope to retrieve only active configurations.
         | 
| 45 44 | 
             
                    # Usage: YourModel.active
         | 
| 46 | 
            -
                    scope :active, -> { where( | 
| 45 | 
            +
                    scope :active, -> { where(deleted_at: nil) }
         | 
| 47 46 |  | 
| 48 47 | 
             
                    # Scope to order configurations by priority.
         | 
| 49 48 | 
             
                    # Lower numbers indicate higher priority. Secondary sort by ID for deterministic ordering.
         | 
| @@ -51,9 +50,8 @@ module ContextualConfig | |
| 51 50 | 
             
                    scope :order_by_priority, -> { order(priority: :asc, id: :asc) } # 'asc' for priority means 1 is higher than 10
         | 
| 52 51 |  | 
| 53 52 | 
             
                    # --- Default Values ---
         | 
| 54 | 
            -
                    # It's generally recommended to set database-level defaults for ` | 
| 55 | 
            -
                    #  | 
| 56 | 
            -
                    # If not set at DB level, you can use `after_initialize` here, but DB defaults are safer.
         | 
| 53 | 
            +
                    # It's generally recommended to set database-level defaults for `priority`
         | 
| 54 | 
            +
                    # (e.g., a high number like 100 for low priority). `deleted_at` defaults to NULL for active records.
         | 
| 57 55 | 
             
                    after_initialize :set_default_values, if: :new_record?
         | 
| 58 56 | 
             
                  end
         | 
| 59 57 |  | 
| @@ -63,10 +61,8 @@ module ContextualConfig | |
| 63 61 |  | 
| 64 62 | 
             
                  def set_default_values
         | 
| 65 63 | 
             
                    # Only set defaults if the model is completely new (not loaded from DB and no explicit values set)
         | 
| 66 | 
            -
                     | 
| 67 | 
            -
             | 
| 68 | 
            -
                    end
         | 
| 69 | 
            -
                    
         | 
| 64 | 
            +
                    # deleted_at defaults to NULL (active), no need to set explicitly
         | 
| 65 | 
            +
             | 
| 70 66 | 
             
                    # Override database default priority with module-specific priority if available
         | 
| 71 67 | 
             
                    set_module_default_priority if should_use_module_default_priority?
         | 
| 72 68 | 
             
                  end
         | 
| @@ -100,7 +96,7 @@ module ContextualConfig | |
| 100 96 |  | 
| 101 97 | 
             
                  # Helper to quickly check if a configuration is currently active
         | 
| 102 98 | 
             
                  def active?
         | 
| 103 | 
            -
                     | 
| 99 | 
            +
                    deleted_at.nil?
         | 
| 104 100 | 
             
                  end
         | 
| 105 101 |  | 
| 106 102 | 
             
                  # Get the module configuration for this model if available
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'active_support/concern'
         | 
| 2 4 | 
             
            require 'digest'
         | 
| 3 5 |  | 
| @@ -36,8 +38,8 @@ module ContextualConfig | |
| 36 38 |  | 
| 37 39 | 
             
                      # Start with active configurations, ordered by priority (higher priority first).
         | 
| 38 40 | 
             
                      # The `Configurable` concern is expected to provide `active` and `order_by_priority` scopes.
         | 
| 39 | 
            -
                      unless  | 
| 40 | 
            -
                        raise(NoMethodError, "#{ | 
| 41 | 
            +
                      unless respond_to?(:active) && respond_to?(:order_by_priority)
         | 
| 42 | 
            +
                        raise(NoMethodError, "#{name} must include ContextualConfig::Concern::Configurable to use Lookupable.")
         | 
| 41 43 | 
             
                      end
         | 
| 42 44 |  | 
| 43 45 | 
             
                      # Check candidates cache if enabled
         | 
| @@ -46,9 +48,7 @@ module ContextualConfig | |
| 46 48 | 
             
                        candidates_cache_key = generate_candidates_cache_key(key, context)
         | 
| 47 49 | 
             
                        begin
         | 
| 48 50 | 
             
                          candidates = ContextualConfig.configuration.cache_store.read(candidates_cache_key)
         | 
| 49 | 
            -
                          if candidates
         | 
| 50 | 
            -
                            log_lookup_attempt(key, context, 'candidates_cache_hit')
         | 
| 51 | 
            -
                          end
         | 
| 51 | 
            +
                          log_lookup_attempt(key, context, 'candidates_cache_hit') if candidates
         | 
| 52 52 | 
             
                        rescue StandardError => e
         | 
| 53 53 | 
             
                          log_lookup_attempt(key, context, "candidates_cache_error: #{e.message}")
         | 
| 54 54 | 
             
                        end
         | 
| @@ -108,8 +108,8 @@ module ContextualConfig | |
| 108 108 | 
             
                    # @param context [Hash] A hash representing the current context.
         | 
| 109 109 | 
             
                    # @return [Array<ActiveRecord::Base>] An array of all matching configuration instances, ordered by priority.
         | 
| 110 110 | 
             
                    def find_all_applicable_configs(context:)
         | 
| 111 | 
            -
                      unless  | 
| 112 | 
            -
                        raise(NoMethodError, "#{ | 
| 111 | 
            +
                      unless respond_to?(:active) && respond_to?(:order_by_priority)
         | 
| 112 | 
            +
                        raise(NoMethodError, "#{name} must include ContextualConfig::Concern::Configurable to use Lookupable.")
         | 
| 113 113 | 
             
                      end
         | 
| 114 114 |  | 
| 115 115 | 
             
                      log_lookup_attempt('all_configs', context, 'all_configs_lookup')
         | 
| @@ -171,13 +171,13 @@ module ContextualConfig | |
| 171 171 | 
             
                    # Generate cache key for configuration lookup
         | 
| 172 172 | 
             
                    def generate_cache_key(key, context)
         | 
| 173 173 | 
             
                      context_hash = context.is_a?(Hash) ? context.sort.to_h : context
         | 
| 174 | 
            -
                      "contextual_config:#{ | 
| 174 | 
            +
                      "contextual_config:#{name}:#{key}:#{Digest::MD5.hexdigest(context_hash.to_s)}"
         | 
| 175 175 | 
             
                    end
         | 
| 176 176 |  | 
| 177 177 | 
             
                    # Generate cache key for candidates
         | 
| 178 178 | 
             
                    def generate_candidates_cache_key(key, context)
         | 
| 179 179 | 
             
                      context_hash = context.is_a?(Hash) ? context.sort.to_h : context
         | 
| 180 | 
            -
                      "contextual_config_candidates:#{ | 
| 180 | 
            +
                      "contextual_config_candidates:#{name}:#{key}:#{Digest::MD5.hexdigest(context_hash.to_s)}"
         | 
| 181 181 | 
             
                    end
         | 
| 182 182 |  | 
| 183 183 | 
             
                    # Log configuration lookup attempts
         | 
| @@ -186,7 +186,7 @@ module ContextualConfig | |
| 186 186 |  | 
| 187 187 | 
             
                      logger = ContextualConfig.configuration.effective_logger
         | 
| 188 188 | 
             
                      logger.info(
         | 
| 189 | 
            -
                        "ContextualConfig::Lookupable: #{operation} for #{ | 
| 189 | 
            +
                        "ContextualConfig::Lookupable: #{operation} for #{name} - Key: #{key}, Context: #{context}"
         | 
| 190 190 | 
             
                      )
         | 
| 191 191 | 
             
                    end
         | 
| 192 192 | 
             
                  end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'active_support/concern'
         | 
| 2 4 | 
             
            require 'json-schema'
         | 
| 3 5 |  | 
| @@ -42,7 +44,7 @@ module ContextualConfig | |
| 42 44 |  | 
| 43 45 | 
             
                    begin
         | 
| 44 46 | 
             
                      # Using fully_validate to get an array of all errors.
         | 
| 45 | 
            -
                      errors_found = JSON::Validator.fully_validate(schema,  | 
| 47 | 
            +
                      errors_found = JSON::Validator.fully_validate(schema, config_data, strict: false, insert_defaults: false)
         | 
| 46 48 | 
             
                      if errors_found.any?
         | 
| 47 49 | 
             
                        errors_found.each do |error_message|
         | 
| 48 50 | 
             
                          # Add a somewhat generic error. More specific parsing of error_message could provide better field-level errors.
         | 
| @@ -67,7 +69,8 @@ module ContextualConfig | |
| 67 69 | 
             
                    return unless schema # Skip if no schema defined
         | 
| 68 70 |  | 
| 69 71 | 
             
                    begin
         | 
| 70 | 
            -
                      errors_found = JSON::Validator.fully_validate(schema,  | 
| 72 | 
            +
                      errors_found = JSON::Validator.fully_validate(schema, scoping_rules, strict: false,
         | 
| 73 | 
            +
                                                                                           insert_defaults: false)
         | 
| 71 74 | 
             
                      if errors_found.any?
         | 
| 72 75 | 
             
                        errors_found.each do |error_message|
         | 
| 73 76 | 
             
                          errors.add(:scoping_rules, "is invalid - #{error_message}")
         | 
| @@ -55,13 +55,9 @@ module ContextualConfig | |
| 55 55 | 
             
                    raise(ConfigurationError, 'Cache is enabled but cache_store is not properly configured')
         | 
| 56 56 | 
             
                  end
         | 
| 57 57 |  | 
| 58 | 
            -
                  if cache_ttl <= 0
         | 
| 59 | 
            -
                    raise(ConfigurationError, 'cache_ttl must be a positive number')
         | 
| 60 | 
            -
                  end
         | 
| 58 | 
            +
                  raise(ConfigurationError, 'cache_ttl must be a positive number') if cache_ttl <= 0
         | 
| 61 59 |  | 
| 62 | 
            -
                  if default_priority.negative?
         | 
| 63 | 
            -
                    raise(ConfigurationError, 'default_priority must be non-negative')
         | 
| 64 | 
            -
                  end
         | 
| 60 | 
            +
                  raise(ConfigurationError, 'default_priority must be non-negative') if default_priority.negative?
         | 
| 65 61 |  | 
| 66 62 | 
             
                  true
         | 
| 67 63 | 
             
                end
         | 
| @@ -10,7 +10,7 @@ module ContextualConfig | |
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 12 | 
             
                # Register a module with its configuration
         | 
| 13 | 
            -
                def register(module_name | 
| 13 | 
            +
                def register(module_name)
         | 
| 14 14 | 
             
                  module_sym = module_name.to_sym
         | 
| 15 15 | 
             
                  config = @modules[module_sym] || ModuleConfig.new(module_name)
         | 
| 16 16 | 
             
                  yield(config) if block_given?
         | 
    
        data/lib/contextual_config.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require_relative 'contextual_config/version'
         | 
| 2 4 | 
             
            require 'active_support'
         | 
| 3 5 | 
             
            require 'active_record'
         | 
| @@ -66,6 +68,4 @@ module ContextualConfig | |
| 66 68 | 
             
            end
         | 
| 67 69 |  | 
| 68 70 | 
             
            # Load Rails generators if Rails is available
         | 
| 69 | 
            -
            if defined?(Rails)
         | 
| 70 | 
            -
              require 'contextual_config/generators'
         | 
| 71 | 
            -
            end
         | 
| 71 | 
            +
            require 'contextual_config/generators' if defined?(Rails)
         | 
| @@ -18,8 +18,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version | |
| 18 18 | 
             
                  # Lower numbers generally indicate higher priority.
         | 
| 19 19 | 
             
                  t.integer :priority, null: false, default: 100
         | 
| 20 20 |  | 
| 21 | 
            -
                  # ' | 
| 22 | 
            -
                   | 
| 21 | 
            +
                  # 'deleted_at' allows for soft-deleting configurations.
         | 
| 22 | 
            +
                  # NULL indicates active configuration, timestamp indicates soft-deleted.
         | 
| 23 | 
            +
                  t.datetime :deleted_at
         | 
| 23 24 |  | 
| 24 25 | 
             
                  # 'description' provides a human-readable summary of the configuration's purpose.
         | 
| 25 26 | 
             
                  t.text :description
         | 
| @@ -41,8 +42,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version | |
| 41 42 | 
             
                # Note: If 'type' is not used or key should be globally unique for this table,
         | 
| 42 43 | 
             
                # a simpler index might be `add_index :<%= @migration_table_name %>, :key, unique: true`
         | 
| 43 44 |  | 
| 44 | 
            -
                #  | 
| 45 | 
            -
                 | 
| 45 | 
            +
                # Functional index to efficiently query active configurations by priority.
         | 
| 46 | 
            +
                # Uses expression index on (deleted_at IS NULL) for optimal soft-delete performance.
         | 
| 47 | 
            +
                add_index :<%= @migration_table_name %>, ['(deleted_at IS NULL)', :priority], name: 'idx_<%= @migration_table_name.first(40) %>_on_active_priority'
         | 
| 46 48 |  | 
| 47 49 | 
             
                # Optional: GIN indexes for jsonb columns if you frequently query directly into them.
         | 
| 48 50 | 
             
                # These can be large and have write performance implications, so add if needed.
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: contextual_config
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.3.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - bazinga012
         | 
| @@ -162,7 +162,6 @@ files: | |
| 162 162 | 
             
            - lib/contextual_config/module_registry.rb
         | 
| 163 163 | 
             
            - lib/contextual_config/services/contextual_matcher.rb
         | 
| 164 164 | 
             
            - lib/contextual_config/version.rb
         | 
| 165 | 
            -
            - lib/generators/contextual_config/configurable_table/USAGE
         | 
| 166 165 | 
             
            - lib/generators/contextual_config/configurable_table/configurable_table_generator.rb
         | 
| 167 166 | 
             
            - lib/generators/contextual_config/configurable_table/templates/migration.rb.tt
         | 
| 168 167 | 
             
            homepage: https://github.com/bazinga012/contextual_config
         | 
| @@ -1,33 +0,0 @@ | |
| 1 | 
            -
            Description:
         | 
| 2 | 
            -
                Generates a migration file for a table that's compatible with ContextualConfig concerns.
         | 
| 3 | 
            -
                The table includes all necessary columns for storing contextual configurations including
         | 
| 4 | 
            -
                key, config_data (JSONB), scoping_rules (JSONB), priority, is_active, and type (for STI).
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            Example:
         | 
| 7 | 
            -
                ```rails generate contextual_config:configurable_table YourModel```
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                This will create:
         | 
| 10 | 
            -
                    db/migrate/create_your_models.rb
         | 
| 11 | 
            -
             | 
| 12 | 
            -
            Options:
         | 
| 13 | 
            -
                --table-name=TABLE_NAME    # Override the default table name
         | 
| 14 | 
            -
                                           # Default: pluralized, underscored version of the model name
         | 
| 15 | 
            -
             | 
| 16 | 
            -
            Examples:
         | 
| 17 | 
            -
                rails generate contextual_config:configurable_table Finance::Configuration
         | 
| 18 | 
            -
                rails generate contextual_config:configurable_table UserPreference --table-name=user_prefs
         | 
| 19 | 
            -
                rails generate contextual_config:configurable_table PayrollConfig
         | 
| 20 | 
            -
             | 
| 21 | 
            -
            The generated migration creates a table with:
         | 
| 22 | 
            -
                - key (string): Identifier for the configuration
         | 
| 23 | 
            -
                - config_data (jsonb): Main configuration payload
         | 
| 24 | 
            -
                - scoping_rules (jsonb): Rules for when configuration applies
         | 
| 25 | 
            -
                - priority (integer): Priority for conflict resolution
         | 
| 26 | 
            -
                - is_active (boolean): Enable/disable configurations
         | 
| 27 | 
            -
                - description (text): Human-readable description
         | 
| 28 | 
            -
                - type (string): For Single Table Inheritance
         | 
| 29 | 
            -
                - timestamps: created_at and updated_at
         | 
| 30 | 
            -
             | 
| 31 | 
            -
            Indexes are automatically created for:
         | 
| 32 | 
            -
                - [type, key] (unique)
         | 
| 33 | 
            -
                - [is_active, priority] (for efficient querying)
         |