ffast 0.2.2 → 0.2.3
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/.github/workflows/release.yml +27 -0
- data/.github/workflows/ruby.yml +34 -0
- data/.gitignore +2 -0
- data/Fastfile +102 -15
- data/README.md +21 -7
- data/bin/console +1 -1
- data/bin/fast-experiment +3 -0
- data/bin/fast-mcp +7 -0
- data/fast.gemspec +1 -3
- data/lib/fast/cli.rb +58 -26
- data/lib/fast/experiment.rb +19 -2
- data/lib/fast/git.rb +1 -1
- data/lib/fast/mcp_server.rb +317 -0
- data/lib/fast/node.rb +258 -0
- data/lib/fast/prism_adapter.rb +310 -0
- data/lib/fast/rewriter.rb +64 -10
- data/lib/fast/scan.rb +203 -0
- data/lib/fast/shortcut.rb +16 -4
- data/lib/fast/source.rb +116 -0
- data/lib/fast/source_rewriter.rb +153 -0
- data/lib/fast/sql/rewriter.rb +36 -7
- data/lib/fast/sql.rb +15 -17
- data/lib/fast/summary.rb +435 -0
- data/lib/fast/version.rb +1 -1
- data/lib/fast.rb +140 -83
- data/mkdocs.yml +19 -4
- data/requirements-docs.txt +3 -0
- metadata +16 -59
- data/docs/command_line.md +0 -238
- data/docs/editors-integration.md +0 -46
- data/docs/experiments.md +0 -155
- data/docs/git.md +0 -115
- data/docs/ideas.md +0 -70
- data/docs/index.md +0 -404
- data/docs/pry-integration.md +0 -27
- data/docs/research.md +0 -93
- data/docs/shortcuts.md +0 -323
- data/docs/similarity_tutorial.md +0 -176
- data/docs/sql-support.md +0 -253
- data/docs/syntax.md +0 -395
- data/docs/videos.md +0 -16
- data/docs/walkthrough.md +0 -135
- data/examples/build_stubbed_and_let_it_be_experiment.rb +0 -51
- data/examples/experimental_replacement.rb +0 -46
- data/examples/find_usage.rb +0 -26
- data/examples/let_it_be_experiment.rb +0 -11
- data/examples/method_complexity.rb +0 -37
- data/examples/search_duplicated.rb +0 -15
- data/examples/similarity_research.rb +0 -58
- data/examples/simple_rewriter.rb +0 -6
- data/experiments/let_it_be_experiment.rb +0 -9
- data/experiments/remove_useless_hook.rb +0 -9
- data/experiments/replace_create_with_build_stubbed.rb +0 -10
data/docs/walkthrough.md
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Fast walkthrough
|
|
2
|
-
|
|
3
|
-
!!! note "This is the main interactive tutorial we have on `fast`. If you're reading it on the web, please consider also try it in the command line: `fast .intro` in the terminal to get a rapid pace on reading and testing on your own computer."
|
|
4
|
-
|
|
5
|
-
The objective here is give you some insights about how to use `ffast` gem in the
|
|
6
|
-
command line.
|
|
7
|
-
|
|
8
|
-
Let's start finding the main `fast.rb` file for the fast library:
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
$ gem which fast
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
And now, let's combine the previous expression that returns the path to the file
|
|
15
|
-
and take a quick look into the methods `match?` in the file using a regular grep:
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
$ grep "def match\?" $(gem which fast)
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Boring results, no? The code here is not easy to digest because we just see a
|
|
22
|
-
fragment of the code block that we want.
|
|
23
|
-
Let's make it a bit more advanced with `grep -rn` to file name and line number:
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
$ grep -rn "def match\?" $(gem which fast)
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Still hard to understand the scope of the search.
|
|
30
|
-
|
|
31
|
-
That's why fast exists! Now, let's take a look on how a method like this looks
|
|
32
|
-
like from the AST perspective. Let's use `ruby-parse` for it:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
$ ruby-parse -e "def match?(node); end"
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Now, let's make the same search with `fast` node pattern:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
fast "(def match?)" $(gem which fast)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Wow! in this case you got all the `match?` methods, but you'd like to go one level upper
|
|
45
|
-
and understand the classes that implements the method with a single node as
|
|
46
|
-
argument. Let's first use `^` to jump into the parent:
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
fast "^(def match?)" $(gem which fast)
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
As you can see it still prints some `match?` methods that are not the ones that
|
|
53
|
-
we want, so, let's add a filter by the argument node `(args (arg node))`:
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
fast "(def match? (args (arg node)))" $(gem which fast)
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Now, it looks closer to have some understanding of the scope, filtering only
|
|
60
|
-
methods that have the name `match?` and receive `node` as a parameter.
|
|
61
|
-
|
|
62
|
-
Now, let's do something different and find all methods that receives a `node` as
|
|
63
|
-
an argument:
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
fast "(def _ (args (arg node)))" $(gem which fast)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Looks like almost all of them are the `match?` and we can also skip the `match?`
|
|
70
|
-
methods negating the expression prefixing with `!`:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
fast "(def !match? (args (arg node)))" $(gem which fast)
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Let's move on and learn more about node pattern with the RuboCop project:
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
$ VISUAL=echo gem open rubocop
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
RuboCop contains `def_node_matcher` and `def_node_search`. Let's make a search
|
|
83
|
-
for both method names wrapping the query with `{}` selector:
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
fast "(send nil {def_node_matcher def_node_search})" $(VISUAL=echo gem open rubocop)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
As you can see, node pattern is widely adopted in the cops to target code.
|
|
90
|
-
Rubocop contains a few projects with dedicated cops that can help you learn
|
|
91
|
-
more.
|
|
92
|
-
|
|
93
|
-
## How to automate refactor using AST
|
|
94
|
-
|
|
95
|
-
Moving towards to the code automation, the next step after finding some target code
|
|
96
|
-
is refactor and change the code behavior.
|
|
97
|
-
|
|
98
|
-
Let's imagine that we already found some code that we want to edit or remove. If
|
|
99
|
-
we get the AST we can also cherry-pick any fragment of the expression to be
|
|
100
|
-
replaced. As you can imagine, RuboCop also benefits from automatic refactoring
|
|
101
|
-
offering the `--autocorrect` option.
|
|
102
|
-
|
|
103
|
-
All the hardcore algorithms are in the [parser](https://github.com/whitequark/parser)
|
|
104
|
-
rewriter, but we can find a lot of great examples on RuboCop project searching
|
|
105
|
-
for the `autocorrect` method.
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
fast "(def autocorrect)" $(VISUAL=echo gem open rubocop)
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Look that most part of the methods are just returning a lambda with a
|
|
112
|
-
corrector. Now, let's use the `--ast` to get familiar with the tree details for the
|
|
113
|
-
implementation:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
fast --ast "(def autocorrect)" $(VISUAL=echo gem open rubocop)/lib/rubocop/cop/style
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
As we can see, we have a `(send (lvar corrector))` that is the interface that we
|
|
120
|
-
can get the most interesting calls to overwrite files:
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
fast "(send (lvar corrector)" $(VISUAL=echo gem open rubocop)
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
## That is all for now!
|
|
128
|
-
|
|
129
|
-
I hope you enjoyed to learn by example searching with fast. If you like it,
|
|
130
|
-
please [star the project](https://github.com/jonatas/fast/)!
|
|
131
|
-
|
|
132
|
-
You can also build your own tutorials simply using markdown files like I did
|
|
133
|
-
here, you can find this tutorial [here](https://github.com/jonatas/fast/tree/master/docs/walkthrough.md).
|
|
134
|
-
|
|
135
|
-
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'fast'
|
|
4
|
-
require 'open3'
|
|
5
|
-
require 'ostruct'
|
|
6
|
-
require 'fileutils'
|
|
7
|
-
|
|
8
|
-
# Usage instructions:
|
|
9
|
-
# 1. Add the following to your project's Gemfile: gem 'ffast' (yes, two "f"s)
|
|
10
|
-
# 2. Copy this file to your Rails project root directory
|
|
11
|
-
# 3. Run: bundle exec ruby build_stubbed_and_let_it_be_experiment.rb
|
|
12
|
-
|
|
13
|
-
# List of spec files you want to experiment with. One per line.
|
|
14
|
-
FILE_NAMES = %w[
|
|
15
|
-
# spec/model/foo_spec.rb
|
|
16
|
-
# spec/model/bar_spec.rb
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
def execute_rspec(file_name)
|
|
20
|
-
rspec_command = "bin/spring rspec --fail-fast --format progress #{file_name}"
|
|
21
|
-
stdout_str, stderr_str, status = Open3.capture3(rspec_command)
|
|
22
|
-
execution_time = /Finished in (.*?) seconds/.match(stdout_str)[1]
|
|
23
|
-
print stderr_str.gsub(/Running via Spring preloader.*?$/, '').chomp unless status.success?
|
|
24
|
-
OpenStruct.new(success: status.success?, execution_time: execution_time)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def delete_temp_files(original_file_name)
|
|
28
|
-
file_path = File.dirname(original_file_name)
|
|
29
|
-
file_name = File.basename(original_file_name)
|
|
30
|
-
Dir.glob("#{file_path}/experiment*#{file_name}").each { |file| File.delete(file)}
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
FILE_NAMES.each do |original_file_name|
|
|
34
|
-
Fast.experiment('RSpec/ReplaceCreateWithBuildStubbed') do
|
|
35
|
-
lookup original_file_name
|
|
36
|
-
search '(block (send nil let (sym _)) (args) $(send nil create))'
|
|
37
|
-
edit { |_, (create)| replace(create.loc.selector, 'build_stubbed') }
|
|
38
|
-
policy { |experiment_file_name| execute_rspec(experiment_file_name) }
|
|
39
|
-
end.run
|
|
40
|
-
|
|
41
|
-
Fast.experiment('RSpec/LetItBe') do
|
|
42
|
-
lookup original_file_name
|
|
43
|
-
search '(block $(send nil let! (sym _)) (args) (send nil create))'
|
|
44
|
-
edit { |_, (let)| replace(let.loc.selector, 'let_it_be') }
|
|
45
|
-
policy { |experiment_file_name| execute_rspec(experiment_file_name) }
|
|
46
|
-
end.run
|
|
47
|
-
|
|
48
|
-
delete_temp_files(original_file_name)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH << File.expand_path('../lib', __dir__)
|
|
4
|
-
require 'fast'
|
|
5
|
-
|
|
6
|
-
# It's a simple script that you can try to replace
|
|
7
|
-
# `create` by `build_stubbed` and it moves the file if
|
|
8
|
-
# successfully passed the specs
|
|
9
|
-
#
|
|
10
|
-
# $ ruby experimental_replacement.rb spec/*/*_spec.rb
|
|
11
|
-
def experimental_spec(file, name)
|
|
12
|
-
parts = file.split('/')
|
|
13
|
-
dir = parts[0..-2]
|
|
14
|
-
filename = "experiment_#{name}_#{parts[-1]}"
|
|
15
|
-
File.join(*dir, filename)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def experiment(file, name, search, replacement)
|
|
19
|
-
ast = Fast.ast_from_file(file)
|
|
20
|
-
|
|
21
|
-
results = Fast.search(ast, search)
|
|
22
|
-
unless results.empty?
|
|
23
|
-
new_content = Fast.replace_file(file, search, replacement)
|
|
24
|
-
new_spec = experimental_spec(file, name)
|
|
25
|
-
return if File.exist?(new_spec)
|
|
26
|
-
File.open(new_spec, 'w+') { |f| f.puts new_content }
|
|
27
|
-
if system("bin/spring rspec --fail-fast #{new_spec}")
|
|
28
|
-
system "mv #{new_spec} #{file}"
|
|
29
|
-
puts "✅ #{file}"
|
|
30
|
-
else
|
|
31
|
-
system "rm #{new_spec}"
|
|
32
|
-
puts "🔴 #{file}"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
rescue StandardError
|
|
36
|
-
# Avoid stop because weird errors like encoding issues
|
|
37
|
-
puts "🔴🔴 🔴 #{file}: #{$ERROR_INFO}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
ARGV.each do |file|
|
|
41
|
-
[
|
|
42
|
-
# Thread.new { experiment(file, 'build_stubbed', '(send nil create)', ->(node) { replace(node.location.selector, 'build_stubbed') }) },
|
|
43
|
-
experiment(file, 'seed', '(send nil create)', ->(node) { replace(node.location.selector, 'seed') })
|
|
44
|
-
|
|
45
|
-
] # .each(&:join)
|
|
46
|
-
end
|
data/examples/find_usage.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# List files that matches with some expression
|
|
5
|
-
# Usage:
|
|
6
|
-
#
|
|
7
|
-
# ruby examples/find_usage.rb defs
|
|
8
|
-
#
|
|
9
|
-
# Or be explicit about directory or folder:
|
|
10
|
-
#
|
|
11
|
-
# ruby examples/find_usage.rb defs lib/
|
|
12
|
-
$LOAD_PATH.unshift(File.expand_path('lib', __dir__))
|
|
13
|
-
|
|
14
|
-
require 'fast'
|
|
15
|
-
require 'coderay'
|
|
16
|
-
|
|
17
|
-
arguments = ARGV
|
|
18
|
-
pattern = arguments.shift
|
|
19
|
-
files = Fast.ruby_files_from(arguments.any? ? arguments : '.')
|
|
20
|
-
files.select do |file|
|
|
21
|
-
begin
|
|
22
|
-
puts file if Fast.search_file(pattern, file).any?
|
|
23
|
-
rescue Parser::SyntaxError
|
|
24
|
-
[]
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require '../../fast/lib/fast'
|
|
4
|
-
|
|
5
|
-
# For specs using `let(:something) { create ... }` it tries to use `let_it_be` instead
|
|
6
|
-
Fast.experiment('RSpec/LetItBe') do
|
|
7
|
-
lookup 'spec/models'
|
|
8
|
-
search '(block $(send nil let (sym _)) (args) (send nil create))'
|
|
9
|
-
edit { |_, (let)| replace(let.loc.selector, 'let_it_be') }
|
|
10
|
-
policy { |new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
|
11
|
-
end.run
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH << File.expand_path('../lib', __dir__)
|
|
4
|
-
require 'fast'
|
|
5
|
-
|
|
6
|
-
def node_size(node)
|
|
7
|
-
return 1 unless node.respond_to?(:children)
|
|
8
|
-
children = node.children
|
|
9
|
-
return 1 if children.empty? || children.length == 1
|
|
10
|
-
nodes, syms = children.partition { |e| e.respond_to?(:children) }
|
|
11
|
-
1 + syms.length + (nodes.map(&method(:node_size)).inject(:+) || 0)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def method_complexity(file)
|
|
15
|
-
ast = Fast.ast_from_file(file)
|
|
16
|
-
Fast.search(ast, '(class ...)').map do |node_class|
|
|
17
|
-
manager_name = node_class.children.first.children.last
|
|
18
|
-
|
|
19
|
-
defs = Fast.search(node_class, '(def !{initialize} ... ... )')
|
|
20
|
-
|
|
21
|
-
defs.map do |node|
|
|
22
|
-
complexity = node_size(node)
|
|
23
|
-
method_name = node.children.first
|
|
24
|
-
{ "#{manager_name}##{method_name}" => complexity }
|
|
25
|
-
end.inject(:merge) || {}
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
files = ARGV || Dir['**/*.rb']
|
|
30
|
-
|
|
31
|
-
complexities = files.map(&method(:method_complexity)).flatten.inject(:merge!)
|
|
32
|
-
|
|
33
|
-
puts '| Method | Complexity |'
|
|
34
|
-
puts '| ------ | ---------- |'
|
|
35
|
-
complexities.sort_by { |_, v| -v }.map do |method, complexity|
|
|
36
|
-
puts "| #{method} | #{complexity} |"
|
|
37
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'fast'
|
|
2
|
-
|
|
3
|
-
# Search for duplicated methods interpolating the method and collecting previous
|
|
4
|
-
# method names. Returns true if the name already exists in the same class level.
|
|
5
|
-
# Note that this example will work only in a single file because it does not
|
|
6
|
-
# cover any detail on class level.
|
|
7
|
-
def duplicated(method_name)
|
|
8
|
-
@methods ||= []
|
|
9
|
-
already_exists = @methods.include?(method_name)
|
|
10
|
-
@methods << method_name
|
|
11
|
-
already_exists
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
puts Fast.search_file( '(def #duplicated)', 'example.rb')
|
|
15
|
-
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require 'bundler/setup'
|
|
2
|
-
require 'fast'
|
|
3
|
-
require 'coderay'
|
|
4
|
-
require 'pp'
|
|
5
|
-
require 'set'
|
|
6
|
-
|
|
7
|
-
arguments = ARGV
|
|
8
|
-
pattern = arguments.shift || '{ block case send def defs while class if }'
|
|
9
|
-
|
|
10
|
-
files = Fast.ruby_files_from(*%w(spec lib app gems)) +
|
|
11
|
-
Dir[File.join(Gem.path.first,'**/*.rb')]
|
|
12
|
-
|
|
13
|
-
total = files.count
|
|
14
|
-
pattern = Fast.expression(pattern)
|
|
15
|
-
|
|
16
|
-
similarities = {}
|
|
17
|
-
|
|
18
|
-
def similarities.show pattern
|
|
19
|
-
files = self[pattern]
|
|
20
|
-
files.each do |file|
|
|
21
|
-
nodes = Fast.search_file(pattern, file)
|
|
22
|
-
nodes.each do |result|
|
|
23
|
-
Fast.report(result, file: file)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def similarities.top
|
|
29
|
-
self.transform_values(&:size)
|
|
30
|
-
.sort_by{|search,results|search.size / results.size}
|
|
31
|
-
.reverse.select{|k,v|v > 10}[0,10]
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
begin
|
|
35
|
-
files.each_with_index do |file, i|
|
|
36
|
-
progress = ((i / total.to_f) * 100.0).round(2)
|
|
37
|
-
print "\r (#{i}/#{total}) #{progress}% Researching on #{file}"
|
|
38
|
-
begin
|
|
39
|
-
results = Fast.search_file(pattern, file) || []
|
|
40
|
-
rescue
|
|
41
|
-
next
|
|
42
|
-
end
|
|
43
|
-
results.each do |n|
|
|
44
|
-
search = Fast.expression_from(n)
|
|
45
|
-
similarities[search] ||= Set.new
|
|
46
|
-
similarities[search] << file
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
rescue Interrupt
|
|
50
|
-
# require 'pry'; binding.pry
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
puts "mapped #{similarities.size} cases"
|
|
54
|
-
similarities.delete_if {|k,v| k.size < 30 || v.size < 5}
|
|
55
|
-
puts "Removing the small ones we have #{similarities.size} similarities"
|
|
56
|
-
|
|
57
|
-
similarities.show similarities.top[0][0]
|
|
58
|
-
|
data/examples/simple_rewriter.rb
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# For specs using `let!(:something) { create ... }` it tries to use `let_it_be` instead
|
|
4
|
-
Fast.experiment('RSpec/LetItBe') do
|
|
5
|
-
lookup 'spec'
|
|
6
|
-
search '(block $(send nil let! (sym _)) (args) (send nil create))'
|
|
7
|
-
edit { |_, (let)| replace(let.loc.selector, 'let_it_be') }
|
|
8
|
-
policy { |new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
|
9
|
-
end
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Experimentally remove a before or an after block
|
|
4
|
-
Fast.experiment('RSpec/RemoveUselessBeforeAfterHook') do
|
|
5
|
-
lookup 'spec'
|
|
6
|
-
search '(block (send nil {before after}))'
|
|
7
|
-
edit { |node| remove(node.loc.expression) }
|
|
8
|
-
policy { |new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
|
9
|
-
end
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# For specs using `let(:something) { create ... }` it tries to use
|
|
4
|
-
# `build_stubbed` instead
|
|
5
|
-
Fast.experiment('RSpec/ReplaceCreateWithBuildStubbed') do
|
|
6
|
-
lookup 'spec'
|
|
7
|
-
search '(block (send nil let (sym _)) (args) $(send nil create))'
|
|
8
|
-
edit { |_, (create)| replace(create.loc.selector, 'build_stubbed') }
|
|
9
|
-
policy { |new_file| system("bin/spring rspec --format progress --fail-fast #{new_file}") }
|
|
10
|
-
end
|