class RuboCop::Cop::Bundler::GemComment

Each gem in the Gemfile should have a comment explaining its purpose in the project, or the reason for its version or source.

The optional “OnlyFor” configuration array can be used to only register offenses when the gems use certain options or have version specifiers.

When “version_specifiers” is included, a comment will be enforced if the gem has any version specifier.

When “restrictive_version_specifiers” is included, a comment will be enforced if the gem has a version specifier that holds back the version of the gem.

For any other value in the array, a comment will be enforced for a gem if an option by the same name is present. A useful use case is to enforce a comment when using options that change the source of a gem:

For a full list of options supported by bundler, see bundler.io/man/gemfile.5.html .

@example OnlyFor: [] (default)

# bad

gem 'foo'

# good

# Helpers for the foo things.
gem 'foo'

@example OnlyFor: [‘version_specifiers’]

# bad

gem 'foo', '< 2.1'

# good

# Version 2.1 introduces breaking change baz
gem 'foo', '< 2.1'

@example OnlyFor: [‘restrictive_version_specifiers’]

# bad

gem 'foo', '< 2.1'

# good

gem 'foo', '>= 1.0'

# Version 2.1 introduces breaking change baz
gem 'foo', '< 2.1'

@example OnlyFor: [‘version_specifiers’, ‘github’]

# bad

gem 'foo', github: 'some_account/some_fork_of_foo'

gem 'bar', '< 2.1'

# good

# Using this fork because baz
gem 'foo', github: 'some_account/some_fork_of_foo'

# Version 2.1 introduces breaking change baz
gem 'bar', '< 2.1'

Constants

CHECKED_OPTIONS_CONFIG
MSG
RESTRICTIVE_VERSION_PATTERN
RESTRICTIVE_VERSION_SPECIFIERS_OPTION
RESTRICT_ON_SEND
VERSION_SPECIFIERS_OPTION

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 94
def on_send(node)
  return unless gem_declaration?(node)
  return if ignored_gem?(node)
  return if commented_any_descendant?(node)
  return if cop_config[CHECKED_OPTIONS_CONFIG].any? && !checked_options_present?(node)

  add_offense(node)
end

Private Instance Methods

checked_options_present?(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 135
def checked_options_present?(node)
  (cop_config[CHECKED_OPTIONS_CONFIG].include?(VERSION_SPECIFIERS_OPTION) &&
    version_specified_gem?(node)) ||
    (cop_config[CHECKED_OPTIONS_CONFIG].include?(RESTRICTIVE_VERSION_SPECIFIERS_OPTION) &&
      restrictive_version_specified_gem?(node)) ||
    contains_checked_options?(node)
end
commented?(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 109
def commented?(node)
  preceding_lines = preceding_lines(node)
  preceding_comment?(node, preceding_lines.last)
end
commented_any_descendant?(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 105
def commented_any_descendant?(node)
  commented?(node) || node.each_descendant.any? { |n| commented?(n) }
end
contains_checked_options?(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 159
def contains_checked_options?(node)
  (Array(cop_config[CHECKED_OPTIONS_CONFIG]) & gem_options(node).map(&:to_s)).any?
end
gem_options(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 163
def gem_options(node)
  return [] unless node.last_argument&.type == :hash

  node.last_argument.keys.map(&:value)
end
ignored_gem?(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 130
def ignored_gem?(node)
  ignored_gems = Array(cop_config['IgnoredGems'])
  ignored_gems.include?(node.first_argument.value)
end
precede?(node1, node2) click to toggle source

The args node1 & node2 may represent a RuboCop::AST::Node or a Parser::Source::Comment. Both respond to loc.

# File lib/rubocop/cop/bundler/gem_comment.rb, line 116
def precede?(node1, node2)
  node2.loc.line - node1.loc.line <= 1
end
preceding_comment?(node1, node2) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 126
def preceding_comment?(node1, node2)
  node1 && node2 && precede?(node2, node1) && comment_line?(node2.source)
end
preceding_lines(node) click to toggle source
# File lib/rubocop/cop/bundler/gem_comment.rb, line 120
def preceding_lines(node)
  processed_source.ast_with_comments[node].select do |line|
    line.loc.line <= node.loc.line
  end
end
restrictive_version_specified_gem?(node) click to toggle source

Version specifications that restrict all updates going forward. This excludes versions like “>= 1.0” or “!= 2.0.3”.

# File lib/rubocop/cop/bundler/gem_comment.rb, line 152
def restrictive_version_specified_gem?(node)
  return false unless version_specified_gem?(node)

  node.arguments[1..]
      .any? { |arg| arg&.str_type? && RESTRICTIVE_VERSION_PATTERN.match?(arg.value) }
end
version_specified_gem?(node) click to toggle source

Besides the gem name, all other positional arguments to ‘gem` are version specifiers, as long as it has one we know there’s at least one version specifier.

# File lib/rubocop/cop/bundler/gem_comment.rb, line 145
def version_specified_gem?(node)
  # arguments[0] is the gem name
  node.arguments[1]&.str_type?
end