-
-
Notifications
You must be signed in to change notification settings - Fork 3k
/
gem_version.rb
121 lines (102 loc) · 3.07 KB
/
gem_version.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
# frozen_string_literal: true
module RuboCop
module Cop
module Bundler
# Enforce that Gem version specifications are either required
# or forbidden.
#
# @example EnforcedStyle: required (default)
# # bad
# gem 'rubocop'
#
# # good
# gem 'rubocop', '~> 1.12'
#
# # good
# gem 'rubocop', '>= 1.10.0'
#
# # good
# gem 'rubocop', '>= 1.5.0', '< 1.10.0'
#
# @example EnforcedStyle: forbidden
# # good
# gem 'rubocop'
#
# # bad
# gem 'rubocop', '~> 1.12'
#
# # bad
# gem 'rubocop', '>= 1.10.0'
#
# # bad
# gem 'rubocop', '>= 1.5.0', '< 1.10.0'
#
class GemVersion < Base
include ConfigurableEnforcedStyle
include GemDeclaration
REQUIRED_MSG = 'Gem version specification is required.'
FORBIDDEN_MSG = 'Gem version specification is forbidden.'
VERSION_SPECIFICATION_REGEX = /^\s*[~<>=]*\s*[0-9.]+/.freeze
# @!method includes_version_specification?(node)
def_node_matcher :includes_version_specification?, <<~PATTERN
(send nil? :gem <(str #version_specification?) ...>)
PATTERN
# @!method with_git_ref?(node)
def_node_matcher :with_git_ref?, <<~PATTERN
(send nil? :gem <(hash <#git? #tag_ref? ...>) ...>)
PATTERN
# @!method tag_ref?(node)
def_node_matcher :tag_ref?, <<~PATTERN
(pair (sym {:tag :ref}) (str _))
PATTERN
# @!method git?(node)
def_node_matcher :git?, <<~PATTERN
(pair (sym {:git :github :bitbucket}) (str _))
PATTERN
def on_send(node)
return unless gem_declaration?(node)
return if allowed_gem?(node)
if offense?(node)
add_offense(node)
opposite_style_detected
else
correct_style_detected
end
end
private
def allowed_gem?(node)
allowed_gems.include?(node.first_argument.value)
end
def allowed_gems
Array(cop_config['AllowedGems'])
end
def message(range)
gem_specification = range.source
if required_style?
format(REQUIRED_MSG, gem_specification: gem_specification)
elsif forbidden_style?
format(FORBIDDEN_MSG, gem_specification: gem_specification)
end
end
def offense?(node)
required_offense?(node) || forbidden_offense?(node)
end
def required_offense?(node)
required_style? && !includes_version_specification?(node) && !with_git_ref?(node)
end
def forbidden_offense?(node)
forbidden_style? && includes_version_specification?(node)
end
def forbidden_style?
style == :forbidden
end
def required_style?
style == :required
end
def version_specification?(expression)
expression.match?(VERSION_SPECIFICATION_REGEX)
end
end
end
end
end