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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a58e87bfcf5ac18b1e87c51da6a3fd4b37e53c664ec86518ae05c5275b446b2
4
- data.tar.gz: 9f08cc06b6f03c0bd16ab76fd3b66e2540f2e4c1ba06d23a02f0bffe49c778fa
3
+ metadata.gz: 6cd3d37c24acab5f66947faec9dde637103acfa42f660988f38ea7354e605f2c
4
+ data.tar.gz: 788b97be9c8bed6dfbcf2782ee80b779e2dcda463a693a1a2bff8faadb91ef4f
5
5
  SHA512:
6
- metadata.gz: 436c241836f68bd0136f66d6639ea08c2d8e935583609a421c1045222903aff380a4553df6169dd9131e7e6fddeaf82110a003e4050f0966c1955bbcaa8d3d33
7
- data.tar.gz: 27fb4e271c37e2ee980669e81f8c0237d3314a3fc77ba0310bc2401c75430b99cbe765991c418b848432c3ff20de036e338c09a86d26370171368bcac46aedae
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
- names = group_associations.map { |assoc| assoc[:name] }
79
- sorted_names = names.sort
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 names == sorted_names
107
+ return if actual_order == expected_order
82
108
 
83
- expected_order = sorted_names.join(', ')
84
- message = format(MSG, expected: expected_order)
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Nueca
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  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.0.0
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