momocop 0.1.10 → 0.1.12

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: 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: