rubocop-nueca 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rubocop/cop/deprecated/auto_time_definition.rb +24 -0
- data/lib/rubocop/cop/deprecated/browser_driver.rb +22 -0
- data/lib/rubocop/cop/deprecated/mongoid_needs.rb +80 -0
- data/lib/rubocop/cop/rails/model_association_missing_through.rb +99 -0
- data/lib/rubocop/cop/rails/model_association_sorting.rb +56 -5
- data/lib/rubocop/nueca/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cd3d37c24acab5f66947faec9dde637103acfa42f660988f38ea7354e605f2c
|
4
|
+
data.tar.gz: 788b97be9c8bed6dfbcf2782ee80b779e2dcda463a693a1a2bff8faadb91ef4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34d5421a39b74e48d9eb999fe7ef10dd45b6e2868a6f6d2635fa90ecf2f85e1920ab092ff766be2ab920c342d539de2ff31e82945b92f42f3f1330a03d58a188
|
7
|
+
data.tar.gz: 54a9044c2ee9c452c59478be3783402d9b161112b34e06fa11e69b2ff0b4aab1f26afb0fb23aad37faf8cf3b657f412245fd1631db04f80732966be6bf02ae0c
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
class AutoTimeDefinition < RuboCop::Cop::Base
|
7
|
+
MSG = 'Use autotime(...) helper instead of using Date, DateTime or Time.'
|
8
|
+
RESTRICT_ON_SEND = [:new, :today, :now].freeze
|
9
|
+
|
10
|
+
def_node_matcher :datetime_usage, <<~PATTERN
|
11
|
+
(send
|
12
|
+
(const nil? {:DateTime | :Time | :Date}) ...)
|
13
|
+
PATTERN
|
14
|
+
|
15
|
+
def on_send(node)
|
16
|
+
return unless node.source_range.source_buffer.name.include?('_spec.rb')
|
17
|
+
return unless datetime_usage(node)
|
18
|
+
|
19
|
+
add_offense(node)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Capybara
|
6
|
+
class BrowserDriver < ::RuboCop::Cop::Base
|
7
|
+
MSG = 'Use default driver instead of :browser.'
|
8
|
+
|
9
|
+
def_node_matcher :browser_usage, <<~PATTERN
|
10
|
+
(send _ _ _ (sym :browser) ...)
|
11
|
+
PATTERN
|
12
|
+
|
13
|
+
def on_send(node)
|
14
|
+
return unless node.source_range.source_buffer.name.include?('_spec.rb')
|
15
|
+
return unless browser_usage(node)
|
16
|
+
|
17
|
+
add_offense(node)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
class MongoidNeeds < RuboCop::Cop::Base
|
7
|
+
extend RuboCop::Cop::AutoCorrector
|
8
|
+
|
9
|
+
SAFETY = :unsafe
|
10
|
+
MSG = 'Add `needs: :mongoid` to RSpec.describe when requiring mongoid_helper.'
|
11
|
+
|
12
|
+
def_node_matcher :require_mongoid_helper, <<~PATTERN
|
13
|
+
(send nil? :require (str "mongoid_helper"))
|
14
|
+
PATTERN
|
15
|
+
|
16
|
+
def_node_matcher :rspec_describe_with_needs, <<~PATTERN
|
17
|
+
(send
|
18
|
+
(const nil? :RSpec) :describe
|
19
|
+
_
|
20
|
+
(hash <(pair (sym :needs) (sym :mongoid)) ...>))
|
21
|
+
PATTERN
|
22
|
+
|
23
|
+
def_node_matcher :rspec_describe_without_needs, <<~PATTERN
|
24
|
+
(send
|
25
|
+
(const nil? :RSpec) :describe
|
26
|
+
_
|
27
|
+
$...)
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
def on_send(node)
|
31
|
+
buffer = node.source_range.source_buffer
|
32
|
+
return unless buffer.name.include?('_spec.rb')
|
33
|
+
return unless require_mongoid_helper(node)
|
34
|
+
|
35
|
+
root_node = processed_source.ast
|
36
|
+
describe_node = find_rspec_describe(root_node)
|
37
|
+
|
38
|
+
return unless describe_node
|
39
|
+
return if rspec_describe_with_needs(describe_node)
|
40
|
+
|
41
|
+
add_offense(describe_node, message: MSG) do |corrector|
|
42
|
+
autocorrect(corrector, describe_node)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def find_rspec_describe(node)
|
49
|
+
return node if rspec_describe_without_needs(node)
|
50
|
+
|
51
|
+
node.children.each do |child|
|
52
|
+
next unless child.is_a?(Parser::AST::Node)
|
53
|
+
|
54
|
+
result = find_rspec_describe(child)
|
55
|
+
return result if result
|
56
|
+
end
|
57
|
+
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def autocorrect(corrector, node)
|
62
|
+
args = node.arguments
|
63
|
+
last_arg = args.last
|
64
|
+
if args.size == 2 && last_arg.hash_type?
|
65
|
+
hash_node = last_arg
|
66
|
+
last_pair = hash_node.pairs.last
|
67
|
+
|
68
|
+
if last_pair
|
69
|
+
corrector.insert_after(last_pair, ', needs: :mongoid')
|
70
|
+
else
|
71
|
+
corrector.insert_after(hash_node.loc.begin, 'needs: :mongoid')
|
72
|
+
end
|
73
|
+
else
|
74
|
+
corrector.insert_after(last_arg, ', needs: :mongoid')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
class ModelAssociationMissingThrough < RuboCop::Cop::Base
|
7
|
+
MSG = 'Association %<association>s references through %<through>s, ' \
|
8
|
+
'but %<through>s is not defined in this model.'
|
9
|
+
ASSOCIATION_METHODS = [
|
10
|
+
:has_one,
|
11
|
+
:has_many,
|
12
|
+
:has_and_belongs_to_many
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
def on_class(node)
|
16
|
+
return unless model_class?(node)
|
17
|
+
|
18
|
+
associations = find_associations(node)
|
19
|
+
return if associations.empty?
|
20
|
+
|
21
|
+
check_missing_through_associations(associations)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def model_class?(node)
|
27
|
+
parent_class = node.parent_class
|
28
|
+
return false unless parent_class
|
29
|
+
|
30
|
+
parent_name = parent_class.const_name
|
31
|
+
['ApplicationRecord', 'ActiveRecord::Base'].include?(parent_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_associations(class_node)
|
35
|
+
associations = []
|
36
|
+
|
37
|
+
class_node.body&.each_child_node do |child|
|
38
|
+
next unless child.type == :send
|
39
|
+
next unless association_method?(child)
|
40
|
+
|
41
|
+
associations << {
|
42
|
+
node: child,
|
43
|
+
method: child.method_name,
|
44
|
+
name: association_name(child),
|
45
|
+
through: association_through(child)
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
associations
|
50
|
+
end
|
51
|
+
|
52
|
+
def association_method?(node)
|
53
|
+
return false unless node.receiver.nil?
|
54
|
+
|
55
|
+
ASSOCIATION_METHODS.include?(node.method_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def association_name(node)
|
59
|
+
first_arg = node.arguments.first
|
60
|
+
return nil unless first_arg&.sym_type?
|
61
|
+
|
62
|
+
first_arg.value.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def association_through(node)
|
66
|
+
options_hash = find_options_hash(node)
|
67
|
+
return nil unless options_hash
|
68
|
+
|
69
|
+
through_pair = options_hash.pairs.find do |pair|
|
70
|
+
pair.key.sym_type? && pair.key.value == :through
|
71
|
+
end
|
72
|
+
|
73
|
+
return nil unless through_pair&.value&.sym_type?
|
74
|
+
|
75
|
+
through_pair.value.value.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_options_hash(node)
|
79
|
+
node.arguments.find(&:hash_type?)
|
80
|
+
end
|
81
|
+
|
82
|
+
def check_missing_through_associations(associations)
|
83
|
+
defined_associations = associations.to_set { |assoc| assoc[:name] }
|
84
|
+
|
85
|
+
associations.each do |assoc|
|
86
|
+
next unless assoc[:through]
|
87
|
+
|
88
|
+
next if defined_associations.include?(assoc[:through])
|
89
|
+
|
90
|
+
message = format(MSG,
|
91
|
+
association: assoc[:name],
|
92
|
+
through: assoc[:through])
|
93
|
+
add_offense(assoc[:node], message: message)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -43,6 +43,7 @@ module RuboCop
|
|
43
43
|
node: child,
|
44
44
|
method: child.method_name,
|
45
45
|
name: association_name(child),
|
46
|
+
through: association_through(child),
|
46
47
|
start_line: source_range.line,
|
47
48
|
end_line: source_range.last_line
|
48
49
|
}
|
@@ -64,6 +65,23 @@ module RuboCop
|
|
64
65
|
first_arg.value.to_s
|
65
66
|
end
|
66
67
|
|
68
|
+
def association_through(node)
|
69
|
+
options_hash = find_options_hash(node)
|
70
|
+
return nil unless options_hash
|
71
|
+
|
72
|
+
through_pair = options_hash.pairs.find do |pair|
|
73
|
+
pair.key.sym_type? && pair.key.value == :through
|
74
|
+
end
|
75
|
+
|
76
|
+
return nil unless through_pair&.value&.sym_type?
|
77
|
+
|
78
|
+
through_pair.value.value.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_options_hash(node)
|
82
|
+
node.arguments.find(&:hash_type?)
|
83
|
+
end
|
84
|
+
|
67
85
|
def check_association_sorting(associations)
|
68
86
|
grouped = associations.group_by { |assoc| assoc[:method] }
|
69
87
|
|
@@ -75,16 +93,49 @@ module RuboCop
|
|
75
93
|
end
|
76
94
|
|
77
95
|
def check_group_sorting(group_associations)
|
78
|
-
|
79
|
-
|
96
|
+
# Separate associations with and without through
|
97
|
+
through_associations = group_associations.select { |assoc| assoc[:through] }
|
98
|
+
regular_associations = group_associations.reject { |assoc| assoc[:through] }
|
99
|
+
|
100
|
+
# Sort regular associations alphabetically
|
101
|
+
regular_sorted = regular_associations.sort_by { |assoc| assoc[:name] }
|
102
|
+
|
103
|
+
# Build expected order respecting through dependencies
|
104
|
+
expected_order = build_expected_order(regular_sorted, through_associations)
|
105
|
+
actual_order = group_associations.map { |assoc| assoc[:name] }
|
80
106
|
|
81
|
-
return if
|
107
|
+
return if actual_order == expected_order
|
82
108
|
|
83
|
-
|
84
|
-
message = format(MSG, expected:
|
109
|
+
expected_order_str = expected_order.join(', ')
|
110
|
+
message = format(MSG, expected: expected_order_str)
|
85
111
|
|
86
112
|
add_offense(group_associations.first[:node], message: message)
|
87
113
|
end
|
114
|
+
|
115
|
+
def build_expected_order(regular_associations, through_associations)
|
116
|
+
expected = []
|
117
|
+
|
118
|
+
# Add regular associations first, sorted alphabetically
|
119
|
+
regular_associations.each do |assoc|
|
120
|
+
expected << assoc[:name]
|
121
|
+
|
122
|
+
# Add any through associations that depend on this one
|
123
|
+
dependent_through = through_associations.select { |ta| ta[:through] == assoc[:name] }
|
124
|
+
dependent_through.sort_by { |ta| ta[:name] }.each do |ta|
|
125
|
+
expected << ta[:name]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add any through associations that don't have dependencies in this group
|
130
|
+
orphaned_through = through_associations.reject do |ta|
|
131
|
+
regular_associations.any? { |ra| ra[:name] == ta[:through] }
|
132
|
+
end
|
133
|
+
orphaned_through.sort_by { |ta| ta[:name] }.each do |ta|
|
134
|
+
expected << ta[:name]
|
135
|
+
end
|
136
|
+
|
137
|
+
expected
|
138
|
+
end
|
88
139
|
end
|
89
140
|
end
|
90
141
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-nueca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tien
|
@@ -138,11 +138,15 @@ files:
|
|
138
138
|
- README.md
|
139
139
|
- config/default.yml
|
140
140
|
- lib/rubocop-nueca.rb
|
141
|
+
- lib/rubocop/cop/deprecated/auto_time_definition.rb
|
142
|
+
- lib/rubocop/cop/deprecated/browser_driver.rb
|
143
|
+
- lib/rubocop/cop/deprecated/mongoid_needs.rb
|
141
144
|
- lib/rubocop/cop/rails/date_time_conversion.rb
|
142
145
|
- lib/rubocop/cop/rails/date_time_current.rb
|
143
146
|
- lib/rubocop/cop/rails/migration_table_variable.rb
|
144
147
|
- lib/rubocop/cop/rails/model_association_consistent_spacing.rb
|
145
148
|
- lib/rubocop/cop/rails/model_association_grouping.rb
|
149
|
+
- lib/rubocop/cop/rails/model_association_missing_through.rb
|
146
150
|
- lib/rubocop/cop/rails/model_association_scattering.rb
|
147
151
|
- lib/rubocop/cop/rails/model_association_separation.rb
|
148
152
|
- lib/rubocop/cop/rails/model_association_sorting.rb
|