Claude-skill-registry anyway-config-coder
Implement type-safe configuration with anyway_config gem. Use when creating configuration classes, replacing ENV access, or managing application settings. Triggers on configuration, environment variables, settings, secrets, or ENV patterns.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/anyway-config-coder" ~/.claude/skills/majiayu000-claude-skill-registry-anyway-config-coder && rm -rf "$T"
manifest:
skills/data/anyway-config-coder/SKILL.mdsafety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
- references API keys
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content
Anyway Config Coder
Implement type-safe configuration management using the
anyway_config gem. Never access ENV directly - wrap all configuration in typed classes.
Core Principle
Never use ENV directly or Rails credentials. Create typed configuration classes instead.
# WRONG - scattered ENV access api_key = ENV["GEMINI_API_KEY"] timeout = ENV.fetch("API_TIMEOUT", 30).to_i # RIGHT - typed configuration class class GeminiConfig < Anyway::Config attr_config :api_key, timeout: 30 required :api_key end # Usage GeminiConfig.new.api_key
Setup
# Gemfile gem "anyway_config", "~> 2.6"
Configuration Class Structure
Basic Configuration
# config/configs/gemini_config.rb class GeminiConfig < Anyway::Config # Define attributes with defaults attr_config :api_key, model: "gemini-pro", timeout: 30, max_retries: 3 # Mark required attributes required :api_key # Computed helpers def configured? api_key.present? end def base_url "https://generativelanguage.googleapis.com/v1beta" end end
Environment Variable Mapping
Anyway Config automatically maps environment variables:
class GeminiConfig < Anyway::Config attr_config :api_key # GEMINI_API_KEY attr_config :model # GEMINI_MODEL attr_config :timeout # GEMINI_TIMEOUT end # Custom prefix class StripeConfig < Anyway::Config config_name :payment # Uses PAYMENT_* prefix instead of STRIPE_* attr_config :api_key, # PAYMENT_API_KEY :webhook_secret end
Nested Configuration
class AppConfig < Anyway::Config attr_config :name, :environment, database: { host: "localhost", port: 5432, pool: 5 }, redis: { url: "redis://localhost:6379" } end # Access nested values AppConfig.new.database.host AppConfig.new.redis.url
Directory Structure
config/ ├── configs/ │ ├── gemini_config.rb │ ├── stripe_config.rb │ ├── storage_config.rb │ └── app_config.rb └── settings/ # YAML files (optional) ├── gemini.yml └── storage.yml
YAML Configuration Files
# config/settings/gemini.yml default: &default model: gemini-pro timeout: 30 development: <<: *default api_key: <%= ENV["GEMINI_API_KEY"] %> test: <<: *default api_key: test-key production: <<: *default timeout: 60
Using Configurations
Direct Instantiation
config = GeminiConfig.new config.api_key config.timeout
Singleton Pattern (Recommended)
class GeminiConfig < Anyway::Config attr_config :api_key, :model class << self def instance @instance ||= new end end end # Usage anywhere GeminiConfig.instance.api_key
Memoized Helper Method
# app/models/concerns/gemini_client.rb module GeminiClient extend ActiveSupport::Concern private def gemini_config @gemini_config ||= GeminiConfig.new end end
In Jobs/Services
class Cloud::CardGenerator def initialize(cloud) @cloud = cloud @config = GeminiConfig.new end def generate return unless @config.configured? client = Gemini::Client.new( api_key: @config.api_key, timeout: @config.timeout ) # ... end end
Validation
class StorageConfig < Anyway::Config attr_config :bucket, :region, :access_key_id, :secret_access_key # Required attributes required :bucket, :region # Conditional requirements required :access_key_id, :secret_access_key, env: :production # Custom validation def validate! super raise_validation_error("Invalid region") unless valid_regions.include?(region) end private def valid_regions %w[us-east-1 us-west-2 eu-west-1] end end
Type Coercion
class ApiConfig < Anyway::Config # Automatic coercion attr_config timeout: 30 # Integer attr_config enabled: true # Boolean attr_config rate: 1.5 # Float # Coerce arrays from comma-separated strings coerce_types allowed_origins: { type: :string, array: true } # ALLOWED_ORIGINS="example.com,other.com" => ["example.com", "other.com"] end
Testing Configurations
# spec/configs/gemini_config_spec.rb RSpec.describe GeminiConfig do subject(:config) { described_class.new } describe "defaults" do it "has default timeout" do expect(config.timeout).to eq(30) end it "has default model" do expect(config.model).to eq("gemini-pro") end end describe "validation" do it "requires api_key" do expect { described_class.new(api_key: nil) } .to raise_error(Anyway::Config::ValidationError) end end describe "#configured?" do context "with api_key" do subject(:config) { described_class.new(api_key: "test") } it "returns true" do expect(config.configured?).to be true end end end end
Override in Tests
# spec/support/anyway_config.rb RSpec.configure do |config| config.around(:each) do |example| # Override config for test with_env( "GEMINI_API_KEY" => "test-key", "GEMINI_TIMEOUT" => "5" ) do example.run end end end
Common Patterns
Feature Flags
class FeaturesConfig < Anyway::Config attr_config dark_mode: false, beta_features: false, maintenance_mode: false def maintenance? maintenance_mode end def beta? beta_features end end
External API Client
class OpenAIConfig < Anyway::Config attr_config :api_key, :organization_id, model: "gpt-4", max_tokens: 1000, temperature: 0.7 required :api_key def client_options { access_token: api_key, organization_id: organization_id }.compact end end
Multi-Environment Storage
class StorageConfig < Anyway::Config attr_config provider: "local", bucket: nil, endpoint: nil, credentials: {} def s3? provider == "s3" end def r2? provider == "r2" end def local? provider == "local" end def service_options case provider when "s3" then s3_options when "r2" then r2_options else local_options end end end
Anti-Patterns
| Anti-Pattern | Problem | Solution |
|---|---|---|
Direct | No type safety, scattered | Config class |
everywhere | Duplication, no validation | Centralized config |
| Rails credentials | Complex, hard to test | anyway_config classes |
| Hardcoded secrets | Security risk | Environment variables |
| Magic strings | Typos, no IDE support | Config constants |
Quick Reference
# Create config class class MyConfig < Anyway::Config attr_config :required_key, # Required optional: "default" # With default required :required_key end # Environment variables MY_REQUIRED_KEY=value # Mapped automatically MY_OPTIONAL=override # Overrides default # Usage config = MyConfig.new config.required_key # => "value" config.optional # => "override"
Detailed References
- Dynamic configs, callbacks, inheritancereferences/advanced-patterns.md