README.md
# ENV!
Verify correctness of environment configuration at startup time.
[![Gem Version](https://img.shields.io/gem/v/env_bang?logo=rubygems)](https://rubygems.org/gems/env_bang)
[![Build Status](https://img.shields.io/circleci/build/github/jcamenisch/ENV_BANG/main)](https://dl.circleci.com/status-badge/redirect/gh/jcamenisch/ENV_BANG/tree/main)
[![Maintainability](https://img.shields.io/codeclimate/maintainability/jcamenisch/ENV_BANG?logo=codeclimate)](https://codeclimate.com/github/jcamenisch/ENV_BANG)
[![Coverage Status](https://img.shields.io/coverallsCoverage/github/jcamenisch/ENV_BANG?logo=coveralls)](https://coveralls.io/r/jcamenisch/ENV_BANG)
ENV! provides a thin wrapper around ENV to accomplish a few things:
- Provide a central place to specify all your app’s environment variables.
- Fail loudly and helpfully if any environment variables are missing or invalid.
- Prevent an application from starting up with invalid environment variables.
## Installation
Add this line to your application’s Gemfile:
```ruby
gem 'env_bang'
```
Or for Rails apps, use `env_bang-rails` instead for more convenience:
```ruby
gem 'env_bang-rails'
```
And then execute:
```sh
$ bundle
```
## Usage
### Basic Configuration
First, configure your environment variables somewhere in your app’s
startup process. If you use the env_bang-rails gem, place this in `config/env.rb`
to load before application configuration.
Example configuration:
```ruby
ENV!.config do
use :APP_HOST
use :RAILS_SECRET_TOKEN
use :STRIPE_SECRET_KEY
use :STRIPE_PUBLISHABLE_KEY
# ... etc.
end
```
Once a variable is specified with the `use` method, access it with
```ruby
ENV!['MY_VAR']
```
This will function just like accessing `ENV` directly, except that it will require the variable
to have been specified, and be present in the current environment. If either of these conditions
is not met, a KeyError will be raised with an explanation of what needs to be configured.
### Adding a default value
For some variables, you’ll want to include a default value in your code, and allow each
environment to ommit the variable for default behaviors. You can accomplish this with the
`:default` option:
```ruby
ENV!.config do
# ...
use :MAIL_DELIVERY_METHOD, default: 'smtp'
# ...
end
```
### Adding a description
When a new team member installs or deploys your project, they may run into a missing
environment variable error. Save them time by including documentation along with the error
that is raised. To accomplish this, provide a description (of any length) to the `use` method:
```ruby
ENV!.config do
use 'RAILS_SECRET_KEY_BASE',
'Generate a fresh one with `SecureRandom.urlsafe_base64(64)`; see http://guides.rubyonrails.org/security.html#session-storage'
end
```
Now if someone installs or deploys the app without setting the RAILS_SECRET_KEY_BASE variable,
they will see these instructions immediately upon running the app.
### Automatic type conversion
ENV! can convert your environment variables for you, keeping that tedium out of your application
code. To specify a type, use the `:class` option:
```ruby
ENV!.config do
use :COPYRIGHT_YEAR, class: Integer
use :MEMCACHED_SERVERS, class: Array
use :MAIL_DELIVERY_METHOD, class: Symbol, default: :smtp
use :DEFAULT_FRACTION, class: Float
use :ENABLE_SOUNDTRACK, class: :boolean
use :PUPPETMASTERS, class: Hash
end
```
Note that arrays will be derived by splitting the value on commas (','). To get arrays
of a specific type of value, use the `:of` option:
```ruby
ENV!.config do
use :YEARS_OF_INTEREST, class: Array, of: Integer
end
```
Hashes are split on commas (',') and key:value pairs are delimited by colon (':'). To get hashes of a specific type of value, use the `:of` option, and to use a different type for keys (default is `Symbol`), use the `:keys` option:
```ruby
ENV!.config do
use :BIRTHDAYS, class: Hash, of: Integer, keys: String
end
```
#### Default type conversion behavior
If you don’t specify a `:class` option for a variable, ENV! defaults to a special
type conversion called `:StringUnlessFalsey`. This conversion returns a string, unless
the value is a "falsey" string ('false', 'no', 'off', '0', 'disable', or 'disabled').
To turn off this magic for one variable, pass in `class: String`. To disable it globally,
set
```ruby
ENV!.config do
default_class String
end
```
#### Custom type conversion
Suppose your app needs a special type conversion that doesn’t come with ENV_BANG. You can
implement the conversion yourself with the `add_class` method in the `ENV!.config` block.
For example, to convert one of your environment variables to type `Set`, you could write
the following configuration:
```sh
# In your environment:
export NUMBER_SET=1,3,5,7,9
```
```ruby
# In your env.rb configuration file:
require 'set'
ENV!.config do
add_class Set do |value, options|
Set.new self.Array(value, options || {})
end
use :NUMBER_SET, class: Set, of: Integer
end
```
```ruby
# Somewhere in your application:
ENV!['NUMBER_SET']
#=> #<Set: {1, 3, 5, 7, 9}>
```
## Implementation Notes
1. ENV! is simply a method that returns ENV_BANG. In certain contexts
(like defining a class), the exclamation mark notation is not allowed,
so we use an alias to get this shorthand.
2. Any method that can be run within an `ENV!.config` block can also be run
as a method directly on `ENV!`. For instance, instead of
```ruby
ENV!.config do
add_class Set do
...
end
use :NUMBER_SET, class: Set
end
```
It would also work to run
```ruby
ENV!.add_class Set do
...
end
ENV!.use :NUMBER_SET, class: Set
```
While the `config` block is designed to provide a cleaner configuration
file, calling the methods directly can occasionally be handy, such as when
trying things out in an IRB/Pry session.
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request