momocop 0.1.10 → 0.1.12

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: 65a6e94746e124b9b4b7faa9f87effebbbe8f75f7a23ac9f80ee71ef0fee674c
4
- data.tar.gz: e32490d4c0fc3e2f02d96ad154db9ff52ef17f56ba993ac7260bc04169fa0e01
3
+ metadata.gz: acef3c7f38f87034b619c4b5e9ca7ca77ac880a0a665f46abff3a19455ef2d5f
4
+ data.tar.gz: 4e06a90596825134bdad568b41b38ebdb93056d0fab4f58d381c8ffd6d16e6e5
5
5
  SHA512:
6
- metadata.gz: 3aae3b5142feb84c7bc741a0d988e715c9101fc16f362e71325abbe330a7952e7ccaaa1ec99f260227b5e14c967341c57a3855810da3034d12e04dadb0c2dbaa
7
- data.tar.gz: 31a58eb60a8f1cd43dadd62d0be672620157070f00b441171db634fdd24d65b0b6967c75cf743bff77a1ae5baa4776a2d84147f7b8b99ca86f5e5087effd019e
6
+ metadata.gz: 7c18582cdb50926ee4bbc7074c076d90f06504cdec64d0dc4a1389dadac1ad000864cdcea4e9e4322a7fcc2bc6bbb8aadd381a29cef2e4bdee1dee3f751833a9
7
+ data.tar.gz: 6ca756d9793ca03ef4ca8ea88e37d9169bea071641464af88f36e96688a613fa5dc09c3e50b252c56e79e1cf9376f46db1717e0b4dfa7d46be8352fbb656582a
data/README.ja.md CHANGED
@@ -26,8 +26,24 @@ gem 'momocop', require: false
26
26
  require:
27
27
  - momocop
28
28
 
29
+ Momocop/FactoryBotClassExistence:
30
+ Enabled: true
31
+ Momocop/FactoryBotConsistentFileName:
32
+ Enabled: true
29
33
  Momocop/FactoryBotMissingAssociations:
30
34
  Enabled: true
35
+ Momocop/FactoryBotMissingClassOption:
36
+ Enabled: true
37
+ Momocop/FactoryBotMissingProperties:
38
+ Enabled: true
39
+ Momocop/FactoryBotPropertyOrder:
40
+ Enabled: true
41
+ Momocop/FactoryBotSingleDefinePerFile:
42
+ Enabled: true
43
+ Momocop/FactoryBotSingleFactoryPerDefine:
44
+ Enabled: true
45
+ Momocop/FactoryBotSingularFactoryName:
46
+ Enabled: true
31
47
  ```
32
48
 
33
49
  ## Cop一覧
@@ -35,10 +51,14 @@ Momocop/FactoryBotMissingAssociations:
35
51
  |Cop|Rails|FactoryBot|
36
52
  |---|:-:|:-:|
37
53
  |[`Momocop/FactoryBotClassExistence`](lib/rubocop/cop/momocop/factory_bot_class_existence.rb)|:white_check_mark:|:white_check_mark:|
54
+ |[`Momocop/FactoryBotConsistentFileName`](lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb)|:white_check_mark:|:white_check_mark:|
38
55
  |[`Momocop/FactoryBotMissingAssociations`](lib/rubocop/cop/momocop/factory_bot_missing_associations.rb)|:white_check_mark:|:white_check_mark:|
39
56
  |[`Momocop/FactoryBotMissingClassOption`](lib/rubocop/cop/momocop/factory_bot_missing_class_option.rb)|:white_check_mark:|:white_check_mark:|
40
57
  |[`Momocop/FactoryBotMissingProperties`](lib/rubocop/cop/momocop/factory_bot_missing_properties.rb)|:white_check_mark:|:white_check_mark:|
41
58
  |[`Momocop/FactoryBotPropertyOrder`](lib/rubocop/cop/momocop/factory_bot_property_order.rb)|:white_check_mark:|:white_check_mark:|
59
+ |[`Momocop/FactoryBotSingleDefinePerFile`](lib/rubocop/cop/momocop/factory_bot_single_define_per_file.rb)|:white_check_mark:|:white_check_mark:|
60
+ |[`Momocop/FactoryBotSingleFactoryPerDefine`](lib/rubocop/cop/momocop/factory_bot_single_factory_per_define.rb)|:white_check_mark:|:white_check_mark:|
61
+ |[`Momocop/FactoryBotSingularFactoryName`](lib/rubocop/cop/momocop/factory_bot_singular_factory_name.rb)|:white_check_mark:|:white_check_mark:|
42
62
 
43
63
  ## 貢献
44
64
 
data/README.ja.md.erb CHANGED
@@ -26,8 +26,16 @@ gem 'momocop', require: false
26
26
  require:
27
27
  - momocop
28
28
 
29
- Momocop/FactoryBotMissingAssociations:
29
+ <%
30
+ files = Dir.glob('lib/rubocop/cop/momocop/*.rb').sort_by { |f| File.basename(f)}
31
+ files.each do |cop_file_path|
32
+ base_name = File.basename(cop_file_path, '.rb')
33
+ cop_class_name = "Momocop/#{base_name.camelize}"
34
+
35
+ -%>
36
+ <%= cop_class_name %>:
30
37
  Enabled: true
38
+ <% end -%>
31
39
  ```
