factory_sloth 1.3.0 → 1.4.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 +4 -4
- data/CHANGELOG.md +14 -2
- data/README.md +11 -0
- data/lib/factory_sloth/cli.rb +4 -0
- data/lib/factory_sloth/code_mod.rb +35 -20
- data/lib/factory_sloth/color.rb +5 -1
- data/lib/factory_sloth/create_call_finder.rb +19 -6
- data/lib/factory_sloth/execution_check.rb +7 -3
- data/lib/factory_sloth/version.rb +1 -1
- data/lib/factory_sloth.rb +7 -1
- metadata +3 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9c56ba87b89aec371bdf53b61c50c66a0f0c51b8ba75c4edc12245967face48
|
4
|
+
data.tar.gz: f1ead7cb06982fd34eedde94bc017dfb5612c744266d9e76fa014864fdaccc7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1016ee245df8b06fb396f12806004c6ea63f88944b3286723623906dca43be42a5b39dae87db377f9616f35f6b5b3bea2d080eaa0818a14f429f110c388c9a4f
|
7
|
+
data.tar.gz: 96c402e4c4a287e69cb641ccf281365da5ea4f64f6d1092534483b9971d93fd0196bcb22c870516b3df5b88eaabe9097ae2a12758acc28f7616b2615e56935f6
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
### Added
|
4
|
+
|
5
|
+
## [1.4.0] - 2024-10-04
|
6
|
+
|
7
|
+
- ignore underscore-prefixed assignments e.g. `_user = create(:user)`
|
8
|
+
|
9
|
+
## [1.3.1] - 2023-05-24
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
- Stop trying to patch lines with multiple create calls, which was unreliable
|
14
|
+
|
3
15
|
## [1.3.0] - 2023-05-22
|
4
16
|
|
5
17
|
### Added
|
6
18
|
|
7
|
-
-
|
8
|
-
-
|
19
|
+
- Nicer output
|
20
|
+
- Verbose mode
|
9
21
|
|
10
22
|
## [1.2.2] - 2023-05-18
|
11
23
|
|
data/README.md
CHANGED
@@ -27,6 +27,7 @@ Examples:
|
|
27
27
|
Options:
|
28
28
|
-f, --force Ignore ./.factory_sloth_done
|
29
29
|
-l, --lint Dont fix, just list bad create calls
|
30
|
+
-u, --underscore Check underscore-prefixed variables
|
30
31
|
-V, --verbose Verbose output, useful for debugging
|
31
32
|
-v, --version Show gem version
|
32
33
|
-h, --help Show this help
|
@@ -80,6 +81,16 @@ expect { User.delete_all }.to change { User.count }.from(1).to(0)
|
|
80
81
|
|
81
82
|
If you have a good idea about how to detect such cases automatically, let me know :)
|
82
83
|
|
84
|
+
### Underscore-prefixed variable names
|
85
|
+
|
86
|
+
Prefixing a variable name with an underscore is a common way of saying "I am creating this for side effects":
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
_user = create(:user)
|
90
|
+
```
|
91
|
+
|
92
|
+
Therefore `factory_sloth` ignores such cases by default. Use the `-u` flag to check such cases as well.
|
93
|
+
|
83
94
|
## Development
|
84
95
|
|
85
96
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/factory_sloth/cli.rb
CHANGED
@@ -36,6 +36,10 @@ module FactorySloth
|
|
36
36
|
FactorySloth.lint = true
|
37
37
|
end
|
38
38
|
|
39
|
+
opts.on('-u', '--underscore', 'Check underscore-prefixed variables') do
|
40
|
+
FactorySloth.check_underscore_vars = true
|
41
|
+
end
|
42
|
+
|
39
43
|
opts.on('-V', '--verbose', 'Verbose output, useful for debugging') do
|
40
44
|
FactorySloth.verbose = true
|
41
45
|
end
|
@@ -22,17 +22,7 @@ module FactorySloth
|
|
22
22
|
def call
|
23
23
|
self.create_calls = CreateCallFinder.call(code: original_code)
|
24
24
|
|
25
|
-
|
26
|
-
# given spec file to tempfiles first, and then run them all in a single
|
27
|
-
# rspec call. However, this would make it impossible to use `--fail-fast`,
|
28
|
-
# and might make examples fail that are not as idempotent as they should be.
|
29
|
-
self.changed_create_calls =
|
30
|
-
create_calls.sort_by { |call| [-call.line, -call.column] }.select do |call|
|
31
|
-
build_result = try_patch(call, 'build')
|
32
|
-
next if build_result == ABORT
|
33
|
-
|
34
|
-
build_result == SUCCESS || try_patch(call, 'build_stubbed') == SUCCESS
|
35
|
-
end
|
25
|
+
self.changed_create_calls = find_changeable_create_calls
|
36
26
|
|
37
27
|
# validate whole spec after changes, e.g. to detect side-effects
|
38
28
|
self.ok = changed_create_calls.none? || begin
|
@@ -65,6 +55,27 @@ module FactorySloth
|
|
65
55
|
|
66
56
|
attr_writer :create_calls, :changed_create_calls, :ok, :path, :original_code, :patched_code
|
67
57
|
|
58
|
+
# Performance note: it might be faster to write ALL possible patches for a
|
59
|
+
# given spec file to tempfiles first, and then run them all in a single
|
60
|
+
# rspec call. However, this would make it impossible to use `--fail-fast`,
|
61
|
+
# and might make examples fail that are not as idempotent as they should be.
|
62
|
+
def find_changeable_create_calls
|
63
|
+
lines = create_calls.map(&:line)
|
64
|
+
|
65
|
+
self.changed_create_calls =
|
66
|
+
create_calls.sort_by { |call| [-call.line, -call.column] }.select do |call|
|
67
|
+
if lines.count(call.line) > 1
|
68
|
+
print_call_info(call, 'multiple create calls per line are unsupported, skipping')
|
69
|
+
next
|
70
|
+
end
|
71
|
+
|
72
|
+
build_result = try_patch(call, 'build')
|
73
|
+
next if build_result == ABORT
|
74
|
+
|
75
|
+
build_result == SUCCESS || try_patch(call, 'build_stubbed') == SUCCESS
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
68
79
|
def try_patch(call, base_variant)
|
69
80
|
variant = call.name.sub('create', base_variant)
|
70
81
|
FactorySloth.verbose && puts("#{link_to_call(call)}: trying #{variant} ...")
|
@@ -79,14 +90,14 @@ module FactorySloth
|
|
79
90
|
|
80
91
|
if result.success?
|
81
92
|
info = FactorySloth.dry_run ? 'can be replaced' : 'replaced'
|
82
|
-
|
93
|
+
print_call_info(call, "#{info} with #{variant}")
|
83
94
|
self.patched_code = new_patched_code
|
84
95
|
SUCCESS
|
85
96
|
elsif result.exitstatus == ExecutionCheck::FACTORY_UNUSED_CODE
|
86
|
-
|
97
|
+
print_call_info(call, "is never executed, skipping")
|
87
98
|
ABORT
|
88
99
|
elsif result.exitstatus == ExecutionCheck::FACTORY_PERSISTED_LATER_CODE
|
89
|
-
FactorySloth.verbose &&
|
100
|
+
FactorySloth.verbose && print_call_info("record is persisted later, skipping")
|
90
101
|
ABORT
|
91
102
|
end
|
92
103
|
end
|
@@ -100,13 +111,17 @@ module FactorySloth
|
|
100
111
|
ABORT = :ABORT # returned if there is no need to try other variants
|
101
112
|
SUCCESS = :SUCCESS
|
102
113
|
|
103
|
-
def
|
114
|
+
def print_call_info(call, message)
|
104
115
|
line_content = original_code[/\A(?:.*\R){#{call.line - 1}}\K.*/]
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
116
|
+
indentation = line_content[/^\s*/]
|
117
|
+
underline = Color.yellow('^' * call.name.size)
|
118
|
+
|
119
|
+
puts(
|
120
|
+
"#{link_to_call(call)}: #{call.name} #{message}",
|
121
|
+
" #{line_content.delete_prefix(indentation)}",
|
122
|
+
" #{' ' * (call.column - indentation.size)}#{underline}",
|
123
|
+
"",
|
124
|
+
)
|
110
125
|
end
|
111
126
|
|
112
127
|
def link_to_call(call)
|
data/lib/factory_sloth/color.rb
CHANGED
@@ -14,8 +14,12 @@ module FactorySloth::Color
|
|
14
14
|
private
|
15
15
|
|
16
16
|
def colorize(str, color_code)
|
17
|
-
return str unless
|
17
|
+
return str unless tty?
|
18
18
|
|
19
19
|
"\e[#{color_code}m#{str}\e[0m"
|
20
20
|
end
|
21
|
+
|
22
|
+
def tty?
|
23
|
+
$stdout.is_a?(IO) && $stdout.tty?
|
24
|
+
end
|
21
25
|
end
|
@@ -17,19 +17,32 @@ class FactorySloth::CreateCallFinder < Ripper
|
|
17
17
|
|
18
18
|
def store_call(obj)
|
19
19
|
@calls << obj if obj.is_a?(FactorySloth::CreateCall) && !@disabled
|
20
|
+
obj
|
20
21
|
end
|
21
22
|
|
22
23
|
def on_ident(name, *)
|
23
|
-
%w[create create_list create_pair].include?(name)
|
24
|
-
|
24
|
+
return name unless %w[create create_list create_pair].include?(name)
|
25
|
+
|
26
|
+
FactorySloth::CreateCall.new(name: name, line: lineno, column: column)
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_assign(left, right)
|
30
|
+
if left.to_s.match?(/\A_/) &&
|
31
|
+
!FactorySloth.check_underscore_vars &&
|
32
|
+
[right, Array(right).last].any? { |v| v.is_a?(FactorySloth::CreateCall) }
|
33
|
+
@calls.pop
|
34
|
+
end
|
35
|
+
[left, right]
|
25
36
|
end
|
26
37
|
|
27
|
-
def on_call(mod,
|
28
|
-
store_call(
|
38
|
+
def on_call(mod, op, method, *args)
|
39
|
+
store_call(method) if mod == 'FactoryBot'
|
40
|
+
[mod, op, method, *args]
|
29
41
|
end
|
30
42
|
|
31
|
-
def on_command_call(mod,
|
32
|
-
store_call(
|
43
|
+
def on_command_call(mod, op, method, *args)
|
44
|
+
store_call(method) if mod == 'FactoryBot'
|
45
|
+
[mod, op, method, *args]
|
33
46
|
end
|
34
47
|
|
35
48
|
def on_comment(text, *)
|
@@ -4,6 +4,10 @@
|
|
4
4
|
# The rationale behind a) is that things like skipped examples should not
|
5
5
|
# be broken. The rationale behind b) is that not much DB work would be saved,
|
6
6
|
# but diff noise would be increased and ease of editing the example reduced.
|
7
|
+
#
|
8
|
+
# Note: the caller column is only available in a roundabout way in Ruby >= 3.1,
|
9
|
+
# https://bugs.ruby-lang.org/issues/17930, 19452, so multiple replacements
|
10
|
+
# in one line would not be validated correctly iff they had mixed validity.
|
7
11
|
|
8
12
|
module FactorySloth::ExecutionCheck
|
9
13
|
FACTORY_UNUSED_CODE = 77
|
@@ -15,10 +19,10 @@ module FactorySloth::ExecutionCheck
|
|
15
19
|
records_by_line = {} # track records initialized through factories per line
|
16
20
|
|
17
21
|
FactoryBot::Syntax::Methods.class_eval do
|
18
|
-
|
22
|
+
original_variant = instance_method("#{variant}") # e.g. build
|
19
23
|
|
20
|
-
define_method("#{variant}") do |*args, **kwargs, &blk|
|
21
|
-
result =
|
24
|
+
define_method("#{variant}") do |*args, **kwargs, &blk|
|
25
|
+
result = original_variant.bind_call(self, *args, **kwargs, &blk)
|
22
26
|
list = records_by_line[caller_locations(1, 1)&.first&.lineno] ||= []
|
23
27
|
list.concat([result].flatten) # to work with single, list, and pair
|
24
28
|
result
|
data/lib/factory_sloth.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factory_sloth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janosch Müller
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2024-10-04 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
|
-
description:
|
14
12
|
email:
|
15
13
|
- janosch84@gmail.com
|
16
14
|
executables:
|
@@ -44,7 +42,6 @@ metadata:
|
|
44
42
|
homepage_uri: https://github.com/jaynetics/factory_sloth
|
45
43
|
source_code_uri: https://github.com/jaynetics/factory_sloth
|
46
44
|
changelog_uri: https://github.com/jaynetics/factory_sloth/blob/main/CHANGELOG.md
|
47
|
-
post_install_message:
|
48
45
|
rdoc_options: []
|
49
46
|
require_paths:
|
50
47
|
- lib
|
@@ -59,8 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
56
|
- !ruby/object:Gem::Version
|
60
57
|
version: '0'
|
61
58
|
requirements: []
|
62
|
-
rubygems_version: 3.
|
63
|
-
signing_key:
|
59
|
+
rubygems_version: 3.6.0.dev
|
64
60
|
specification_version: 4
|
65
61
|
summary: Find and replace unnecessary factory_bot create calls.
|
66
62
|
test_files: []
|