Skip to content

Ruby Environment Variables

Environment variables are dynamic values defined in the operating system that can affect how programs run. Ruby provides multiple ways to access and manipulate environment variables. This chapter will explain in detail how to use environment variables in Ruby.

🌍 What are Environment Variables?

Environment variables are operating system-level configuration parameters that provide configuration information for programs running on the system. Common environment variables include:

  • PATH: Search path for executable files
  • HOME: User home directory
  • USER: Current username
  • LANG: System language setting
  • RUBY_ENV: Ruby application environment (development, production, test)

📦 Accessing Environment Variables

Using the ENV Object

Ruby accesses environment variables through the built-in ENV object, which is a hash-like object:

ruby
# Get environment variables
home_dir = ENV['HOME']
user_name = ENV['USER']
path = ENV['PATH']

puts "Home directory: #{home_dir}"
puts "Username: #{user_name}"
puts "Path: #{path}"

# Check if environment variable exists
if ENV.has_key?('RUBY_ENV')
  puts "Ruby environment: #{ENV['RUBY_ENV']}"
else
  puts "RUBY_ENV environment variable not set"
end

# Get all environment variables
puts "All environment variables:"
ENV.each do |key, value|
  puts "#{key}: #{value}"
end

Safe Access to Environment Variables

ruby
# Provide default values
database_url = ENV.fetch('DATABASE_URL', 'sqlite://localhost/default.db')
api_key = ENV.fetch('API_KEY') { 'default-api-key' }

# Check if it exists
if ENV.key?('SECRET_KEY')
  secret_key = ENV['SECRET_KEY']
else
  puts "Warning: SECRET_KEY environment variable not set"
  secret_key = 'default-secret'
end

# Using ternary operator
debug_mode = ENV['DEBUG'] ? ENV['DEBUG'] == 'true' : false

🛠️ Setting Environment Variables

Setting in Ruby Program

ruby
# Set environment variables
ENV['MY_APP_NAME'] = 'My Ruby Application'
ENV['APP_VERSION'] = '1.0.0'

# Verify settings
puts "Application name: #{ENV['MY_APP_NAME']}"
puts "Application version: #{ENV['APP_VERSION']}"

# Delete environment variable
ENV.delete('MY_APP_NAME')
puts "Application name: #{ENV['MY_APP_NAME']}"  # nil

Setting in Different Operating Systems

Linux/macOS Systems

bash
# Temporary setting (valid for current session)
export DATABASE_URL="postgresql://localhost/myapp"
export API_KEY="your-api-key-here"

# Setting in shell script
#!/bin/bash
export RUBY_ENV=production
ruby my_app.rb

# Permanent setting in .bashrc or .zshrc
echo 'export RUBY_ENV=development' >> ~/.bashrc
source ~/.bashrc

Windows Systems

cmd
# Command line setting (valid for current session)
set DATABASE_URL=postgresql://localhost/myapp
set API_KEY=your-api-key-here

# PowerShell setting
$env:RUBY_ENV="production"

# Permanent setting (system environment variables)
# Set through System Properties -> Advanced -> Environment Variables

🎯 Common Ruby Environment Variables

ruby
# RUBY_ENV - Application environment
ruby_env = ENV.fetch('RUBY_ENV', 'development')
case ruby_env
when 'development'
  puts "Development environment mode"
when 'production'
  puts "Production environment mode"
when 'test'
  puts "Test environment mode"
else
  puts "Unknown environment: #{ruby_env}"
end

# RUBYLIB - Ruby library path
ruby_lib_path = ENV['RUBYLIB']
puts "Ruby library path: #{ruby_lib_path}" if ruby_lib_path

# RUBYOPT - Ruby command line options
ruby_options = ENV['RUBYOPT']
puts "Ruby options: #{ruby_options}" if ruby_options

# GEM_PATH - Gem search path
gem_path = ENV['GEM_PATH']
puts "Gem path: #{gem_path}" if gem_path

# GEM_HOME - Gem installation directory
gem_home = ENV['GEM_HOME']
puts "Gem home directory: #{gem_home}" if gem_home

Application Environment Variables

ruby
# Database configuration
database_url = ENV.fetch('DATABASE_URL') do
  case ENV.fetch('RUBY_ENV', 'development')
  when 'development'
    'sqlite://localhost/development.db'
  when 'test'
    'sqlite://localhost/test.db'
  when 'production'
    raise "DATABASE_URL environment variable must be set"
  end
end

# API key
api_key = ENV.fetch('API_KEY') do
  puts "Warning: Using default API key (development only)"
  'default-api-key'
end

# Server configuration
server_port = ENV.fetch('PORT', 3000).to_i
server_host = ENV.fetch('HOST', 'localhost')

puts "Server configuration: #{server_host}:#{server_port}"

🛡️ Environment Variable Security

Sensitive Information Handling

ruby
class Config
  # Sensitive environment variable list
  SENSITIVE_VARS = %w[
    DATABASE_URL
    API_KEY
    SECRET_KEY
    PASSWORD
    PRIVATE_KEY
  ].freeze

  def self.get(key, default = nil)
    value = ENV[key]
    return default if value.nil? || value.empty?
    value
  end

  def self.get_required(key)
    value = ENV[key]
    raise "Environment variable must be set: #{key}" if value.nil? || value.empty?
    value
  end

  def self.safe_display
    ENV.each do |key, value|
      if SENSITIVE_VARS.any? { |sensitive| key.upcase.include?(sensitive) }
        puts "#{key}: ********"  # Hide sensitive information
      else
        puts "#{key}: #{value}"
      end
    end
  end
end

# Usage example
begin
  db_url = Config.get_required('DATABASE_URL')
  api_key = Config.get('API_KEY', 'default-key')
  puts "Configuration loaded successfully"
rescue => e
  puts "Configuration error: #{e.message}"
end

Environment Variable Validation

ruby
class EnvironmentValidator
  def self.validate_required(vars)
    missing = []
    vars.each do |var|
      missing << var unless ENV[var] && !ENV[var].empty?
    end

    unless missing.empty?
      raise "Missing required environment variables: #{missing.join(', ')}"
    end
  end

  def self.validate_format(var, pattern, description = nil)
    value = ENV[var]
    return true if value.nil? || value.empty?

    unless value.match?(pattern)
      desc = description || "#{var} format is incorrect"
      raise "Environment variable validation failed: #{desc}"
    end

    true
  end

  def self.validate_numeric(var, min = nil, max = nil)
    value = ENV[var]
    return true if value.nil? || value.empty?

    begin
      num = Integer(value)
      if min && num < min
        raise "Environment variable #{var} must be greater than or equal to #{min}"
      end
      if max && num > max
        raise "Environment variable #{var} must be less than or equal to #{max}"
      end
    rescue ArgumentError
      raise "Environment variable #{var} must be a number"
    end

    true
  end
end

# Usage example
begin
  # Validate required environment variables
  EnvironmentValidator.validate_required(['DATABASE_URL', 'API_KEY'])

  # Validate email format
  EnvironmentValidator.validate_format('ADMIN_EMAIL',
                                     /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
                                     'Admin email format is incorrect')

  # Validate port number
  EnvironmentValidator.validate_numeric('PORT', 1, 65535)

  puts "Environment variable validation passed"
rescue => e
  puts "Environment variable validation failed: #{e.message}"
  exit 1
end

📁 Environment Configuration Management

Using Configuration Files

ruby
require 'yaml'

class EnvironmentConfig
  def self.load_config(config_file = 'config.yml')
    # Load configuration file
    config = YAML.load_file(config_file) rescue {}

    # Merge environment variables
    env = ENV.fetch('RUBY_ENV', 'development')
    config.merge(config[env] || {})
  end

  def self.database_config
    {
      url: ENV.fetch('DATABASE_URL') { default_database_url },
      pool: ENV.fetch('DB_POOL_SIZE', 5).to_i,
      timeout: ENV.fetch('DB_TIMEOUT', 5000).to_i
    }
  end

  private

  def self.default_database_url
    case ENV.fetch('RUBY_ENV', 'development')
    when 'development'
      'sqlite://localhost/development.db'
    when 'test'
      'sqlite://localhost/test.db'
    when 'production'
      raise "DATABASE_URL must be set in production environment"
    end
  end
end

# config.yml example
=begin
---
development:
  database_url: sqlite://localhost/dev.db
  api_key: dev-key
  debug: true

production:
  debug: false

test:
  database_url: sqlite://localhost/test.db
  api_key: test-key
=end

# Using configuration
config = EnvironmentConfig.load_config
puts "Database URL: #{config['database_url']}"

Environment-Specific Configuration

ruby
class AppConfig
  def self.environment
    @environment ||= ENV.fetch('RUBY_ENV', 'development')
  end

  def self.development?
    environment == 'development'
  end

  def self.production?
    environment == 'production'
  end

  def self.test?
    environment == 'test'
  end

  def self.log_level
    if development?
      'debug'
    elsif production?
      'warn'
    else
      'info'
    end
  end

  def self.cache_enabled?
    return false if development?
    ENV.fetch('CACHE_ENABLED', 'true') == 'true'
  end

  def self.worker_processes
    if production?
      ENV.fetch('WORKER_PROCESSES', 4).to_i
    else
      1
    end
  end
