momocop 0.1.12 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acef3c7f38f87034b619c4b5e9ca7ca77ac880a0a665f46abff3a19455ef2d5f
4
- data.tar.gz: 4e06a90596825134bdad568b41b38ebdb93056d0fab4f58d381c8ffd6d16e6e5
3
+ metadata.gz: 125920382da157b5f151d8d7de60a05df4943fbbdf521db3b082001891f7af25
4
+ data.tar.gz: 8860b388475b79b0eec94ee28e9635a2c288b662bdcdbe33a5172b8959d969c9
5
5
  SHA512:
6
- metadata.gz: 7c18582cdb50926ee4bbc7074c076d90f06504cdec64d0dc4a1389dadac1ad000864cdcea4e9e4322a7fcc2bc6bbb8aadd381a29cef2e4bdee1dee3f751833a9
7
- data.tar.gz: 6ca756d9793ca03ef4ca8ea88e37d9169bea071641464af88f36e96688a613fa5dc09c3e50b252c56e79e1cf9376f46db1717e0b4dfa7d46be8352fbb656582a
6
+ metadata.gz: f538d288ad3858eb9c5c6bbd0c6576b6e8bd0811282da99215b295426e83b4d51f5b111fa256d1c6246eb1577209a114d8745c28c0568e8fc93b5f774774b93b
7
+ data.tar.gz: e49a4e8e1ab9ad2ce341566257730d7eab510002319a8c4c48db179f2be5dbdfc6ee7ddecec9552debef73360729546e6eb954eee14c2489f8c8037f950ac891
data/README.ja.md CHANGED
@@ -30,6 +30,8 @@ Momocop/FactoryBotClassExistence:
30
30
  Enabled: true
31
31
  Momocop/FactoryBotConsistentFileName:
32
32
  Enabled: true
33
+ Momocop/FactoryBotInlineAssociation:
34
+ Enabled: true
33
35
  Momocop/FactoryBotMissingAssociations:
34
36
  Enabled: true
35
37
  Momocop/FactoryBotMissingClassOption:
@@ -52,6 +54,7 @@ Momocop/FactoryBotSingularFactoryName:
52
54
  |---|:-:|:-:|
53
55
  |[`Momocop/FactoryBotClassExistence`](lib/rubocop/cop/momocop/factory_bot_class_existence.rb)|:white_check_mark:|:white_check_mark:|
54
56
  |[`Momocop/FactoryBotConsistentFileName`](lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb)|:white_check_mark:|:white_check_mark:|
57
+ |[`Momocop/FactoryBotInlineAssociation`](lib/rubocop/cop/momocop/factory_bot_inline_association.rb)|:white_check_mark:|:white_check_mark:|
55
58
  |[`Momocop/FactoryBotMissingAssociations`](lib/rubocop/cop/momocop/factory_bot_missing_associations.rb)|:white_check_mark:|:white_check_mark:|
56
59
  |[`Momocop/FactoryBotMissingClassOption`](lib/rubocop/cop/momocop/factory_bot_missing_class_option.rb)|:white_check_mark:|:white_check_mark:|
57
60
  |[`Momocop/FactoryBotMissingProperties`](lib/rubocop/cop/momocop/factory_bot_missing_properties.rb)|:white_check_mark:|:white_check_mark:|
data/README.md CHANGED
@@ -30,6 +30,8 @@ Momocop/FactoryBotClassExistence:
30
30
  Enabled: true
31
31
  Momocop/FactoryBotConsistentFileName:
32
32
  Enabled: true
33
+ Momocop/FactoryBotInlineAssociation:
34
+ Enabled: true
33
35
  Momocop/FactoryBotMissingAssociations:
34
36
  Enabled: true
35
37
  Momocop/FactoryBotMissingClassOption:
@@ -52,6 +54,7 @@ Momocop/FactoryBotSingularFactoryName:
52
54
  |---|:-:|:-:|
53
55
  |[`Momocop/FactoryBotClassExistence`](lib/rubocop/cop/momocop/factory_bot_class_existence.rb)|:white_check_mark:|:white_check_mark:|
54
56
  |[`Momocop/FactoryBotConsistentFileName`](lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb)|:white_check_mark:|:white_check_mark:|
57
+ |[`Momocop/FactoryBotInlineAssociation`](lib/rubocop/cop/momocop/factory_bot_inline_association.rb)|:white_check_mark:|:white_check_mark:|
55
58
  |[`Momocop/FactoryBotMissingAssociations`](lib/rubocop/cop/momocop/factory_bot_missing_associations.rb)|:white_check_mark:|:white_check_mark:|
56
59
  |[`Momocop/FactoryBotMissingClassOption`](lib/rubocop/cop/momocop/factory_bot_missing_class_option.rb)|:white_check_mark:|:white_check_mark:|
57
60
  |[`Momocop/FactoryBotMissingProperties`](lib/rubocop/cop/momocop/factory_bot_missing_properties.rb)|:white_check_mark:|:white_check_mark:|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Momocop
