momocop 0.1.9 → 0.1.11
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/README.ja.md +20 -0
- data/README.ja.md.erb +9 -1
- data/README.md +20 -0
- data/README.md.erb +9 -1
- data/lib/momocop/association_extractor.rb +7 -1
- data/lib/momocop/version.rb +1 -1
- data/lib/rubocop/cop/momocop/factory_bot_class_existence.rb +0 -1
- data/lib/rubocop/cop/momocop/factory_bot_consistent_file_name.rb +53 -0
- data/lib/rubocop/cop/momocop/factory_bot_missing_associations.rb +5 -1
- data/lib/rubocop/cop/momocop/factory_bot_single_define_per_file.rb +54 -0
- data/lib/rubocop/cop/momocop/factory_bot_single_factory_per_define.rb +89 -0
- data/lib/rubocop/cop/momocop/factory_bot_singular_factory_name.rb +57 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03e06c385a3b8b502f7fff3ec4f7770e330e9abfaeec326c7a1b30f52aa4b694
|
4
|
+
data.tar.gz: 1bd4131b3dac0997bff3c594f97490e806fbe441fc327e36be2581815bb47358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f88c00415baea87b4a529b2dc96653a8d3753edd989e378251a6bcc17807e20ed95630392d5c788e56fdcb88fbb696f65d571155753b5c1ac642f390beb9d87
|
7
|
+
data.tar.gz: bbf69bb7e5062676c19cf88d8dfcb04d821edc6abd6930b88cd52c12a0c7b6c999c307748df0f2b688f2a47b421c2d5b157791db9302f74741a859d6c78d7d08
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
data/lib/momocop/version.rb
CHANGED
@@ -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
|
-
|
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)
|
@@ -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.
|
4
|
+
version: 0.1.11
|
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-
|
11
|
+
date: 2024-02-21 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:
|