32
40
 
33
41
  ## Cop一覧
data/README.md CHANGED
@@ -26,8 +26,24 @@ All cops are disabled by default.
26
26
  require:
27
27
  - momocop
28
28
 
29
+ Momocop/FactoryBotClassExistence:
30
+ Enabled: true
31
+ Momocop/FactoryBotConsistentFileName:
32
+ Enabled: true
29
33
  Momocop/FactoryBotMissingAssociations:
30
34
  Enabled: true
35
+ Momocop/FactoryBotMissingClassOption:
36
+ Enabled: true
37
+ Momocop/FactoryBotMissingProperties:
38
+ Enabled: true
39
+ Momocop/FactoryBotPropertyOrder:
40
+ Enabled: true
41
+ Momocop/FactoryBotSingleDefinePerFile:
42
+ Enabled: true
43
+ Momocop/FactoryBotSingleFactoryPerDefine:
44
+ Enabled: true
45
+ Momocop/FactoryBotSingularFactoryName:
46
+ Enabled: true
31
47
  ```
32
48
 
33
49
  ## Cops
@@ -35,10 +51,14 @@ Momocop/FactoryBotMissingAssociations:
35
51
  |Cop|Rails|FactoryBot|
36
52
  |---|:-:|:-:|
37
53
  |[`Momocop/FactoryBotClassExistence`](lib/rubocop/cop/momocop/factory_bot_class_existence.rb)|:white_check_mark:|:white_check_mark:|
54
+ |[`Momocop/FactoryBotConsistentFileName`](lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb)|:white_check_mark:|:white_check_mark:|
38
55
  |[`Momocop/FactoryBotMissingAssociations`](lib/rubocop/cop/momocop/factory_bot_missing_associations.rb)|:white_check_mark:|:white_check_mark:|
39
56
  |[`Momocop/FactoryBotMissingClassOption`](lib/rubocop/cop/momocop/factory_bot_missing_class_option.rb)|:white_check_mark:|:white_check_mark:|
40
57
  |[`Momocop/FactoryBotMissingProperties`](lib/rubocop/cop/momocop/factory_bot_missing_properties.rb)|:white_check_mark:|:white_check_mark:|
41
58
  |[`Momocop/FactoryBotPropertyOrder`](lib/rubocop/cop/momocop/factory_bot_property_order.rb)|:white_check_mark:|:white_check_mark:|
59
+ |[`Momocop/FactoryBotSingleDefinePerFile`](lib/rubocop/cop/momocop/factory_bot_single_define_per_file.rb)|:white_check_mark:|:white_check_mark:|
60
+ |[`Momocop/FactoryBotSingleFactoryPerDefine`](lib/rubocop/cop/momocop/factory_bot_single_factory_per_define.rb)|:white_check_mark:|:white_check_mark:|
61
+ |[`Momocop/FactoryBotSingularFactoryName`](lib/rubocop/cop/momocop/factory_bot_singular_factory_name.rb)|:white_check_mark:|:white_check_mark:|
42
62
 
43
63
  ## Contributing
44
64
 
data/README.md.erb CHANGED
@@ -26,8 +26,16 @@ All cops are disabled by default.
26
26
  require:
27
27
  - momocop
28
28
 
29
- Momocop/FactoryBotMissingAssociations:
29
+ <%
30
+ files = Dir.glob('lib/rubocop/cop/momocop/*.rb').sort_by { |f| File.basename(f)}
31
+ files.each do |cop_file_path|
32
+ base_name = File.basename(cop_file_path, '.rb')
33
+ cop_class_name = "Momocop/#{base_name.camelize}"
34
+
35
+ -%>
36
+ <%= cop_class_name %>:
30
37
  Enabled: true
38
+ <% end -%>
31
39
  ```
32
40
 
33
41
  ## Cops
@@ -32,9 +32,15 @@ module Momocop
32
32
  option_hash = node.children[3]
33
33
  option_hash&.children&.each do |option|
34
34
  key_node, value_node = option.children
35
- if value_node.type == :sym || value_node.type == :str
35
+ # rubocop:disable Lint/BooleanSymbol
36
+ if %i[sym str].include?(value_node.type)
36
37
  options[key_node.children[0]] = value_node.children[0]
38
+ elsif value_node.type == :true
39
+ options[key_node.children[0]] = true
40
+ elsif value_node.type == :false
41
+ options[key_node.children[0]] = false
37
42
  end
43
+ # rubocop:enable Lint/BooleanSymbol
38
44
  end
39
45
  end
40
46
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Momocop
4
- VERSION = '0.1.10'
4
+ VERSION = '0.1.12'
5
5
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Momocop
6
+ # Ensures that FactoryBot factory names match their filenames.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # # in a file named user.rb
11
+ # FactoryBot.define do
12
+ # factory :admin_user do
13
+ # end
14
+ # end
15
+ #
16
+ # # good
17
+ # # in a file named admin_user.rb
18
+ # FactoryBot.define do
19
+ # factory :admin_user do
20
+ # end
21
+ # end
22
+ class FactoryBotConsistentFileName < RuboCop::Cop::Base
23
+ include RangeHelp
24
+
25
+ MSG = 'Factory name should match the file name.'
26
+
27
+ RESTRICT_ON_SEND = %i[factory].freeze
28
+
29
+ def_node_matcher :factory_bot_definition, <<~PATTERN
30
+ (send nil? :factory ({sym str} $_) ...)
31
+ PATTERN
32
+
33
+ def on_send(node)
34
+ factory_name = factory_bot_definition(node)
35
+ return unless factory_name
36
+
37
+ return unless inside_factory_bot_define?(node)
38
+
39
+ expected_file_name = "#{factory_name}.rb"
40
+ actual_file_name = File.basename(processed_source.file_path)
41
+ return unless actual_file_name != expected_file_name
42
+
43
+ add_offense(node.source_range, message: MSG)
44
+ end
45
+
46
+ private def inside_factory_bot_define?(node)
47
+ ancestors = node.each_ancestor(:block).to_a
48
+ ancestors.any? { |ancestor| ancestor.method_name == :define && ancestor.receiver&.const_name == 'FactoryBot' }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -100,7 +100,11 @@ module RuboCop
100
100
 
101
101
  extractor = ::Momocop::AssociationExtractor.new