end

# Configure application based on environment
puts "Current environment: #{AppConfig.environment}"
puts "Log level: #{AppConfig.log_level}"
puts "Cache enabled: #{AppConfig.cache_enabled?}"
puts "Worker processes: #{AppConfig.worker_processes}"

🧪 Environment Variable Testing

Testing Environment Variables

ruby
# Test environment variable setting and getting
RSpec.describe 'Environment Variables' do
  before do
    @original_env = ENV.to_hash
  end

  after do
    ENV.replace(@original_env)
  end

  it 'should get environment variable' do
    ENV['TEST_VAR'] = 'test_value'
    expect(ENV['TEST_VAR']).to eq('test_value')
  end

  it 'should provide default value' do
    expect(ENV.fetch('NON_EXISTENT_VAR', 'default')).to eq('default')
  end

  it 'should handle required variables' do
    expect { ENV.fetch('REQUIRED_VAR') }.to raise_error(KeyError)
  end
end

# Simple test script
def test_environment_variables
  puts "=== Environment Variable Test ==="

  # Test basic operations
  ENV['TEST_VAR'] = 'test_value'
  puts "Set test variable: #{ENV['TEST_VAR']}"

  # Test default value
  default_value = ENV.fetch('NON_EXISTENT', 'default')
  puts "Default value test: #{default_value}"

  # Test existence check
  exists = ENV.key?('PATH')
  puts "PATH variable exists: #{exists}"

  # Clean up test variable
  ENV.delete('TEST_VAR')
  puts "Test variable cleanup complete"

  puts "=== Test Complete ==="
end

# Run test
test_environment_variables if __FILE__ == $0

🎯 Best Practices

1. Using dotenv for Development Environment Variables

ruby
# Gemfile
# gem 'dotenv-rails'  # Rails application
# gem 'dotenv'        # Regular Ruby application

# .env file example
=begin
# Development environment configuration
DATABASE_URL=sqlite://localhost/development.db
API_KEY=dev-api-key
DEBUG=true
SECRET_KEY=dev-secret-key
=end

# Load dotenv (at application startup)
require 'dotenv/load' if development?

# Access environment variables
database_url = ENV['DATABASE_URL']
api_key = ENV['API_KEY']

2. Environment Variable Documentation

ruby
# environment_variables.rb
class EnvironmentVariables
  # Application environment variable documentation
  VARIABLES = {
    'DATABASE_URL' => {
      description: 'Database connection URL',
      required: true,
      default: nil,
      example: 'postgresql://user:pass@localhost/dbname'
    },
    'API_KEY' => {
      description: 'Third-party API key',
      required: false,
      default: 'default-key',
      example: 'sk-xxxxxxxxxxxxxxxxxxxxxxxx'
    },
    'PORT' => {
      description: 'Server port',
      required: false,
      default: '3000',
      example: '3000'
    },
    'RUBY_ENV' => {
      description: 'Application environment',
      required: false,
      default: 'development',
      example: 'development|production|test'
    }
  }.freeze

  def self.document
    puts "Application Environment Variable Documentation:"
    puts "=" * 50
    VARIABLES.each do |name, info|
      puts "Variable name: #{name}"
      puts "  Description: #{info[:description]}"
      puts "  Required: #{info[:required] ? 'Yes' : 'No'}"
      puts "  Default: #{info[:default] || 'None'}"
      puts "  Example: #{info[:example]}"
      puts
    end
  end
end

# Generate documentation
# EnvironmentVariables.document

3. Environment Isolation

ruby
class EnvironmentManager
  def self.setup_environment
    case ENV.fetch('RUBY_ENV', 'development')
    when 'development'
      setup_development
    when 'production'
      setup_production
    when 'test'
      setup_test
    else
      puts "Unknown environment: #{ENV['RUBY_ENV']}"
    end
  end

  private

  def self.setup_development
    puts "Setting up development environment"
    # Development environment specific configuration
  end

  def self.setup_production
    puts "Setting up production environment"
    # Production environment specific configuration
    disable_debug_output
    enable_caching
  end

  def self.setup_test
    puts "Setting up test environment"
    # Test environment specific configuration
  end

  def self.disable_debug_output
    # Disable debug output
  end

  def self.enable_caching
    # Enable caching
  end
end

# Call at application startup
EnvironmentManager.setup_environment

📚 Next Steps

After mastering Ruby environment variables, we recommend continuing to learn:

Continue your Ruby learning journey!

Content is for learning and research only.