SmartCredentials

SmartCredentials intelligently manages your Rails application credentials by allowing environment variables to override Rails encrypted credentials with automatic fallback. Perfect for development, staging, and production environments where you want the flexibility of ENV variables without losing the security of encrypted credentials.

Features

  • Automatic Fallback - ENV variables take priority, falling back to Rails credentials
  • Nested Credentials - Full support for deeply nested credential structures
  • Familiar Interface - Works just like Rails.application.credentials
  • Multiple Access Patterns - Method chaining, hash-style, and dig support

Installation

Add this line to your application's Gemfile:

gem "smart_credentials"

Usage

Basic Usage

Instead of using Rails.application.credentials.api_key, use:

SmartCredentials.api_key

This will:

  1. First check for ENV["API_KEY"]
  2. If not found, fall back to Rails.application.credentials.api_key

Nested Credentials

For nested credentials like Rails.application.credentials.aws.access_key_id:

SmartCredentials.aws.access_key_id

This will:

  1. First check for ENV["AWS_ACCESS_KEY_ID"]
  2. If not found, fall back to Rails.application.credentials.aws.access_key_id

Hash-Style Access

You can also use hash-style access:

SmartCredentials[:api_key]
SmartCredentials[:aws][:access_key_id]

# or mix and match
SmartCredentials.aws[:access_key_id]
SmartCredentials[:aws].access_key_id

Dig Method

For deeply nested values:

SmartCredentials.dig(:aws, :s3, :bucket_name)
# Checks ENV["AWS_S3_BUCKET_NAME"]
# Falls back to Rails.application.credentials.aws.s3.bucket_name

Configuration

SmartCredentials works without any configuration, but you can customize its behavior.

Using an Initializer

Create config/initializers/smart_credentials.rb:

# Block style
SmartCredentials::Config.setup do |config|
  config.env_prefix = "MYAPP"
  config.env_separator = "__"
end

# or direct assignment style
SmartCredentials::Config.env_prefix = "MYAPP"
SmartCredentials::Config.env_separator = "__"

Configuration Options

env_prefix

Add a prefix to all ENV variable lookups:

SmartCredentials::Config.env_prefix = "MYAPP"

# Now looks for ENV["MYAPP_API_KEY"] instead of ENV["API_KEY"]
SmartCredentials.api_key

Default: nil (no prefix)

env_separator

Change the separator used for nested keys:

SmartCredentials::Config.env_separator = "__"

# Now looks for ENV["AWS__ACCESS_KEY_ID"] instead of ENV["AWS_ACCESS_KEY_ID"]
SmartCredentials.aws.access_key_id

# Also affects prefix separator:
SmartCredentials::Config.env_prefix = "MYAPP"
# Now looks for ENV["MYAPP__AWS__ACCESS_KEY_ID"] instead of ENV["MYAPP_AWS_ACCESS_KEY_ID"]
SmartCredentials.aws.access_key_id

Default: "_" (underscore)

Real-World Examples

Example credentials.yml.enc

production:
  api_key: "production_key_from_credentials"
  secret_key_base: "long_secret_string"
  aws:
    access_key_id: "AKIAIOSFODNN7EXAMPLE"
    secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
    region: "us-east-1"
    s3:
      bucket_name: "my-app-production"
  stripe:
    publishable_key: "pk_live_123"
    secret_key: "sk_live_456"
  database:
    host: "db.example.com"
    username: "app_user"
    password: "secure_password"

AWS Configuration

# config/initializers/aws.rb
Aws.config.update(
  credentials: Aws::Credentials.new(
    SmartCredentials.aws.access_key_id,
    SmartCredentials.aws.secret_access_key
  ),
  region: SmartCredentials.aws.region
)

Stripe Configuration

# config/initializers/stripe.rb
Stripe.api_key = SmartCredentials.stripe.secret_key

Database Configuration

# config/database.yml
production:
  adapter: postgresql
  host: <%= SmartCredentials.database.host %>
  username: <%= SmartCredentials.database.username %>
  password: <%= SmartCredentials.database.password %>
  database: myapp_production

In Your Application Code

# app/services/api_client.rb
class ApiClient
  def initialize
    @api_key = SmartCredentials.api_key
    @base_url = SmartCredentials.api.base_url
  end

  def call
    HTTParty.get(@base_url, headers: { "Authorization" => "Bearer #{@api_key}" })
  end
end

Use Cases

Development

Keep encrypted credentials for your team while allowing individual developers to override specific values:

# Developer can use their own AWS credentials without touching credentials file
export AWS_ACCESS_KEY_ID="my_dev_key"
export AWS_SECRET_ACCESS_KEY="my_dev_secret"

Staging/Production

Use ENV variables for secrets in production while keeping sensible defaults in credentials for development:

# Always works in any environment
SmartCredentials.stripe.secret_key

Behavior

SmartCredentials behaves like Rails credentials:

# Returns the value if it exists
SmartCredentials.api_key
# => "your_api_key"

# Returns nil if it doesn't exist
SmartCredentials.nonexistent_key
# => nil

# Raises NoMethodError when chaining after nil (like Rails)
SmartCredentials.nonexistent.nested.key
# => NoMethodError: undefined method `nested' for nil

Contributing

Bug reports and pull requests are welcome.

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am "Added some new feature")
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

License

The gem is available as open source under the terms of the MIT License.