102
102
  associations = extractor.extract(source)
103
- return associations.map { _1[:name].to_s }
103
+ belongs_to_associations =
104
+ associations
105
+ .select { _1[:type] == :belongs_to }
106
+ .map { _1[:name].to_s }
107
+ return belongs_to_associations
104
108
  end
105
109
 
106
110
  private def get_defined_association_names(block_node)
@@ -139,13 +139,7 @@ module RuboCop
139
139
  .select { _1[:type] == :belongs_to }
140
140
  .map { |association|
141
141
  options = association[:options]
142
- if options[:foreign_key]
143
- options[:foreign_key]
144
- elsif options[:class_name]
145
- foreign_key_name(options[:class_name])
146
- else
147
- "#{association[:name]}_id"
148
- end
142
+ options[:foreign_key] || "#{association[:name]}_id"
149
143
  }
150
144
  .compact
151
145
  return foreign_key_names.map(&:to_sym)
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Momocop
6
+ # This cop checks for multiple `FactoryBot.define` blocks in a single file.
7
+ # It's recommended to have only one `FactoryBot.define` block per file to keep
8
+ # factory definitions clear and maintainable.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # FactoryBot.define do
13
+ # factory :user do
14
+ # end
15
+ # end
16
+ #
17
+ # FactoryBot.define do
18
+ # factory :admin do
19
+ # end
20
+ # end
21
+ #
22
+ # # good
23
+ # FactoryBot.define do
24
+ # factory :user do
25
+ # end
26
+ # factory :admin do
27
+ # end
28
+ # end
29
+ class FactoryBotSingleDefinePerFile < RuboCop::Cop::Base
30
+ MSG = 'Only one `FactoryBot.define` block is allowed per file.'
31
+
32
+ def on_new_investigation
33
+ factory_bot_define_blocks =
34
+ processed_source
35
+ .ast
36
+ .descendants
37
+ .select { |node|
38
+ factory_bot_define?(node)
39
+ }
40
+
41
+ return if factory_bot_define_blocks.size <= 1
42
+
43
+ factory_bot_define_blocks[1..].each do |factory_bot_define_block|
44
+ add_offense(factory_bot_define_block, message: MSG)
45
+ end
46
+ end
47
+
48
+ private def factory_bot_define?(node)
49
+ node.send_type? && node.method_name == :define && node.receiver&.const_name == 'FactoryBot'
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Momocop
6
+ # Ensures that each file contains only one top-level FactoryBot factory.
7
+ # Nested factories within a parent factory are allowed.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # FactoryBot.define do
12
+ # factory :user do
13
+ # end
14
+ # end
15
+ #
16
+ # FactoryBot.define do
17
+ # factory :profile do
18
+ # end
19
+ # end
20
+ #
21
+ # # good
22
+ # FactoryBot.define do
23
+ # factory :user do
24
+ # end
25
+ # end
26
+ #
27
+ # # bad
28
+ # FactoryBot.define do
29
+ # factory :user do
30
+ # end
31
+ # end
32
+ #
33
+ # FactoryBot.define do
34
+ # factory :admin_user, class: ‘User' do
35
+ # end
36
+ # end
37
+ #
38
+ # # good
39
+ # FactoryBot.define do
40
+ # factory :user do
41
+ # factory :admin_user do
42
+ # end
43
+ # end
44
+ # end
45
+ class FactoryBotSingleFactoryPerDefine < RuboCop::Cop::Base
46
+ MSG = 'Only one top-level factory is allowed per FactoryBot.define.'
47
+
48
+ RESTRICT_ON_SEND = %i[define].freeze
49
+
50
+ # Define the investigation method to be called during cop processing.
51
+ def on_send(node)
52
+ return unless factory_bot_define_block?(node)
53
+
54
+ factories = top_level_factories(node)
55
+
56
+ return if factories.size <= 1
57
+
58
+ # Register an offense for each factory beyond the first one.
59
+ factories[1..].each do |factory|
60
+ add_offense(factory.loc.expression, message: MSG)
61
+ end
62
+ end
63
+
64
+ # Checks if the node is a FactoryBot definition block.
65
+ private def factory_bot_define_block?(node)
66
+ node.receiver&.const_name == 'FactoryBot'
67
+ end
68
+
69
+ # Returns all top-level factories within a FactoryBot.define block.
70
+ private def top_level_factories(node)
71
+ factory_nodes =
72
+ node
73
+ .block_node
74
+ .body.each_descendant(:send)
75
+ .select { |n| n.method_name == :factory }
76
+ factory_nodes.select { |factory_node|
77
+ base_node = factory_node.block_node || factory_node
78
+ context =
79
+ base_node
80
+ .each_ancestor(:block)
81
+ .map { _1.send_node&.method_name&.to_sym }
82
+ .find { %i[define factory].include? _1 }
83
+ context == :define
84
+ }
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/inflections'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Momocop
8
+ # Ensures that FactoryBot factories have singular names.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # FactoryBot.define do
13
+ # factory :users do
14
+ # end
15
+ # end
16
+ #
17
+ # # good
18
+ # FactoryBot.define do
19
+ # factory :user do
20
+ # end
21
+ # end
22
+ class FactoryBotSingularFactoryName < RuboCop::Cop::Base
23
+ extend AutoCorrector
24
+
25
+ MSG = 'Factory name should be singular, not plural.'
26
+
27
+ RESTRICT_ON_SEND = %i[factory].freeze
28
+
29
+ def on_send(node)
30
+ return unless inside_factory_bot_define?(node)
31
+
32
+ factory_name = node.first_argument.value.to_s
33
+ return if factory_name.singularize == factory_name
34
+
35
+ add_offense(node.first_argument) do |corrector|
36
+ p node.first_argument
37
+ if node.first_argument.type == :sym
38
+ corrector.replace(node.first_argument.loc.expression, ":#{factory_name.singularize}")
39
+ elsif node.first_argument.type == :str
40
+ if node.first_argument.source.start_with?("'")
41
+ corrector.replace(node.first_argument.loc.expression, "'#{factory_name.singularize}'")
42
+ elsif node.first_argument.source.start_with?('"')
43
+ corrector.replace(node.first_argument.loc.expression, "\"#{factory_name.singularize}\"")
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ # Checks if the node is inside a FactoryBot definition block.
50
+ private def inside_factory_bot_define?(node)
51
+ ancestors = node.each_ancestor(:block).to_a
52
+ ancestors.any? { |ancestor| ancestor.method_name == :define && ancestor.receiver&.const_name == 'FactoryBot' }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
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.10
4
+ version: 0.1.12
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-20 00:00:00.000000000 Z
11
+ date: 2024-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -92,10 +92,14 @@ files:
92
92
  - lib/momocop/enum_extractor.rb
93
93
  - lib/momocop/version.rb
94
94
  - lib/rubocop/cop/momocop/factory_bot_class_existence.rb
95
+ - lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb
95
96
  - lib/rubocop/cop/momocop/factory_bot_missing_associations.rb
96
97
  - lib/rubocop/cop/momocop/factory_bot_missing_class_option.rb
97
98
  - lib/rubocop/cop/momocop/factory_bot_missing_properties.rb
98
99
  - lib/rubocop/cop/momocop/factory_bot_property_order.rb
100
+ - lib/rubocop/cop/momocop/factory_bot_single_define_per_file.rb
101
+ - lib/rubocop/cop/momocop/factory_bot_single_factory_per_define.rb
102
+ - lib/rubocop/cop/momocop/factory_bot_singular_factory_name.rb
99
103
  - sig/momocop.rbs
100
104
  homepage: https://github.com/supermomonga/momocop
101
105
  licenses: