Class: Invar::Reality

Inherits:
Object
  • Object
show all
Includes:
TestExtension::RealityMethods
Defined in:
lib/invar/reality.rb,
lib/invar/test.rb

Overview

Stores application config, secrets, and environmental variables. Environment variables come from ENV at the time of instantiation, while configs and secrets are loaded from respective files in the appropriate location. The secrets file is kept encrypted at rest.

Fetch information from a Reality by using the slash operator or square brackets:

invar = Invar::Reality.new(namespace: 'test-app')

puts invar / :config / :database / :host
puts invar[:config][:database][:host] # same as above

puts invar / :secrets / :database / :user

Note: ‘Invar.new` is a shorthand for Invar::Reality.new

Information known by a Reality is immutable. You may use the ‘#pretend` method to simulate different values during testing.

 # In your tests, define an after_load hook
require 'invar/test'
Invar.after_load do |reality|
   reality[:config][:database][:host].pretend 'example.com'
end

# then later, in your app, it will use the pretend value
invar = Invar.new namespace: 'my-app'
puts invar / :config / :database / :host # prints example.com

See Also:

  • new
  • Reality#after_load
  • Reality#pretend

Defined Under Namespace

Classes: RealityValidator

Constant Summary collapse

DEFAULT_KEY_FILE_NAME =

Name of the default key file to be searched for within config directories

'master_key'

Instance Method Summary collapse

Constructor Details

#initialize(namespace:, decryption_keyfile: nil, configs_schema: nil, secrets_schema: nil) ⇒ Reality

Constructs a new Invar.

It will search for config, secrets, and decryption key files using the XDG specification.

The secrets file is decrypted using Lockbox. The key will be requested from these locations in order:

1. the Lockbox.master_key variable
2. the LOCKBOX_MASTER_KEY environment variable.
3. saved in a secure key file (recommended)
4. manual terminal prompt entry (recommended)

The :decryption_keyfile argument specifies the filename to read for option 3. It will be searched for in the same XDG locations as the secrets file. The decryption keyfile will be checked for safe permission modes.

NEVER hardcode your encryption key. This class intentionally does not accept a raw string of your decryption key to discourage hardcoding your encryption key and committing it to version control.

Parameters:

  • namespace (String)

    name of the subdirectory within XDG locations

  • decryption_keyfile (#read) (defaults to: nil)

    Any #read capable object referring to the decryption key file



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/invar/reality.rb', line 67

def initialize(namespace:, decryption_keyfile: nil, configs_schema: nil, secrets_schema: nil)
   locator      = FileLocator.new(namespace)
   search_paths = locator.search_paths.join(', ')

   begin
      @configs = Scope.new(load_configs(locator))
   rescue FileLocator::FileNotFoundError
      raise MissingConfigFileError,
            "No Invar config file found. Create config.yml in one of these locations: #{ search_paths }"
   end

   begin
      @secrets = Scope.new(load_secrets(locator, decryption_keyfile || DEFAULT_KEY_FILE_NAME))
   rescue FileLocator::FileNotFoundError
      hint = "Create encrypted secrets.yml in one of these locations: #{ search_paths }"
      raise MissingSecretsFileError,
            "No Invar secrets file found. #{ hint }"
   end

   freeze

   RealityValidator.new(configs_schema, secrets_schema).validate(@configs, @secrets)
end

Instance Method Details

#fetch(base_scope) ⇒ Object Also known as: /, []

Fetch from one of the two base scopes: :config or :secret. Plural names are also accepted (ie. :configs and :secrets).

Parameters:

  • base_scope (Symbol, String)


95
96
97
98
99
100
101
102
103
104
# File 'lib/invar/reality.rb', line 95

def fetch(base_scope)
   case base_scope
   when /configs?/i
      @configs
   when /secrets?/i
      @secrets
   else
      raise ArgumentError, 'The root scope name must be either :config or :secret.'
   end
end