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/command_line.md
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
# Command line
|
|
2
|
-
|
|
3
|
-
When you install the ffast gem, it will also create an executable named `fast`
|
|
4
|
-
and you can use it to search and find code using the concept:
|
|
5
|
-
|
|
6
|
-
```
|
|
7
|
-
$ fast '(def match?)' lib/fast.rb
|
|
8
|
-
```
|
|
9
|
-
- Use `-d` or `--debug` for enable debug mode.
|
|
10
|
-
- Use `--ast` to output the AST instead of the original code
|
|
11
|
-
- Use `--pry` to jump debugging the first result with pry
|
|
12
|
-
- Use `-c` to search from code example
|
|
13
|
-
- Use `-s` to search similar code
|
|
14
|
-
- Use `-p` to or `--parallel` to use multi core search
|
|
15
|
-
|
|
16
|
-
## `--pry`
|
|
17
|
-
|
|
18
|
-
$ fast '(block (send nil it))' spec --pry
|
|
19
|
-
|
|
20
|
-
And inside pry session, you can use `result` as the first result or `results`
|
|
21
|
-
to use all occurrences found.
|
|
22
|
-
|
|
23
|
-
```ruby
|
|
24
|
-
results.map{|e|e.children[0].children[2]}
|
|
25
|
-
# => [s(:str, "parses ... as Find"),
|
|
26
|
-
# s(:str, "parses $ as Capture"),
|
|
27
|
-
# s(:str, "parses quoted values as strings"),
|
|
28
|
-
# s(:str, "parses {} as Any"),
|
|
29
|
-
# s(:str, "parses [] as All"), ...]
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Getting all `it` blocks without description:
|
|
33
|
-
|
|
34
|
-
$ fast '(block (send nil it (nil)) (args ) (!str)) ) )' spec
|
|
35
|
-
|
|
36
|
-
```ruby
|
|
37
|
-
# spec/fast_spec.rb:166
|
|
38
|
-
it { expect(described_class).to be_match('(...)', s(:int, 1)) }
|
|
39
|
-
...
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## `--debug`
|
|
43
|
-
|
|
44
|
-
This option will print all matching details while validating each node.
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
$ echo 'object.method' > sample.rb
|
|
48
|
-
$ fast -d '(send (send nil _) _)' sample.rb
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
It will bring details of the expression compiled and each node being validated:
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
Expression: f[send] [#<Fast::Find:0x00007f8c53047158 @token="send">, #<Fast::Find:0x00007f8c530470e0 @token="nil">, #<Fast::Find:0x00007f8c53047090 @token="_">] f[_]
|
|
55
|
-
send == (send
|
|
56
|
-
(send nil :object) :method) # => true
|
|
57
|
-
f[send] == (send
|
|
58
|
-
(send nil :object) :method) # => true
|
|
59
|
-
send == (send nil :object) # => true
|
|
60
|
-
f[send] == (send nil :object) # => true
|
|
61
|
-
== # => true
|
|
62
|
-
f[nil] == # => true
|
|
63
|
-
#<Proc:0x00007f8c53057af8@/Users/jonatasdp/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/ffast-0.0.2/lib/fast.rb:25 (lambda)> == object # => true
|
|
64
|
-
f[_] == object # => true
|
|
65
|
-
[#<Fast::Find:0x00007f8c53047158 @token="send">, #<Fast::Find:0x00007f8c530470e0 @token="nil">, #<Fast::Find:0x00007f8c53047090 @token="_">] == (send nil :object) # => true
|
|
66
|
-
#<Proc:0x00007f8c53057af8@/Users/jonatasdp/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/ffast-0.0.2/lib/fast.rb:25 (lambda)> == method # => true
|
|
67
|
-
f[_] == method # => true
|
|
68
|
-
# sample.rb:1
|
|
69
|
-
object.method
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## `-s` for similarity
|
|
73
|
-
|
|
74
|
-
Sometimes you want to search for some similar code like `(send (send (send nil _) _) _)` and we could simply say `a.b.c`.
|
|
75
|
-
|
|
76
|
-
The option `-s` build an expression from the code ignoring final values.
|
|
77
|
-
|
|
78
|
-
$ echo 'object.method' > sample.rb
|
|
79
|
-
$ fast -s 'a.b' sample.rb
|
|
80
|
-
|
|
81
|
-
```ruby
|
|
82
|
-
# sample.rb:1
|
|
83
|
-
object.method
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
See also [Code Similarity](similarity_tutorial.md) tutorial.
|
|
87
|
-
|
|
88
|
-
## `-c` to search from code example
|
|
89
|
-
|
|
90
|
-
You can search for the exact expression with `-c`
|
|
91
|
-
|
|
92
|
-
$ fast -c 'object.method' sample.rb
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
# sample.rb:1
|
|
96
|
-
object.method
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Combining with `-d`, in the header you can see the generated expression.
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
$ fast -d -c 'object.method' sample.rb | head -n 3
|
|
103
|
-
|
|
104
|
-
The generated expression from AST was:
|
|
105
|
-
(send
|
|
106
|
-
(send nil :object) :method)
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Fastfile
|
|
110
|
-
|
|
111
|
-
`Fastfile` will loaded when you start a pattern with a dot. It means the pattern
|
|
112
|
-
will be a shortcut predefined on these Fastfiles.
|
|
113
|
-
|
|
114
|
-
It will make three attempts to load `Fastfile` defined in `$PWD`, `$HOME` or
|
|
115
|
-
checking if the `$FAST_FILE_DIR` is configured.
|
|
116
|
-
|
|
117
|
-
You can define a `Fastfile` in any project with your custom shortcuts and easy
|
|
118
|
-
check some code or run some task.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
## Shortcut examples
|
|
122
|
-
|
|
123
|
-
Create shortcuts with blocks enables introduce custom coding in
|
|
124
|
-
the scope of the `Fast` module.
|
|
125
|
-
|
|
126
|
-
### Print library version.
|
|
127
|
-
|
|
128
|
-
Let's say you'd like to show the version of your library. Your regular params
|
|
129
|
-
in the command line will look like:
|
|
130
|
-
|
|
131
|
-
$ fast '(casgn nil VERSION)' lib/*/version.rb
|
|
132
|
-
|
|
133
|
-
It will output but the command is not very handy. In order to just say `fast .version`
|
|
134
|
-
you can use the previous snippet in your `Fastfile`.
|
|
135
|
-
|
|
136
|
-
```ruby
|
|
137
|
-
Fast.shortcut(:version, '(casgn nil VERSION)', 'lib/fast/version.rb')
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
And calling `fast .version` it will output something like this:
|
|
141
|
-
|
|
142
|
-
```ruby
|
|
143
|
-
# lib/fast/version.rb:4
|
|
144
|
-
VERSION = '0.1.2'
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
We can also always override the files params passing some other target file
|
|
148
|
-
like `fast .version lib/other/file.rb` and it will reuse the other arguments
|
|
149
|
-
from command line but replace the target files.
|
|
150
|
-
|
|
151
|
-
### Bumping a gem version
|
|
152
|
-
|
|
153
|
-
While releasing a new gem version, we always need to mechanical go through the
|
|
154
|
-
`lib/<your_gem>/version.rb` and change the string value to bump the version
|
|
155
|
-
of your library. It's pretty mechanical and here is an example that allow you
|
|
156
|
-
to simple use `fast .bump_version`:
|
|
157
|
-
|
|
158
|
-
```ruby
|
|
159
|
-
Fast.shortcut :bump_version do
|
|
160
|
-
rewrite_file('lib/fast/version.rb', '(casgn nil VERSION (str _)') do |node|
|
|
161
|
-
target = node.children.last.loc.expression
|
|
162
|
-
pieces = target.source.split(".").map(&:to_i)
|
|
163
|
-
pieces.reverse.each_with_index do |fragment,i|
|
|
164
|
-
if fragment < 9
|
|
165
|
-
pieces[-(i+1)] = fragment +1
|
|
166
|
-
break
|
|
167
|
-
else
|
|
168
|
-
pieces[-(i+1)] = 0
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
replace(target, "'#{pieces.join(".")}'")
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
!!! note "Note the shortcut scope"
|
|
177
|
-
The shortcut calls `rewrite_file` from `Fast` scope as it use
|
|
178
|
-
`Fast.instance_exec` for shortcuts that yields blocks.
|
|
179
|
-
|
|
180
|
-
Checking the version:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
$ fast .version 13:58:40
|
|
184
|
-
# lib/fast/version.rb:4
|
|
185
|
-
VERSION = '0.1.2'
|
|
186
|
-
```
|
|
187
|
-
Bumping the version:
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
$ fast .bump_version 13:58:43
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
No output because we don't print anything. Checking version again:
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
$ fast .version 13:58:54
|
|
197
|
-
# lib/fast/version.rb:4
|
|
198
|
-
VERSION = '0.1.3'
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
And now a fancy shortcut to report the other shortcuts :)
|
|
202
|
-
|
|
203
|
-
```ruby
|
|
204
|
-
Fast.shortcut :shortcuts do
|
|
205
|
-
report(shortcuts.keys)
|
|
206
|
-
end
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Or we can make it a bit more friendly and also use Fast to process the shortcut
|
|
210
|
-
positions and pick the comment that each shortcut have in the previous line:
|
|
211
|
-
|
|
212
|
-
```ruby
|
|
213
|
-
# List all shortcut with comments
|
|
214
|
-
Fast.shortcut :shortcuts do
|
|
215
|
-
fast_files.each do |file|
|
|
216
|
-
lines = File.readlines(file).map{|line|line.chomp.gsub(/\s*#/,'').strip}
|
|
217
|
-
result = capture_file('(send ... shortcut $(sym _))', file)
|
|
218
|
-
result = [result] unless result.is_a?Array
|
|
219
|
-
result.each do |capture|
|
|
220
|
-
target = capture.loc.expression
|
|
221
|
-
puts "fast .#{target.source[1..-1].ljust(30)} # #{lines[target.line-2]}"
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
And it will be printing all loaded shortcuts with comments:
|
|
228
|
-
|
|
229
|
-
```
|
|
230
|
-
$ fast .shortcuts
|
|
231
|
-
fast .version # Let's say you'd like to show the version that is over the version file
|
|
232
|
-
fast .parser # Simple shortcut that I used often to show how the expression parser works
|
|
233
|
-
fast .bump_version # Use `fast .bump_version` to rewrite the version file
|
|
234
|
-
fast .shortcuts # List all shortcut with comments
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
You can find more examples in the [Fastfile](https://github.com/jonatas/fast/tree/master/Fastfile).
|
|
238
|
-
|
data/docs/editors-integration.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Editors' integration
|
|
2
|
-
|
|
3
|
-
We don't have any proper integration or official plugins for editors yet.
|
|
4
|
-
|
|
5
|
-
Here are a few ideas you can use to make your own flow.
|
|
6
|
-
|
|
7
|
-
## Vim
|
|
8
|
-
|
|
9
|
-
Split terminal vertically and open fast focused on build the expression.
|
|
10
|
-
|
|
11
|
-
```vim
|
|
12
|
-
nnoremap <Leader>ff :vsplit \| terminal fast "()" % <Left><Left><Left><Left><Left>
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Or you can build a function:
|
|
16
|
-
|
|
17
|
-
```vim
|
|
18
|
-
function! s:Fast(args)
|
|
19
|
-
let cmd = ''
|
|
20
|
-
if !empty(b:ruby_project_root)
|
|
21
|
-
let cmd .= 'cd ' . b:ruby_project_root . ' && '
|
|
22
|
-
endif
|
|
23
|
-
|
|
24
|
-
let cmd .= 'fast --no-color ' . a:args
|
|
25
|
-
|
|
26
|
-
let custom_maker = neomake#utils#MakerFromCommand(cmd)
|
|
27
|
-
let custom_maker.name = cmd
|
|
28
|
-
let custom_maker.cwd = b:ruby_project_root
|
|
29
|
-
let custom_maker.remove_invalid_entries = 0
|
|
30
|
-
" e.g.:
|
|
31
|
-
" # path/to/file.rb:1141
|
|
32
|
-
" my_method(
|
|
33
|
-
" :boom,
|
|
34
|
-
" arg1: 1,
|
|
35
|
-
" )
|
|
36
|
-
" %W# %f:%l -> start a multiline warning when the line matches '# path/file.rb:1234'
|
|
37
|
-
" %-Z# end multiline warning on the next line that starts with '#'
|
|
38
|
-
" %C%m continued multiline warning message
|
|
39
|
-
let custom_maker.errorformat = '%W# %f:%l, %-Z#, %C%m'
|
|
40
|
-
let enabled_makers = [custom_maker]
|
|
41
|
-
update | call neomake#Make(0, enabled_makers) | echom "running: " . cmd
|
|
42
|
-
endfunction
|
|
43
|
-
command! -complete=file -nargs=1 Fast call s:Fast(<q-args>)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Check the conversation about vim integration [here](https://github.com/jonatas/fast/pull/16#issuecomment-555115606).
|
data/docs/experiments.md
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
# Experiments
|
|
2
|
-
|
|
3
|
-
<center></center>
|
|
4
|
-
|
|
5
|
-
Experiments allow us to play with AST and do some code transformation, execute
|
|
6
|
-
some code and continue combining successful transformations.
|
|
7
|
-
|
|
8
|
-
The major idea is try a new approach without any promise and if it works
|
|
9
|
-
continue transforming the code.
|
|
10
|
-
|
|
11
|
-
## Replace `FactoryBot#create` with `build_stubbed`.
|
|
12
|
-
|
|
13
|
-
Let's look into the following spec example:
|
|
14
|
-
|
|
15
|
-
```ruby
|
|
16
|
-
describe "my spec" do
|
|
17
|
-
let(:user) { create(:user) }
|
|
18
|
-
let(:address) { create(:address) }
|
|
19
|
-
# ...
|
|
20
|
-
end
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Let's say we're amazed with `FactoryBot#build_stubbed` and want to build a small
|
|
24
|
-
bot to make the changes in a entire code base. Skip some database
|
|
25
|
-
touches while testing huge test suites are always a good idea.
|
|
26
|
-
|
|
27
|
-
First we can hunt for the cases we want to find:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
$ ruby-parse -e "create(:user)"
|
|
31
|
-
(send nil :create
|
|
32
|
-
(sym :user))
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Using `fast` in the command line to see real examples in the `spec` folder:
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
$ fast "(send nil create)" spec
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
If you don't have a real project but want to test, just create a sample ruby
|
|
42
|
-
file with the code example above.
|
|
43
|
-
|
|
44
|
-
Running it in a big codebase will probably find a few examples of blocks.
|
|
45
|
-
|
|
46
|
-
The next step is build a replacement of each independent occurrence to use
|
|
47
|
-
`build_stubbed` instead of create and combine the successful ones, run again and
|
|
48
|
-
combine again, until try all kind of successful replacements combined.
|
|
49
|
-
|
|
50
|
-
Considering we have the following code in `sample_spec.rb`:
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
describe "my spec" do
|
|
54
|
-
let(:user) { create(:user) }
|
|
55
|
-
let(:address) { create(:address) }
|
|
56
|
-
# ...
|
|
57
|
-
end
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Let's create the experiment that will contain the nodes that are target to be
|
|
61
|
-
executed and what we want to do when we find the node.
|
|
62
|
-
|
|
63
|
-
```ruby
|
|
64
|
-
experiment = Fast.experiment('RSpec/ReplaceCreateWithBuildStubbed') do
|
|
65
|
-
search '(send nil create)'
|
|
66
|
-
edit { |node| replace(node.loc.selector, 'build_stubbed') }
|
|
67
|
-
end
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
If we use `Fast.replace_file` it will replace all occurrences in the same run
|
|
71
|
-
and that's one of the motivations behind create the `ExperimentFile` class.
|
|
72
|
-
|
|
73
|
-
Executing a partial replacement of the first occurrence:
|
|
74
|
-
|
|
75
|
-
```ruby
|
|
76
|
-
experiment_file = Fast::ExperimentFile.new('sample_spec.rb', experiment) }
|
|
77
|
-
puts experiment_file.partial_replace(1)
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
The command will output the following code:
|
|
81
|
-
|
|
82
|
-
```ruby
|
|
83
|
-
describe "my spec" do
|
|
84
|
-
let(:user) { build_stubbed(:user) }
|
|
85
|
-
let(:address) { create(:address) }
|
|
86
|
-
# ...
|
|
87
|
-
end
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Remove useless before block
|
|
91
|
-
|
|
92
|
-
Imagine the following code sample:
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
describe "my spec" do
|
|
96
|
-
before { create(:user) }
|
|
97
|
-
# ...
|
|
98
|
-
after { User.delete_all }
|
|
99
|
-
end
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
And now, we can define an experiment that removes the entire code block and run
|
|
103
|
-
the experimental specs.
|
|
104
|
-
|
|
105
|
-
```ruby
|
|
106
|
-
experiment = Fast.experiment('RSpec/RemoveUselessBeforeAfterHook') do
|
|
107
|
-
lookup 'spec'
|
|
108
|
-
search '(block (send nil {before after}))'
|
|
109
|
-
edit { |node| remove(node.loc.expression) }
|
|
110
|
-
policy { |new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
|
111
|
-
end
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
To run the experiment you can simply say:
|
|
115
|
-
|
|
116
|
-
```ruby
|
|
117
|
-
experiment.run
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Or drop the code into `experiments` folder and use the `fast-experiment` command
|
|
121
|
-
line tool.
|
|
122
|
-
|
|
123
|
-
$ fast-experiment RSpec/RemoveUselessBeforeAfterHook spec
|
|
124
|
-
|
|
125
|
-
## DSL
|
|
126
|
-
|
|
127
|
-
- In the `lookup` you can pass files or folders.
|
|
128
|
-
- The `search` contains the expression you want to match
|
|
129
|
-
- With `edit` block you can apply the code change
|
|
130
|
-
- And the `policy` is executed to check if the current change is valuable
|
|
131
|
-
|
|
132
|
-
If the file contains multiple `before` or `after` blocks, each removal will
|
|
133
|
-
occur independently and the successfull removals will be combined as a
|
|
134
|
-
secondary change. The process repeates until find all possible combinations.
|
|
135
|
-
|
|
136
|
-
See more examples in [experiments](experiments) folder.
|
|
137
|
-
|
|
138
|
-
To run multiple experiments, use `fast-experiment` runner:
|
|
139
|
-
|
|
140
|
-
```
|
|
141
|
-
fast-experiment <experiment-names> <files-or-folders>
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
You can limit experiments or file escope:
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
fast-experiment RSpec/RemoveUselessBeforeAfterHook spec/models/**/*_spec.rb
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Or a single file:
|
|
151
|
-
|
|
152
|
-
```
|
|
153
|
-
fast-experiment RSpec/ReplaceCreateWithBuildStubbed spec/models/my_spec.rb
|
|
154
|
-
```
|
|
155
|
-
|
data/docs/git.md
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
You can overload the AST node with extra methods to get information from Git.
|
|
3
|
-
|
|
4
|
-
Let's start with some basic setup to reuse in the next examples:
|
|
5
|
-
|
|
6
|
-
## Git require
|
|
7
|
-
|
|
8
|
-
By default, this extension is not loaded in the fast environment, so you should
|
|
9
|
-
require it.
|
|
10
|
-
|
|
11
|
-
```ruby
|
|
12
|
-
require 'fast/git'
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Then it will work with any AST node.
|
|
17
|
-
|
|
18
|
-
```ruby
|
|
19
|
-
ast = Fast.ast_from_file('lib/fast.rb')
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Log
|
|
23
|
-
|
|
24
|
-
First commit from git:
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
ast.git_log.first.author.name # => "Jonatas Davi Paganini"
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
It uses [ruby-git](https://github.com/ruby-git/ruby-git#examples) gem, so all
|
|
31
|
-
methods are available:
|
|
32
|
-
|
|
33
|
-
```ruby
|
|
34
|
-
ast.git_log.since(Time.mktime(2019)).entries.map(&:message)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Counting commits per year:
|
|
38
|
-
|
|
39
|
-
```ruby
|
|
40
|
-
ast.git_log.entries.group_by{|t|t.date.year}.transform_values(&:size)
|
|
41
|
-
# => {2020=>4, 2019=>22, 2018=>4}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Counting commits per contributor:
|
|
45
|
-
|
|
46
|
-
```ruby
|
|
47
|
-
ast.git_log.entries.group_by{|t|t.author.name}.transform_values(&:size)
|
|
48
|
-
# => {"Jônatas Davi Paganini"=>29, ...}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Selecting last commit message:
|
|
52
|
-
|
|
53
|
-
```ruby
|
|
54
|
-
ast.last_commit.message # => "Add node extensions for extracting info from git (#21)"
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Remote git URL:
|
|
58
|
-
|
|
59
|
-
```ruby
|
|
60
|
-
ast.remote_url # => "git@github.com:jonatas/fast.git"
|
|
61
|
-
ast.project_url # => "https://github.com/jonatas/fast"
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The `sha` from last commit:
|
|
65
|
-
|
|
66
|
-
```ruby
|
|
67
|
-
ast.sha # => "cd1c036b55ec1d41e5769ad73b282dd6429a90a6"
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Pick a link from the files to master version:
|
|
71
|
-
|
|
72
|
-
```ruby
|
|
73
|
-
ast.link # => "https://github.com/jonatas/fast/blob/master/lib/fast.rb#L3-L776"
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Getting permalink from current commit:
|
|
77
|
-
|
|
78
|
-
```ruby
|
|
79
|
-
ast.permalink # => "https://github.com/jonatas/fast/blob/cd1c036b55ec1d41e5769ad73b282dd6429a90a6/lib/fast.rb#L3-L776"
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Markdown link
|
|
83
|
-
|
|
84
|
-
Let's say you'd like to capture a list of class names that inherits the `Find`
|
|
85
|
-
class:
|
|
86
|
-
|
|
87
|
-
```ruby
|
|
88
|
-
puts ast.capture("(class $(const nil _) (const nil Find)").map(&:md_link).join("\n* ")
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
It will output the following links:
|
|
92
|
-
|
|
93
|
-
* [FindString](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L485)
|
|
94
|
-
* [MethodCall](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L496)
|
|
95
|
-
* [InstanceMethodCall](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L507)
|
|
96
|
-
* [FindWithCapture](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L524)
|
|
97
|
-
* [FindFromArgument](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L551)
|
|
98
|
-
* [Capture](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L598)
|
|
99
|
-
* [Parent](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L622)
|
|
100
|
-
* [Any](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L636)
|
|
101
|
-
* [All](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L647)
|
|
102
|
-
* [Not](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L659)
|
|
103
|
-
* [Maybe](https://github.com/jonatas/fast/blob/master/lib/fast.rb#L667)
|
|
104
|
-
|
|
105
|
-
## Permalink
|
|
106
|
-
|
|
107
|
-
If you need to get a permanent link to the code, use the `permalink` method:
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
ast.search("(class (const nil _) (const nil Find)").map(&:permalink)
|
|
111
|
-
# => ["https://github.com/jonatas/fast/blob/cd1c036b55ec1d41e5769ad73b282dd6429a90a6/lib/fast.rb#L524-L541",
|
|
112
|
-
# "https://github.com/jonatas/fast/blob/cd1c036b55ec1d41e5769ad73b282dd6429a90a6/lib/fast.rb#L551-L571", ...]
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
|
data/docs/ideas.md
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Ideas I want to build with Fast
|
|
2
|
-
|
|
3
|
-
I don't have all the time I need to develop all the ideas I have to build
|
|
4
|
-
around this tool, so here is a dump of a few brainstormings:
|
|
5
|
-
|
|
6
|
-
## Inline target code
|
|
7
|
-
|
|
8
|
-
I started [fast-inline](https://github.com/jonatas/fast-inline) that can be
|
|
9
|
-
useful to try to see how much every library is used in a project.
|
|
10
|
-
|
|
11
|
-
My idea is try to inline some specific method call to understand if it makes
|
|
12
|
-
sense to have an entire library in the stock.
|
|
13
|
-
|
|
14
|
-
Understanding dependencies and how the code works can be a first step to get an
|
|
15
|
-
"algorithm as a service". Instead of loading everything from the library, it
|
|
16
|
-
would facilitate the cherry pick of only the proper dependencies necessaries to
|
|
17
|
-
run the code you have and not the code that is overloading the project.
|
|
18
|
-
|
|
19
|
-
## Neo4J adapter
|
|
20
|
-
|
|
21
|
-
Easy pipe fast results to Neo4J. It would facilitate to explore more complex
|
|
22
|
-
scenarios and combine data from other sources.
|
|
23
|
-
|
|
24
|
-
## Ast Diff
|
|
25
|
-
|
|
26
|
-
Allow to compare and return a summary of differences between two trees.
|
|
27
|
-
|
|
28
|
-
It would be useful to identify renamings or other small changes, like only
|
|
29
|
-
changes in comments that does not affect the file and possibly be ignored for
|
|
30
|
-
some operations like run or not run tests.
|
|
31
|
-
|
|
32
|
-
## Transition synapses
|
|
33
|
-
|
|
34
|
-
Following the previous idea, it would be great if we can understand the
|
|
35
|
-
transition synapses and make it easily available to catch up with previous
|
|
36
|
-
learnings.
|
|
37
|
-
|
|
38
|
-
https://github.com/jonatas/chewy-diff/blob/master/lib/chewy/diff.rb
|
|
39
|
-
|
|
40
|
-
This example, shows adds and removals from specific node targets between two
|
|
41
|
-
different files.
|
|
42
|
-
|
|
43
|
-
If we start tracking AST transition synapses and associating with "Fixes" or
|
|
44
|
-
"Reverts" we can predict introduction of new bugs by inpecting if the
|
|
45
|
-
introduction of new patterns that can be possibly reverted or improved.
|
|
46
|
-
|
|
47
|
-
## Fast Rewriter with pure strings
|
|
48
|
-
|
|
49
|
-
As the AST rewriter adopts a custom block that needs to implement ruby code,
|
|
50
|
-
we can expand the a query language for rewriting files without need to take the
|
|
51
|
-
custom Ruby block.
|
|
52
|
-
|
|
53
|
-
Example:
|
|
54
|
-
|
|
55
|
-
```ruby
|
|
56
|
-
Fast.gsub_expression('remove(@expression)') # (node) => { remove(node.location.expression) }
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
And later we can bind it in the command line to allow implement custom
|
|
60
|
-
replacements without need to write a ruby file.
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
fast (def my_target_method) lib spec --rewrite "remove(@expression)"
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
or
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
fast (def my_target_method) lib spec --rewrite "replace(@name, 'renamed_method')"
|
|
70
|
-
```
|