ffast 0.2.0 → 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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +27 -0
  3. data/.github/workflows/ruby.yml +34 -0
  4. data/.gitignore +2 -0
  5. data/Fastfile +146 -3
  6. data/README.md +244 -132
  7. data/bin/console +6 -1
  8. data/bin/fast-experiment +3 -0
  9. data/bin/fast-mcp +7 -0
  10. data/fast.gemspec +24 -7
  11. data/lib/fast/cli.rb +129 -38
  12. data/lib/fast/experiment.rb +19 -2
  13. data/lib/fast/git.rb +1 -1
  14. data/lib/fast/mcp_server.rb +317 -0
  15. data/lib/fast/node.rb +258 -0
  16. data/lib/fast/prism_adapter.rb +310 -0
  17. data/lib/fast/rewriter.rb +64 -10
  18. data/lib/fast/scan.rb +203 -0
  19. data/lib/fast/shortcut.rb +23 -6
  20. data/lib/fast/source.rb +116 -0
  21. data/lib/fast/source_rewriter.rb +153 -0
  22. data/lib/fast/sql/rewriter.rb +98 -0
  23. data/lib/fast/sql.rb +165 -0
  24. data/lib/fast/summary.rb +435 -0
  25. data/lib/fast/version.rb +1 -1
  26. data/lib/fast.rb +165 -79
  27. data/mkdocs.yml +27 -3
  28. data/requirements-docs.txt +3 -0
  29. metadata +48 -62
  30. data/docs/command_line.md +0 -238
  31. data/docs/editors-integration.md +0 -46
  32. data/docs/experiments.md +0 -153
  33. data/docs/ideas.md +0 -80
  34. data/docs/index.md +0 -402
  35. data/docs/pry-integration.md +0 -27
  36. data/docs/research.md +0 -93
  37. data/docs/shortcuts.md +0 -323
  38. data/docs/similarity_tutorial.md +0 -176
  39. data/docs/syntax.md +0 -395
  40. data/docs/videos.md +0 -16
  41. data/examples/build_stubbed_and_let_it_be_experiment.rb +0 -51
  42. data/examples/experimental_replacement.rb +0 -46
  43. data/examples/find_usage.rb +0 -26
  44. data/examples/let_it_be_experiment.rb +0 -11
  45. data/examples/method_complexity.rb +0 -37
  46. data/examples/search_duplicated.rb +0 -15
  47. data/examples/similarity_research.rb +0 -58
  48. data/examples/simple_rewriter.rb +0 -6
  49. data/experiments/let_it_be_experiment.rb +0 -9
  50. data/experiments/remove_useless_hook.rb +0 -9
  51. data/experiments/replace_create_with_build_stubbed.rb +0 -10