4
- VERSION = '0.1.12'
4
+ VERSION = '0.1.14'
5
5
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Momocop
6
+ # Enforces inline association definitions in FactoryBot factories.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # factory :blog_post do
11
+ # association(:user)
12
+ # end
13
+ #
14
+ # # good
15
+ # factory :blog_post do
16
+ # user { association :user }
17
+ # end
18
+ class FactoryBotInlineAssociation < RuboCop::Cop::Base
19
+ extend AutoCorrector
20
+
21
+ MSG = 'Use inline association definition instead of separate `association` method call.'
22
+
23
+ RESTRICT_ON_SEND = %i[association].freeze
24
+
25
+ def_node_matcher :association_call?, <<-PATTERN
26
+ (send nil? :association (sym _))
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ return unless inside_factory_bot_define?(node)
31
+ return unless inside_factory_bot_factory?(node)
32
+
33
+ add_offense(node.loc.selector, message: MSG) do |corrector|
34
+ association_name = node.arguments.first.value
35
+ options = node.arguments.at(1)
36
+
37
+ convert_options(options) in {
38
+ factory_option:, rest_options:
39
+ }
40
+ factory = factory_option&.source || ":#{association_name}"
41
+ replacement =
42
+ if rest_options.empty?
43
+ "#{association_name} { association #{factory} }"
44
+ else
45
+ rest_options_source = rest_options.map(&:source).join(', ')
46
+ "#{association_name} { association #{factory}, #{rest_options_source} }"
47
+ end
48
+ corrector.replace(node, replacement)
49
+ end
50
+ end
51
+
52
+ # `association :foo, factory: :bar, baz: 1 ...` => `foo { association :bar, baz: 1 ...}`
53
+ private def convert_options(options)
54
+ factory_option = options&.pairs&.find { |pair| pair.key.value == :factory }&.value
55
+ rest_options = options&.pairs&.reject { |pair| pair.key.value == :factory } || []
56
+
57
+ return {
58
+ factory_option:,
59
+ rest_options:
60
+ }
61
+ end
62
+
63
+ private def inside_factory_bot_factory?(node)
64
+ context = node.each_ancestor(:block).first
65
+ send_node = context.block_type? ? context.send_node : context
66
+
67
+ return send_node.method_name == :factory
68
+ end
69
+
70
+ private def inside_factory_bot_define?(node)
71
+ ancestors = node.each_ancestor(:block).to_a
72
+ ancestors.any? { |ancestor| ancestor.method_name == :define && ancestor.receiver&.const_name == 'FactoryBot' }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -112,12 +112,29 @@ module RuboCop
112
112
  return [] unless body_node
113
113
 
114
114
  associations = association_definitions(body_node)
115
- association_names = associations&.map(&:first_argument)&.map(&:value)&.map(&:to_s)
115
+ association_names = associations&.map { |node| get_association_name(node) }
116
116
  return association_names
117
117
  end
118
118
 
119
+ private def get_association_name(node)
120
+ # Explicit definition
121
+ return node.arguments.first.value.to_s if inside_factory_bot_factory?(node)
122
+
123
+ # Inline definition
124
+ context = node.each_ancestor(:block).first
125
+ send_node = context.block_type? ? context.send_node : context
126
+ return send_node.method_name.to_s
127
+ end
128
+
129
+ private def inside_factory_bot_factory?(node)
130
+ context = node.each_ancestor(:block).first
131
+ send_node = context.block_type? ? context.send_node : context
132
+
133
+ return send_node.method_name == :factory
134
+ end
135
+
119
136
  private def generate_association_definition(property)
120
- "association(:#{property})"
137
+ "#{property} { association :#{property} }"
121
138
  end
122
139
 
123
140
  private def inside_factory_bot_define?(node)
@@ -116,8 +116,17 @@ module RuboCop
116
116
 
117
117
  private def definition_type(node)
118
118
  send_node = node.send_type? ? node : node.children.first
119
+ has_association_body =
120
+ send_node
121
+ .block_node
122
+ &.children
123
+ &.last
124
+ &.children
125
+ &.any? { _1.is_a?(Parser::AST::Node) && _1.send_type? && _1.method_name == :association }
119
126
  if %i[association sequence].include? send_node.method_name
120
127
  send_node.method_name
128
+ elsif has_association_body
129
+ :association
121
130
  else
122
131
  :property
123
132
  end
@@ -33,7 +33,6 @@ module RuboCop
33
33
  return if factory_name.singularize == factory_name
34
34
 
35
35
  add_offense(node.first_argument) do |corrector|
36
- p node.first_argument
37
36
  if node.first_argument.type == :sym
38
37
  corrector.replace(node.first_argument.loc.expression, ":#{factory_name.singularize}")
39
38
  elsif node.first_argument.type == :str
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: momocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - supermomonga
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-22 00:00:00.000000000 Z
11
+ date: 2024-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -93,6 +93,7 @@ files:
93
93
  - lib/momocop/version.rb
94
94
  - lib/rubocop/cop/momocop/factory_bot_class_existence.rb
95
95
  - lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb
96
+ - lib/rubocop/cop/momocop/factory_bot_inline_association.rb
96
97
  - lib/rubocop/cop/momocop/factory_bot_missing_associations.rb
97
98
  - lib/rubocop/cop/momocop/factory_bot_missing_class_option.rb
98
99
  - lib/rubocop/cop/momocop/factory_bot_missing_properties.rb