class RuboCop::Cop::Gemspec::RequiredRubyVersion

Checks that `required_ruby_version` in a gemspec file is set to a valid value (non-blank) and matches `TargetRubyVersion` as set in RuboCop's configuration for the gem.

This ensures that RuboCop is using the same Ruby version as the gem.

@example

# When `TargetRubyVersion` of .rubocop.yml is `2.5`.

# bad
Gem::Specification.new do |spec|
  # no `required_ruby_version` specified
end

# bad
Gem::Specification.new do |spec|
  spec.required_ruby_version = '>= 2.4.0'
end

# bad
Gem::Specification.new do |spec|
  spec.required_ruby_version = '>= 2.6.0'
end

# bad
Gem::Specification.new do |spec|
  spec.required_ruby_version = ''
end

# good
Gem::Specification.new do |spec|
  spec.required_ruby_version = '>= 2.5.0'
end

# good
Gem::Specification.new do |spec|
  spec.required_ruby_version = '>= 2.5'
end

# accepted but not recommended
Gem::Specification.new do |spec|
  spec.required_ruby_version = ['>= 2.5.0', '< 2.7.0']
end

# accepted but not recommended, since
# Ruby does not really follow semantic versioning
Gem::Specification.new do |spec|
  spec.required_ruby_version = '~> 2.5'
end

Constants

MISSING_MSG
NOT_EQUAL_MSG
RESTRICT_ON_SEND

Public Instance Methods

on_new_investigation() click to toggle source
# File lib/rubocop/cop/gemspec/required_ruby_version.rb, line 78
def on_new_investigation
  add_global_offense(MISSING_MSG) unless required_ruby_version?(processed_source.ast)
end
on_send(node) click to toggle source
# File lib/rubocop/cop/gemspec/required_ruby_version.rb, line 82
def on_send(node)
  version_def = node.first_argument
  return if dynamic_version?(version_def)

  ruby_version = extract_ruby_version(defined_ruby_version(version_def))
  return if ruby_version == target_ruby_version.to_s

  add_offense(version_def, message: not_equal_message(ruby_version, target_ruby_version))
end

Private Instance Methods

dynamic_version?(node) click to toggle source
# File lib/rubocop/cop/gemspec/required_ruby_version.rb, line 94
def dynamic_version?(node)
  (node.send_type? && !node.receiver) ||
    node.variable? ||
    node.each_descendant(:send, *RuboCop::AST::Node::VARIABLES).any?
end
extract_ruby_version(required_ruby_version) click to toggle source
# File lib/rubocop/cop/gemspec/required_ruby_version.rb, line 100
def extract_ruby_version(required_ruby_version)
  return unless required_ruby_version

  if required_ruby_version.is_a?(Array)
    required_ruby_version = required_ruby_version.detect do |v|
      /[>=]/.match?(v.str_content)
    end
  elsif required_ruby_version.array_type?
    required_ruby_version = required_ruby_version.children.detect do |v|
      /[>=]/.match?(v.str_content)
    end
  end

  required_ruby_version.str_content.scan(/\d/).first(2).join('.')
end
not_equal_message(required_ruby_version, target_ruby_version) click to toggle source
# File lib/rubocop/cop/gemspec/required_ruby_version.rb, line 116
def not_equal_message(required_ruby_version, target_ruby_version)
  format(
    NOT_EQUAL_MSG,
    required_ruby_version: required_ruby_version,
    gemspec_filename: File.basename(processed_source.file_path),
    target_ruby_version: target_ruby_version
  )
end