ffast 0.1.9 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +104 -1
- data/.sourcelevel.yml +2 -0
- data/.travis.yml +1 -1
- data/Fastfile +59 -3
- data/README.md +228 -130
- data/bin/console +5 -0
- data/docs/experiments.md +2 -0
- data/docs/git.md +115 -0
- data/docs/ideas.md +0 -10
- data/docs/index.md +2 -0
- data/docs/sql-support.md +253 -0
- data/docs/videos.md +5 -1
- data/docs/walkthrough.md +135 -0
- data/fast.gemspec +24 -4
- data/lib/fast/cli.rb +102 -24
- data/lib/fast/experiment.rb +1 -2
- data/lib/fast/git.rb +101 -0
- data/lib/fast/shortcut.rb +13 -11
- data/lib/fast/sql/rewriter.rb +69 -0
- data/lib/fast/sql.rb +167 -0
- data/lib/fast/version.rb +1 -1
- data/lib/fast.rb +114 -17
- data/mkdocs.yml +10 -1
- metadata +66 -15
data/docs/git.md
ADDED
@@ -0,0 +1,115 @@
|
|
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
CHANGED
@@ -21,16 +21,6 @@ run the code you have and not the code that is overloading the project.
|
|
21
21
|
Easy pipe fast results to Neo4J. It would facilitate to explore more complex
|
22
22
|
scenarios and combine data from other sources.
|
23
23
|
|
24
|
-
## Git adapter
|
25
|
-
|
26
|
-
Add extra tags to nodes with information from Git.
|
27
|
-
|
28
|
-
* Revision
|
29
|
-
* Author
|
30
|
-
* Date
|
31
|
-
|
32
|
-
Tag every node with the proper author.
|
33
|
-
|
34
24
|
## Ast Diff
|
35
25
|
|
36
26
|
Allow to compare and return a summary of differences between two trees.
|
data/docs/index.md
CHANGED
data/docs/sql-support.md
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
# SQL Support
|
2
|
+
|
3
|
+
Fast is partially supporting SQL syntax. Behind the scenes it parses SQL using
|
4
|
+
[pg_query](https://github.com/pganalyze/pg_query) and simplifies it to AST Nodes
|
5
|
+
using the same interface. It's using Postgresql parser behind the scenes,
|
6
|
+
but probably could be useful for other SQL similar diallects.
|
7
|
+
|
8
|
+
|
9
|
+
!!! info "fast auto detects SQL files in the command line"
|
10
|
+
|
11
|
+
By default, this module is not included into the main library.
|
12
|
+
Fast can auto-detect file extensions and choose the sql path in case the
|
13
|
+
file relates to sql.
|
14
|
+
|
15
|
+
Use `fast --sql` in case you want to force the usage of the SQL parser.
|
16
|
+
|
17
|
+
```
|
18
|
+
|
19
|
+
```
|
20
|
+
|
21
|
+
|
22
|
+
# Parsing a sql content
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'fast/sql'
|
26
|
+
ast = Fast.parse_sql('select 1')
|
27
|
+
# => s(:select_stmt,
|
28
|
+
# s(:target_list,
|
29
|
+
# s(:res_target,
|
30
|
+
# s(:val,
|
31
|
+
# s(:a_const,
|
32
|
+
# s(:val,
|
33
|
+
# s(:integer,
|
34
|
+
# s(:ival, 1))))))))
|
35
|
+
```
|
36
|
+
|
37
|
+
## Why it's interesting to use AST for SQL?
|
38
|
+
|
39
|
+
Both SQL are available and do the same thing:
|
40
|
+
```sql
|
41
|
+
select * from customers
|
42
|
+
```
|
43
|
+
or
|
44
|
+
```sql
|
45
|
+
table customers
|
46
|
+
```
|
47
|
+
|
48
|
+
they have exactly the same objective but written down in very different syntax.
|
49
|
+
|
50
|
+
Give a try:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
Fast.parse_sql("select * from customers") == Fast.parse_sql("table customers") # => true
|
54
|
+
```
|
55
|
+
|
56
|
+
## Match
|
57
|
+
|
58
|
+
Use `match?` with your node pattern to traverse the abstract syntax tree.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
Fast.match?("(select_stmt ...)", ast) # => true
|
62
|
+
```
|
63
|
+
|
64
|
+
Use `$` to capture elements from the AST:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
Fast.match?("(select_stmt $...)", ast)
|
68
|
+
=> [s(:target_list,
|
69
|
+
s(:res_target,
|
70
|
+
s(:val,
|
71
|
+
s(:a_const,
|
72
|
+
s(:val,
|
73
|
+
s(:integer,
|
74
|
+
s(:ival, 1)))))))]
|
75
|
+
|
76
|
+
```
|
77
|
+
|
78
|
+
You can dig deeper into the AST specifying nodes:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Fast.match?("(select_stmt (target_list (res_target (val ($...)))))", ast)
|
82
|
+
# => [s(:a_const,
|
83
|
+
# s(:val,
|
84
|
+
# s(:integer,
|
85
|
+
# s(:ival, 1))))]
|
86
|
+
```
|
87
|
+
|
88
|
+
And ignoring node types or values using `_`. Check all [syntax](/syntax) options.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
Fast.match?("(select_stmt (_ (_ (val ($...)))))", ast)
|
92
|
+
# => [s(:a_const,
|
93
|
+
# s(:val,
|
94
|
+
# s(:integer,
|
95
|
+
# s(:ival, 1))))]
|
96
|
+
```
|
97
|
+
|
98
|
+
## Search directly from the AST
|
99
|
+
|
100
|
+
You can also search directly from nodes and keep digging:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
ast = Fast.parse_sql('select 1');
|
104
|
+
ast.search('ival') # => [s(:ival, s(:ival, 1))]
|
105
|
+
```
|
106
|
+
|
107
|
+
Use first to return the node directly:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
ast.first('(ival (ival _))') #=> s(:ival, s(:ival, 1))
|
111
|
+
```
|
112
|
+
|
113
|
+
Combine the `capture` method with `$`:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
ast.capture('(ival (ival $_))') # => [1]
|
117
|
+
```
|
118
|
+
|
119
|
+
|
120
|
+
# Examples
|
121
|
+
|
122
|
+
Let's dive into a more complex example capturing fields and from clause of a
|
123
|
+
condition. Let's start parsing the sql:
|
124
|
+
|
125
|
+
## Capturing fields and where clause
|
126
|
+
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
ast = Fast.parse_sql('select name from customer')
|
130
|
+
# => s(:select_stmt,
|
131
|
+
# s(:target_list,
|
132
|
+
# s(:res_target,
|
133
|
+
# s(:val,
|
134
|
+
# s(:column_ref,
|
135
|
+
# s(:fields,
|
136
|
+
# s(:string,
|
137
|
+
# s(:str, "name"))))))),
|
138
|
+
# s(:from_clause,
|
139
|
+
# s(:range_var,
|
140
|
+
# s(:relname, "customer"),
|
141
|
+
# s(:inh, true),
|
142
|
+
# s(:relpersistence, "p"))))
|
143
|
+
```
|
144
|
+
|
145
|
+
Now, let's build the expression to get the fields and from_clause.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
cols_and_from = "
|
149
|
+
(select_stmt
|
150
|
+
(target_list (res_target (val (column_ref (fields $...)))))
|
151
|
+
(from_clause (range_var $(relname _))))
|
152
|
+
"
|
153
|
+
```
|
154
|
+
|
155
|
+
Now, we can use `Fast.capture` or `Fast.match?` to extract the values from the
|
156
|
+
AST.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
Fast.capture(cols_and_from, ast)
|
160
|
+
# => [s(:string,
|
161
|
+
# s(:str, "name")), s(:relname, "customer")]
|
162
|
+
```
|
163
|
+
|
164
|
+
## Search inside
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
relname = Fast.parse_sql('select name from customer').search('relname').first
|
168
|
+
# => s(:relname, "customer")
|
169
|
+
```
|
170
|
+
|
171
|
+
Find the location of a node.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
relname.location # => #<Parser::Source::Map:0x00007fd3bcb0b7f0
|
175
|
+
# @expression=#<Parser::Source::Range (sql) 17...25>,
|
176
|
+
# @node=s(:relname, "customer")>
|
177
|
+
```
|
178
|
+
|
179
|
+
The location can be useful to allow you to do refactorings and find specific
|
180
|
+
delimitations of objects in the string.
|
181
|
+
|
182
|
+
The attribute `expression` gives access to the source range.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
relname.location.expression
|
186
|
+
# => #<Parser::Source::Range (sql) 17...25>
|
187
|
+
```
|
188
|
+
|
189
|
+
The `source_buffer` is shared and can be accessed through the expression.
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
relname.location.expression.source_buffer
|
193
|
+
# => #<Fast::SQL::SourceBuffer:0x00007fd3bc2a6420
|
194
|
+
# @name="(sql)",
|
195
|
+
# @source="select name from customer",
|
196
|
+
# @tokens=
|
197
|
+
# [<PgQuery::ScanToken: start: 0, end: 6, token: :SELECT, keyword_kind: :RESERVED_KEYWORD>,
|
198
|
+
# <PgQuery::ScanToken: start: 7, end: 11, token: :NAME_P, keyword_kind: :UNRESERVED_KEYWORD>,
|
199
|
+
# <PgQuery::ScanToken: start: 12, end: 16, token: :FROM, keyword_kind: :RESERVED_KEYWORD>,
|
200
|
+
# <PgQuery::ScanToken: start: 17, end: 25, token: :IDENT, keyword_kind: :NO_KEYWORD>]>
|
201
|
+
```
|
202
|
+
|
203
|
+
The tokens are useful to find the proper node location during the build but
|
204
|
+
they're not available for all the nodes, so, it can be very handy as an extra
|
205
|
+
reference.
|
206
|
+
|
207
|
+
## Replace
|
208
|
+
|
209
|
+
Replace fragments of your SQL based on AST can also be done with all the work
|
210
|
+
inherited from Parser::TreeRewriter components.
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
Fast.parse_sql('select 1').replace('ival', '2') # => "select 2"
|
214
|
+
```
|
215
|
+
|
216
|
+
The previous example is a syntax sugar for the following code:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
Fast.replace_sql('ival',
|
220
|
+
Fast.parse_sql('select 1'),
|
221
|
+
&->(node){ replace(node.location.expression, '2') }
|
222
|
+
) # => "select 2"
|
223
|
+
```
|
224
|
+
|
225
|
+
The last argument is a proc that runs on the [parser tree rewriter](https://www.rubydoc.info/gems/parser/Parser/TreeRewriter
|
226
|
+
) scope.
|
227
|
+
|
228
|
+
Let's break down the previous code:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
ast = Fast.parse_sql("select 1")
|
232
|
+
# => s(:select_stmt,
|
233
|
+
# s(:target_list,
|
234
|
+
# s(:res_target,
|
235
|
+
# s(:val,
|
236
|
+
# s(:a_const,
|
237
|
+
# s(:ival,
|
238
|
+
# s(:ival, 1)))))))
|
239
|
+
```
|
240
|
+
|
241
|
+
The pattern is simply matching node type that is `ival` but it could be a complex expression
|
242
|
+
like `(val (a_const (val (ival (ival _)))))`.
|
243
|
+
|
244
|
+
Completing the example:
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
Fast.replace_sql("ival", ast, &-> (n) { replace(n.loc.expression, "3") })
|
248
|
+
# => "select 3"
|
249
|
+
```
|
250
|
+
|
251
|
+
`loc` is a shortcut for `location` attribute.
|
252
|
+
|
253
|
+
|
data/docs/videos.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# Videos
|
2
2
|
|
3
|
-
- Grepping Ruby code like a boss
|
3
|
+
- [Ruby Kaigi TakeOut 2020: Grepping Ruby code like a boss](https://www.youtube.com/watch?v=YczrZQC9aP8&feature=youtu.be&)
|
4
|
+
|
5
|
+
<iframe width="560" height="315" src="https://www.youtube.com/embed/YczrZQC9aP8?" 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/#_=_).
|
4
8
|
|
5
9
|
- Introduction to [inline code](https://www.youtube.com/watch?v=KQXglNLUv7o).
|
6
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>
|
data/docs/walkthrough.md
ADDED
@@ -0,0 +1,135 @@
|
|
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
|
+
|
data/fast.gemspec
CHANGED
@@ -7,7 +7,7 @@ require 'fast/version'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'ffast'
|
9
9
|
spec.version = Fast::VERSION
|
10
|
-
spec.required_ruby_version = '>= 2.
|
10
|
+
spec.required_ruby_version = '>= 2.6'
|
11
11
|
spec.authors = ['Jônatas Davi Paganini']
|
12
12
|
spec.email = ['jonatasdp@gmail.com']
|
13
13
|
spec.homepage = 'https://jonatas.github.io/fast/'
|
@@ -17,9 +17,27 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.license = 'MIT'
|
18
18
|
|
19
19
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
20
|
-
f.match(%r{^(test|spec|features)/})
|
20
|
+
f.match(%r{^(test|spec|features|docs\/(assets|stylesheets))/})
|
21
21
|
end
|
22
22
|
|
23
|
+
spec.post_install_message = <<~THANKS
|
24
|
+
|
25
|
+
==========================================================
|
26
|
+
Yay! Thanks for installing
|
27
|
+
|
28
|
+
___ __ ___
|
29
|
+
|__ /\ /__` |
|
30
|
+
| /~~\ .__/ |
|
31
|
+
|
32
|
+
To interactive learn about the gem in the terminal use:
|
33
|
+
|
34
|
+
fast .intro
|
35
|
+
|
36
|
+
More docs at: https://jonatas.github.io/fast/
|
37
|
+
==========================================================
|
38
|
+
|
39
|
+
THANKS
|
40
|
+
|
23
41
|
spec.bindir = 'bin'
|
24
42
|
spec.executables = %w[fast fast-experiment]
|
25
43
|
spec.require_paths = %w[lib experiments]
|
@@ -28,15 +46,17 @@ Gem::Specification.new do |spec|
|
|
28
46
|
spec.add_dependency 'coderay'
|
29
47
|
spec.add_dependency 'parallel'
|
30
48
|
spec.add_dependency 'parser'
|
49
|
+
spec.add_dependency 'pg_query'
|
31
50
|
|
32
51
|
spec.add_development_dependency 'bundler'
|
52
|
+
spec.add_development_dependency 'git'
|
33
53
|
spec.add_development_dependency 'guard'
|
34
54
|
spec.add_development_dependency 'guard-livereload'
|
35
55
|
spec.add_development_dependency 'guard-rspec'
|
36
56
|
spec.add_development_dependency 'pry'
|
37
57
|
spec.add_development_dependency 'rake'
|
38
|
-
spec.add_development_dependency 'rspec'
|
39
|
-
spec.add_development_dependency 'rspec-its'
|
58
|
+
spec.add_development_dependency 'rspec'
|
59
|
+
spec.add_development_dependency 'rspec-its'
|
40
60
|
spec.add_development_dependency 'rubocop'
|
41
61
|
spec.add_development_dependency 'rubocop-performance'
|
42
62
|
spec.add_development_dependency 'rubocop-rspec'
|