class RuboCop::Cop::Style::FetchEnvVar

Suggests `ENV.fetch` for the replacement of `ENV[]`. `ENV[]` silently fails and returns `nil` when the environment variable is unset, which may cause unexpected behaviors when the developer forgets to set it. On the other hand, `ENV.fetch` raises KeyError or returns the explicitly specified default value.

@example

# bad
ENV['X']
x = ENV['X']

# good
ENV.fetch('X')
x = ENV.fetch('X')

# also good
!ENV['X']
ENV['X'].some_method # (e.g. `.nil?`)

Constants

MSG

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 35
def on_send(node)
  env_with_bracket?(node) do |name_node|
    break unless offensive?(node)

    message = format(MSG, key: name_node.source)
    add_offense(node, message: message) do |corrector|
      corrector.replace(node, new_code(name_node))
    end
  end
end

Private Instance Methods

allowable_use?(node) click to toggle source

The following are allowed cases:

  • Used as a flag (e.g., `if ENV` or `!ENV`) because it simply checks whether the variable is set.

  • Receiving a message with dot syntax, e.g. `ENV.nil?`.

  • `ENV` assigned by logical AND/OR assignment.

  • `ENV` is the LHS of a `||`.

# File lib/rubocop/cop/style/fetch_env_var.rb, line 105
def allowable_use?(node)
  used_as_flag?(node) || message_chained_with_dot?(node) || assigned?(node) || or_lhs?(node)
end
allowed_var?(node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 48
def allowed_var?(node)
  env_key_node = node.children.last
  env_key_node.str_type? && cop_config['AllowedVars'].include?(env_key_node.value)
end
assigned?(node) click to toggle source

The following are allowed cases:

  • `ENV` is a receiver of `||=`, e.g. `ENV ||= y`.

  • `ENV` is a receiver of `&&=`, e.g. `ENV &&= y`.

# File lib/rubocop/cop/style/fetch_env_var.rb, line 113
def assigned?(node)
  return false unless (parent = node.parent)&.assignment?

  lhs, _method, _rhs = *parent
  node == lhs
end
message_chained_with_dot?(node) click to toggle source

Check if the node is a receiver and receives a message with dot syntax.

# File lib/rubocop/cop/style/fetch_env_var.rb, line 89
def message_chained_with_dot?(node)
  return false if node.root?

  parent = node.parent
  return false if !parent.call_type? || parent.children.first != node

  parent.dot? || parent.safe_navigation?
end
new_code(name_node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 126
def new_code(name_node)
  "ENV.fetch(#{name_node.source}, nil)"
end
offensive?(node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 84
def offensive?(node)
  !(allowed_var?(node) || allowable_use?(node))
end
or_lhs?(node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 120
def or_lhs?(node)
  return false unless (parent = node.parent)&.or_type?

  parent.lhs == node || parent.parent&.or_type?
end
partial_matched?(node, condition) click to toggle source

Avoid offending in the following cases: `ENV if ENV = x`

# File lib/rubocop/cop/style/fetch_env_var.rb, line 80
def partial_matched?(node, condition)
  node.child_nodes == node.child_nodes & condition.child_nodes
end
used_as_flag?(node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 53
def used_as_flag?(node)
  return false if node.root?
  return true if used_if_condition_in_body(node)

  node.parent.send_type? && (node.parent.prefix_bang? || node.parent.comparison_method?)
end
used_if_condition_in_body(node) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 60
def used_if_condition_in_body(node)
  if_node = node.ancestors.find(&:if_type?)

  return false unless (condition = if_node&.condition)
  return true if condition.send_type? && (condition.child_nodes == node.child_nodes)

  used_in_condition?(node, condition)
end
used_in_condition?(node, condition) click to toggle source
# File lib/rubocop/cop/style/fetch_env_var.rb, line 69
def used_in_condition?(node, condition)
  if condition.send_type?
    return true if condition.assignment_method? && partial_matched?(node, condition)
    return false if !condition.comparison_method? && !condition.predicate_method?
  end

  condition.child_nodes.any?(node)
end