/
target_ruby.rb
154 lines (124 loc) · 3.91 KB
/
target_ruby.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# frozen_string_literal: true
module RuboCop
# The kind of Ruby that code inspected by RuboCop is written in.
class TargetRuby
KNOWN_RUBIES = [2.4, 2.5, 2.6, 2.7, 2.8].freeze
DEFAULT_VERSION = KNOWN_RUBIES.first
OBSOLETE_RUBIES = {
1.9 => '0.41', 2.0 => '0.50', 2.1 => '0.57', 2.2 => '0.68', 2.3 => '0.81'
}.freeze
private_constant :KNOWN_RUBIES, :OBSOLETE_RUBIES
# A place where information about a target ruby version is found.
class Source
attr_reader :version, :name
def initialize(config)
@config = config
@version = find_version
end
def to_s
name
end
end
# The target ruby version may be configured in RuboCop's config.
class RuboCopConfig < Source
def name
"`TargetRubyVersion` parameter (in #{@config.smart_loaded_path})"
end
private
def find_version
@config.for_all_cops['TargetRubyVersion']&.to_f
end
end
# The target ruby version may be found in a .ruby-version file.
class RubyVersionFile < Source
FILENAME = '.ruby-version'
def name
"`#{FILENAME}`"
end
private
def find_version
file = ruby_version_file
return unless file && File.file?(file)
# rubocop:disable Lint/MixedRegexpCaptureTypes
# `(ruby-)` is not a capture type.
File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
# rubocop:enable Lint/MixedRegexpCaptureTypes
md[:version].to_f
end
end
def ruby_version_file
@ruby_version_file ||=
@config.find_file_upwards(FILENAME,
@config.base_dir_for_path_parameters)
end
end
# The lock file of Bundler may identify the target ruby version.
class BundlerLockFile < Source
def name
"`#{bundler_lock_file_path}`"
end
private
def find_version
lock_file_path = bundler_lock_file_path
return nil unless lock_file_path
in_ruby_section = false
File.foreach(lock_file_path) do |line|
# If ruby is in Gemfile.lock or gems.lock, there should be two lines
# towards the bottom of the file that look like:
# RUBY VERSION
# ruby W.X.YpZ
# We ultimately want to match the "ruby W.X.Y.pZ" line, but there's
# extra logic to make sure we only start looking once we've seen the
# "RUBY VERSION" line.
in_ruby_section ||= line.match(/^\s*RUBY\s*VERSION\s*$/)
next unless in_ruby_section
# We currently only allow this feature to work with MRI ruby. If
# jruby (or something else) is used by the project, it's lock file
# will have a line that looks like:
# RUBY VERSION
# ruby W.X.YpZ (jruby x.x.x.x)
# The regex won't match in this situation.
result = line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)
return result.captures.first.to_f if result
end
end
def bundler_lock_file_path
@config.bundler_lock_file_path
end
end
# If all else fails, a default version will be picked.
class Default < Source
def name
'default'
end
private
def find_version
DEFAULT_VERSION
end
end
def self.supported_versions
KNOWN_RUBIES
end
SOURCES = [RuboCopConfig, RubyVersionFile, BundlerLockFile, Default].freeze
private_constant :SOURCES
def initialize(config)
@config = config
end
def source
@source ||= SOURCES.each.lazy.map { |c| c.new(@config) }.detect(&:version)
end
def version
source.version
end
def supported?
KNOWN_RUBIES.include?(version)
end
def rubocop_version_with_support
if supported?
RuboCop::Version.version
else
OBSOLETE_RUBIES[version]
end
end
end
end