'Boolean logic with Rails ENV variables

Since Rails ENV variables should only have string values it could be the problem to decide how to use ENV variable for use cases which need boolean logic. For example since ENV variable have a string value and it would always be truthy it wouldn't be too nice to do something like that:

if ENV['MY_VARIABLE']
  # do something
else
  # do something else
end

So there are at least 2 ways to accomplish things like above:

Initialize variable with particular value and check it

if ENV['MY_VARIABLE'] == 'some string'
  # do something
elsif ENV['MY_VARIABLE'] == 'some other string'
  # do something else
end

Or just initialize variable with any value and check if it was initialized (the code could be exactly as we wanted).

if ENV['MY_VARIABLE']
  # do something
else
  # do something else
end

The question is what option is more preferred and what pros and cons each of them could have?



Solution 1:[1]

You should probably refactor your code and use a custom class, so it's more maintenable and changes may be done easily:

class MyEnv
  TRUTHY_VALUES = %w(t true yes y 1).freeze
  FALSEY_VALUES = %w(f false n no 0).freeze

  attr_reader :value

  def initialize(name)
    @value = ENV[name].to_s.downcase
  end

  def to_boolean
    return true if TRUTHY_VALUES.include?(value.to_s)
    return false if FALSEY_VALUES.include?(value.to_s)
    # You can even raise an exception if there's an invalid value
    raise "Invalid value '#{value}' for boolean casting"
  end
end

# Usage example:
MyEnv.new("MY_VARIABLE").to_boolean

I think that, for boolean environment variables, it's more human-friendly to have values like yes, true, no... instead of present or not present variables.

The only drawback that I can see here is performance, where you jump from nil checking (easy) to a comparisson of strings (a little bit more complex). Give the computer power these days, and if performance is not an issue for you, it would be no problem for you.

So, in conclussion: strings-checks are more human friendly and slower, presence-checks are faster but more obscure.

Solution 2:[2]

If you use Rails 5+, you can do ActiveModel::Type::Boolean.new.cast(ENV['MY_VARIABLE']).

In Rails 4.2, use ActiveRecord::Type::Boolean.new.type_cast_from_user(ENV['MY_VARIABLE']).

Documentation Rails 5+: https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html

Solution 3:[3]

Environment Variables as the name suggests, are environment dependent variables which store different values for same keys based on the environment(production, staging, development) you working on.

e.g. it holds an Access_Key for some api which has sandbox mode and production mode. Thus, to make your code DRY and effective you set an environment variable to get that access_key of sandbox mode for development/staging and live key for production.

What you are trying to do is use them unlike the reason they are defined for, no doubt they can be used that way. Since they are constants what I recommend is doing the following.

create a constants.rb file in your initializers containing

class Constant
  BOOL_CONSTANT = ENV['MY_VARIABLE'].present?
  # OR
  BOOL_CONSTANT = ENV['MY_VARIABLE'] == 'true'
end

then you can use it anywhere you like. This way you can achieve what you want to but under the hood. ;)

Solution 4:[4]

The answers here are fine, but I wanted the most conciseness without sacrificing performance or complexity. Here is a modified version of @Wikiti's answer. I don't think it's necessary to define falsey values. Anything contained in the set of true values can be effectively considered as 'true', otherwise treat it as false.

Note that this is intentionally different than the ActiveModel Boolean class, which will treat everything as true if it is NOT contained in a falsey value. If you're looking for strict boolean flags, IMO it's a better practice to have false as the default value, and true to only be returned if the value is explicitly defined as such. Programming languages tend to use false as the default in a boolean variable, so this better practice is grounded in intuitiveness.

class EnvTrue 
  TRUE_VALUES = %w[1 y Y true TRUE True].freeze

  def self.[](key)
    TRUE_VALUES.include?(v)
  end
end

# Usage example:
EnvTrue['MY_VARIABLE'] # returns true or false
ENV['MY_VARIABLE'] # When you want the actual value

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Pere Joan Martorell
Solution 3 Md. Farhan Memon
Solution 4