data/docs/syntax.md DELETED
@@ -1,395 +0,0 @@
1
- # Syntax
2
-
3
- The syntax is inspired on [RuboCop Node Pattern](https://github.com/bbatsov/rubocop/blob/master/lib/rubocop/node_pattern.rb).
4
-
5
- You can find a great tutorial about RuboCop node pattern in the
6
- [official documentation](https://rubocop.readthedocs.io/en/latest/node_pattern/).
7
-
8
- ## Code example
9
-
10
- Let's consider the following `example.rb` code example:
11
-
12
- ```ruby
13
- class Example
14
- ANSWER = 42
15
- def magic
16
- rand(ANSWER)
17
- end
18
- def duplicate(value)
19
- value * 2
20
- end
21
- end
22
- ```
23
-
24
- Looking the AST representation we have:
25
-
26
- $ ruby-parse example.rb
27
-
28
- ```
29
- (class
30
- (const nil :Example) nil
31
- (begin
32
- (casgn nil :ANSWER
33
- (int 42))
34
- (def :magic
35
- (args)
36
- (send nil :rand
37
- (const nil :ANSWER)))
38
- (def :duplicate
39
- (args
40
- (arg :value))
41
- (send
42
- (lvar :value) :*
43
- (int 2)))))
44
- ```
45
-
46
- Now, let's explore all details of the current AST, combining with the syntax
47
- operators.
48
-
49
- Fast works with a single word that will be the node type.
50
-
51
- A simple search of `def` nodes can be done and will also print the code.
52
-
53
- $ fast def example.rb
54
-
55
- ```ruby
56
- # example.rb:3
57
- def magic
58
- rand(ANSWER)
59
- end
60
- ```
61
-
62
- or check the `casgn` that will show constant assignments:
63
-
64
- $ fast casgn example.rb
65
-
66
- ```ruby
67
- # example.rb:2
68
- ANSWER = 42
69
- ```
70
-
71
- ## `()` to represent a **node** search
72
-
73
- To specify details about a node, the `(` means navigate deeply into a node and
74
- go deep into the expression.
75
-
76
- $ fast '(casgn' example.rb
77
-
78
- ```ruby
79
- # example.rb:2
80
- ANSWER = 42
81
- ```
82
-
83
- Fast matcher never checks the end of the expression and close parens are not
84
- necessary. We keep them for the sake of specify more node details but the
85
- expression works with incomplete parens.
86
-
87
- $ fast '(casgn)' example.rb
88
-
89
- ```ruby
90
- # example.rb:2
91
- ANSWER = 42
92
- ```
93
-
94
- Closing extra params also don't have a side effect.
95
-
96
- $ fast '(casgn))' example.rb
97
-
98
- ```ruby
99
- # example.rb:2
100
- ANSWER = 42
101
- ```
102
-
103
- It also automatically flat parens case you put more levels in the beginning.
104
-
105
- $ fast '((casgn))' example.rb
106
-
107
- ```ruby
108
- # example.rb:2
109
- ANSWER = 42
110
- ```
111
-
112
- For checking AST details while doing some search, you can use `--ast` in the
113
- command line for printing the AST instead of the code:
114
-
115
- $ fast '((casgn ' example.rb --ast
116
-
117
- ```ruby
118
- # example.rb:2
119
- (casgn nil :ANSWER
120
- (int 42))
121
- ```
122
-
123
- ## `_` is **something** not nil
124
-
125
- Let's enhance our current expression and specify that we're looking for constant
126
- assignments of integers ignoring values and constant names replacing with `_`.
127
-
128
- $ fast '(casgn nil _ (int _))' example.rb
129
-
130
- ```ruby
131
- # example.rb:2
132
- ANSWER = 42
133
- ```
134
-
135
- Keep in mind that `_` means not nil and `(casgn _ _ (int _))` would not
136
- match.
137
-
138
- Let's search for integer nodes:
139
-
140
- $ fast int example.rb
141
- ```ruby
142
- # example.rb:2
143
- 42
144
- # example.rb:7
145
- 2
146
- ```
147
-
148
- The current search show the nodes but they are not so useful without understand
149
- the expression in their context. We need to check their `parent`.
150
-
151
- ## `^` is to get the **parent node** of an expression
152
-
153
- By default, Parser::AST::Node does not have access to parent and for accessing
154
- it you can say `^` for reaching the parent.
155
-
156
- $ fast '^int' example.rb
157
-
158
- ```ruby
159
- # example.rb:2
160
- ANSWER = 42
161
- # example.rb:7
162
- value * 2
163
- ```
164
-
165
- And using it multiple times will make the node match from levels up:
166
-
167
- $ fast '^^int' example.rb
168
-
169
- ```ruby
170
- # example.rb:2
171
- ANSWER = 42
172
- def magic
173
- rand(ANSWER)
174
- end
175
- def duplicate(value)
176
- value * 2
177
- end
178
- ```
179
-
180
- ## `[]` join conditions
181
-
182
- Let's hunt for integer nodes that the parent is also a method:
183
-
184
- $ fast '[ ^^int def ]' example.rb
185
-
186
- The match will filter only nodes that matches all internal expressions.
187
-
188
- ```ruby
189
- # example.rb:6
190
- def duplicate(value)
191
- value * 2
192
- end
193
- ```
194
-
195
- The expression is matching nodes that have a integer granchild and also with
196
- type `def`.
197
-
198
- ## `...` is a **node** with children
199
-
200
- Looking the method representation we have:
201
-
202
- $ fast def example.rb --ast
203
-
204
- ```ruby
205
- # example.rb:3
206
- (def :magic
207
- (args)
208
- (send nil :rand
209
- (const nil :ANSWER)))
210
- # example.rb:6
211
- (def :duplicate
212
- (args
213
- (arg :value))
214
- (send
215
- (lvar :value) :*
216
- (int 2)))
217
- ```
218
-
219
- And if we want to delimit only methods with arguments:
220
-
221
- $ fast '(def _ ...)' example.rb
222
-
223
- ```ruby
224
- # example.rb:6
225
- def duplicate(value)
226
- value * 2
227
- end
228
- ```
229
-
230
- If we use `(def _ _)` instead it will match both methods because `(args)`
231
- does not have children but is not nil.
232
-
233
- ## `$` is for **capture** current expression
234
-
235
- Now, let's say we want to extract some method name from current classes.
236
-
237
- In such case we don't want to have the node definition but only return the node
238
- name.
239
-
240
- ```ruby
241
- # example.rb:2
242
- def magic
243
- rand(ANSWER)
244
- end
245
- # example.rb:
246
- magic
247
- # example.rb:9
248
- def duplicate(value)
249
- value * 2
250
- end
251
- # example.rb:
252
- duplicate
253
- ```
254
-
255
- One extra method name was printed because of `$` is capturing the element.
256
-
257
- ## `nil` matches exactly **nil**
258
-
259
- Nil is used in the code as a node type but parser gem also represents empty
260
- spaces in expressions with nil.
261
-
262
- Example, a method call from Kernel is a `send` from `nil` calling the method
263
- while I can also send a method call from a class.
264
-
265
- ```
266
- $ ruby-parse -e 'method'
267
- (send nil :method)
268
- ```
269
-
270
- And a method from a object will have the nested target not nil.
271
-
272
- ```
273
- $ ruby-parse -e 'object.method'
274
- (send
275
- (send nil :object) :method)
276
- ```
277
-
278
- Let's build a serch for any calls from `nil`:
279
-
280
- $ fast '(_ nil _)' example.rb
281
-
282
- ```ruby
283
- # example.rb:3
284
- Example
285
- # example.rb:4
286
- ANSWER = 42
287
- # example.rb:6
288
- rand(ANSWER)
289
- ```
290
-
291
- Double check the expressions that have matched printing the AST:
292
-
293
- $ fast '(_ nil _)' example.rb --ast
294
-
295
- ```ruby
296
- # example.rb:3
297
- (const nil :Example)
298
- # example.rb:4
299
- (casgn nil :ANSWER
300
- (int 42))
301
- # example.rb:6
302
- (send nil :rand
303
- (const nil :ANSWER))
304
- ```
305
-
306
- ## `{}` is for **any** matches like **union** conditions with **or** operator
307
-
308
- Let's say we to add check all occurrencies of the constant `ANSWER`.
309
-
310
- We'll need to get both `casgn` and `const` node types. For such cases we can
311
- surround the expressions with `{}` and it will return if the node matches with
312
- any of the internal expressions.
313
-
314
- $ fast '({casgn const} nil ANSWER)' example.rb
315
-
316
- ```
317
- # example.rb:4
318
- ANSWER = 42
319
- # example.rb:6
320
- ANSWER
321
- ```
322
-
323
- ## `#` for custom methods
324
-
325
- Custom methods can let you into ruby doman for more complicated rules. Let's say
326
- we're looking for duplicated methods in the same class. We need to collect
327
- method names and guarantee they are unique.
328
-
329
- ```ruby
330
- def duplicated(method_name)
331
- @methods ||= []
332
- already_exists = @methods.include?(method_name)
333
- @methods << method_name
334
- already_exists
335
- end
336
-
337
- puts Fast.search_file( '(def #duplicated)', 'example.rb')
338
- ```
339
- The same principle can be used in the node level or for debugging purposes.
340
-
341
- ```ruby
342
- require 'pry'
343
- def debug(node)
344
- binding.pry
345
- end
346
-
347
- puts Fast.search_file('#debug', 'example.rb')
348
- ```
349
- If you want to get only `def` nodes you can also intersect expressions with `[]`:
350
- ```ruby
351
- puts Fast.search_file('[ def #debug ]', 'example.rb')
352
- ```
353
- Or if you want to debug a very specific expression you can use `()` to specify
354
- more details of the node
355
- ```ruby
356
- puts Fast.search_file('[ (def a) #debug ]', 'example.rb')
357
- ```
358
-
359
- ## `.` for instance methods
360
-
361
- You can also call instance methods using `.<method-name>`.
362
-
363
- Example `nil` is the same of calling `nil?` and you can also use `(int .odd?)`
364
- to pick only odd integers. The `int` fragment can also be `int_type?`.
365
-
366
- ## `\1` for first previous capture
367
-
368
- Imagine you're looking for a method that is just delegating something to
369
- another method, like:
370
-
371
- ```ruby
372
- def name
373
- person.name
374
- end
375
- ```
376
-
377
- This can be represented as the following AST:
378
-
379
- ```
380
- (def :name
381
- (args)
382
- (send
383
- (send nil :person) :name))
384
- ```
385
-
386
- Then, let's build a search for methods that calls an attribute with the same
387
- name:
388
-
389
- ```ruby
390
- Fast.match?('(def $_ ... (send (send nil _) \1))', ast) # => [:name]
391
- ```
392
-
393
- With the method name being captured with `$_` it can be later referenced in the
394
- expression with `\1`. If the search contains multiple captures, the `\2`,`\3`
395
- can be used as the sequence of captures.
data/docs/videos.md DELETED
@@ -1,16 +0,0 @@
1
- # Videos
2
-
3
- - [Ruby Kaigi TakeOut 2020: Grepping Ruby code like a boss](https://www.youtube.com/watch?v=YzcYXB4L2so&amp;feature=youtu.be&amp;t=11855)
4
-
5
- <iframe width="560" height="315" src="https://www.youtube.com/embed/YzcYXB4L2so?start=11855" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
6
-
7
- Also, similar livecoding session at [RubyConf Brazil 2019 (Portuguese)](https://www.eventials.com/locaweb/jonatas-paganini-live-coding-grepping-ruby-code-like-a-boss/#_=_).
8
-
9
- - Introduction to [inline code](https://www.youtube.com/watch?v=KQXglNLUv7o).
10
- <iframe width="560" height="315" src="https://www.youtube.com/embed/KQXglNLUv7o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
11
-
12
- - [Making local variables inline](https://www.youtube.com/watch?v=JD44nhegCRs)
13
- <iframe width="560" height="315" src="https://www.youtube.com/embed/YN0s9kV1A2A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
14
-
15
- - [Making methods inline](https://www.youtube.com/watch?v=JD44nhegCRs)
16
- <iframe width="560" height="315" src="https://www.youtube.com/embed/YN0s9kV1A2A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@@ -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
@@ -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
-
@@ -1,6 +0,0 @@
1
-
2
- rewriter = Fast::Rewriter.new
3
- rewriter.ast = Fast.ast("a = 1")
4
- rewriter.search ='(lvasgn _ ...)'
5
- rewriter.replacement = -> (node) { replace(node.location.name, 'variable_renamed') }
6
- puts rewriter.rewrite!
@@ -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