ffast 0.0.8 → 0.0.9
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/README.md +191 -263
- data/docs/index.md +2 -4
- data/lib/fast.rb +239 -53
- data/lib/fast/experiment.rb +164 -28
- data/lib/fast/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2cd44ce2405cd969c26c841d7d63d7c15b85cf7a0747ce1f2e08eebff83c490
|
4
|
+
data.tar.gz: 848f0e3ce2904183e3f0f9ec44590110efb4786ddc98862c1671fec68c11df88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 461a90f7d5d42f9abe6fd425c85d3f6e36dc34fc3afa5abd1ce11f87492315166313e4a9b6876d5b8ada3323264c624fd965c07b1f4040c87c67a3044056ca9d
|
7
|
+
data.tar.gz: 1118edfdc9f336d8fc7f351575ed5aaf93720856633e12b899a7c8b0e71818f9bd82979571b0c84b2419794ad960b699ea2dc7d91936d6bc3e7daf26986fe000
|
data/README.md
CHANGED
@@ -51,56 +51,40 @@ to represent code called `s-expressions`.
|
|
51
51
|
|
52
52
|
For example, let's take an `Integer` in Ruby:
|
53
53
|
|
54
|
-
|
55
|
-
1
|
56
|
-
```
|
54
|
+
1
|
57
55
|
|
58
56
|
It's corresponding s-expression would be:
|
59
57
|
|
60
|
-
|
61
|
-
s(:int, 1)
|
62
|
-
```
|
58
|
+
s(:int, 1)
|
63
59
|
|
64
60
|
`s` in `Fast` and `Parser` are a shorthand for creating an `Parser::AST::Node`.
|
65
61
|
Each of these nodes has a `#type` and `#children` contained in it:
|
66
62
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
```
|
63
|
+
def s(type, *children)
|
64
|
+
Parser::AST::Node.new(type, children)
|
65
|
+
end
|
72
66
|
|
73
67
|
### Variable Assignments
|
74
68
|
|
75
69
|
Now let's take a look at a local variable assignment:
|
76
70
|
|
77
|
-
|
78
|
-
value = 42
|
79
|
-
```
|
71
|
+
value = 42
|
80
72
|
|
81
73
|
It's corresponding s-expression would be:
|
82
74
|
|
83
|
-
|
84
|
-
ast = s(:lvasgn, :value, s(:int, 42))
|
85
|
-
```
|
75
|
+
ast = s(:lvasgn, :value, s(:int, 42))
|
86
76
|
|
87
77
|
If we wanted to find this particular assignment somewhere in our AST, we can use
|
88
78
|
Fast to look for a local variable named `value` with a value `42`:
|
89
79
|
|
90
|
-
|
91
|
-
Fast.match?(ast, '(lvasgn value (int 42))')
|
92
|
-
# => true
|
93
|
-
```
|
80
|
+
Fast.match?(ast, '(lvasgn value (int 42))') # => true
|
94
81
|
|
95
82
|
### Wildcard Token
|
96
83
|
|
97
84
|
If we wanted to find a variable named `value` that was assigned any integer value
|
98
85
|
we could replace `42` in our query with an underscore ( `_` ) as a shortcut:
|
99
86
|
|
100
|
-
|
101
|
-
Fast.match?(ast, '(lvasgn value (int _))')
|
102
|
-
# => true
|
103
|
-
```
|
87
|
+
Fast.match?(ast, '(lvasgn value (int _))') # => true
|
104
88
|
|
105
89
|
### Set Inclusion Token
|
106
90
|
|
@@ -108,10 +92,7 @@ If we weren't sure the type of the value we're assigning, we can use our set
|
|
108
92
|
inclusion token (`{}`) from earlier to tell Fast that we expect either a `Float`
|
109
93
|
or an `Integer`:
|
110
94
|
|
111
|
-
|
112
|
-
Fast.match?(ast, '(lvasgn value ({float int} _))')
|
113
|
-
# => true
|
114
|
-
```
|
95
|
+
Fast.match?(ast, '(lvasgn value ({float int} _))') # => true
|
115
96
|
|
116
97
|
### All Matching Token
|
117
98
|
|
@@ -120,42 +101,28 @@ all matching token (`[]`) to express multiple conditions that need to be true.
|
|
120
101
|
In this case we don't want the value to be a `String`, `Hash`, or an `Array` by
|
121
102
|
prefixing all of the types with `!`:
|
122
103
|
|
123
|
-
|
124
|
-
Fast.match?(ast, '(lvasgn value ([!str !hash !array] _))') # true
|
125
|
-
```
|
104
|
+
Fast.match?(ast, '(lvasgn value ([!str !hash !array] _))') # => true
|
126
105
|
|
127
106
|
### Node Child Token
|
128
107
|
|
129
108
|
We can match any node with children by using the child token ( `...` ):
|
130
109
|
|
131
|
-
|
132
|
-
Fast.match?(ast, '(lvasgn value ...)')
|
133
|
-
# => true
|
134
|
-
```
|
110
|
+
Fast.match?(ast, '(lvasgn value ...)') # => true
|
135
111
|
|
136
112
|
We could even match any local variable assignment combining both `_` and `...`:
|
137
113
|
|
138
|
-
|
139
|
-
Fast.match?(ast, '(lvasgn _ ...)')
|
140
|
-
# => true
|
141
|
-
```
|
114
|
+
Fast.match?(ast, '(lvasgn _ ...)') # => true
|
142
115
|
|
143
116
|
### Capturing the Value of an Expression
|
144
117
|
|
145
118
|
You can use `$` to capture the contents of an expression for later use:
|
146
119
|
|
147
|
-
|
148
|
-
Fast.match?(ast, '(lvasgn value $...)')
|
149
|
-
# => [s(:int, 42)]
|
150
|
-
```
|
120
|
+
Fast.match?(ast, '(lvasgn value $...)') # => [s(:int, 42)]
|
151
121
|
|
152
122
|
Captures can be used in any position as many times as you want to capture whatever
|
153
123
|
information you might need:
|
154
124
|
|
155
|
-
|
156
|
-
Fast.match?(ast, '(lvasgn $_ $...)')
|
157
|
-
# => [:value, s(:int, 42)]
|
158
|
-
```
|
125
|
+
Fast.match?(ast, '(lvasgn $_ $...)') # => [:value, s(:int, 42)]
|
159
126
|
|
160
127
|
> Keep in mind that `_` means something not nil and `...` means a node with
|
161
128
|
> children.
|
@@ -166,49 +133,42 @@ You can also define custom methods to set more complicated rules. Let's say
|
|
166
133
|
we're looking for duplicated methods in the same class. We need to collect
|
167
134
|
method names and guarantee they are unique.
|
168
135
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
136
|
+
def duplicated(method_name)
|
137
|
+
@methods ||= []
|
138
|
+
already_exists = @methods.include?(method_name)
|
139
|
+
@methods << method_name
|
140
|
+
already_exists
|
141
|
+
end
|
142
|
+
|
143
|
+
puts Fast.search_file( '(def #duplicated)', 'example.rb')
|
176
144
|
|
177
|
-
puts Fast.search_file( '(def #duplicated)', 'example.rb')
|
178
|
-
```
|
179
145
|
The same principle can be used in the node level or for debugging purposes.
|
180
146
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
147
|
+
require 'pry'
|
148
|
+
def debug(node)
|
149
|
+
binding.pry
|
150
|
+
end
|
151
|
+
|
152
|
+
puts Fast.search_file('#debug', 'example.rb')
|
186
153
|
|
187
|
-
puts Fast.search_file('#debug', 'example.rb')
|
188
|
-
```
|
189
154
|
If you want to get only `def` nodes you can also intersect expressions with `[]`:
|
190
|
-
|
191
|
-
puts Fast.search_file('[ def #debug ]', 'example.rb')
|
192
|
-
```
|
155
|
+
|
156
|
+
puts Fast.search_file('[ def #debug ]', 'example.rb')
|
193
157
|
|
194
158
|
### Methods
|
195
159
|
|
196
160
|
Let's take a look at a method declaration:
|
197
161
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
end
|
202
|
-
```
|
162
|
+
def my_method
|
163
|
+
call_other_method
|
164
|
+
end
|
203
165
|
|
204
166
|
It's corresponding s-expression would be:
|
205
167
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
s(:send, nil, :call_other_method))
|
211
|
-
```
|
168
|
+
ast =
|
169
|
+
s(:def, :my_method,
|
170
|
+
s(:args),
|
171
|
+
s(:send, nil, :call_other_method))
|
212
172
|
|
213
173
|
Pay close attention to the node `(args)`. We can't use `...` to match it, as it
|
214
174
|
has no children (or arguments in this case), but we _can_ match it with a wildcard
|
@@ -219,74 +179,55 @@ has no children (or arguments in this case), but we _can_ match it with a wildca
|
|
219
179
|
Let's take a look at a few other examples. Sometimes you have a chain of calls on
|
220
180
|
a single `Object`, like `a.b.c.d`. Its corresponding s-expression would be:
|
221
181
|
|
222
|
-
|
223
|
-
ast =
|
224
|
-
s(:send,
|
225
|
-
s(:send,
|
182
|
+
ast =
|
226
183
|
s(:send,
|
227
|
-
s(:send,
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
184
|
+
s(:send,
|
185
|
+
s(:send,
|
186
|
+
s(:send, nil, :a),
|
187
|
+
:b),
|
188
|
+
:c),
|
189
|
+
:d)
|
232
190
|
|
233
191
|
### Alternate Syntax
|
234
192
|
|
235
193
|
You can also search using nested arrays with **pure values**, or **shortcuts** or
|
236
194
|
**procs**:
|
237
195
|
|
238
|
-
|
239
|
-
Fast.match?(ast, [:send, [:send, '...'], :
|
240
|
-
# => true
|
241
|
-
|
242
|
-
Fast.match?(ast, [:send, [:send, '...'], :c])
|
243
|
-
# => false
|
244
|
-
|
245
|
-
Fast.match?(ast, [:send, [:send, [:send, '...'], :c], :d])
|
246
|
-
# => true
|
247
|
-
```
|
196
|
+
Fast.match?(ast, [:send, [:send, '...'], :d]) # => true
|
197
|
+
Fast.match?(ast, [:send, [:send, '...'], :c]) # => false
|
198
|
+
Fast.match?(ast, [:send, [:send, [:send, '...'], :c], :d]) # => true
|
248
199
|
|
249
200
|
Shortcut tokens like child nodes `...` and wildcards `_` are just placeholders
|
250
201
|
for procs. If you want, you can even use procs directly like so:
|
251
202
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
])
|
261
|
-
# => true
|
262
|
-
```
|
203
|
+
Fast.match?(ast, [
|
204
|
+
:send, [
|
205
|
+
-> (node) { node.type == :send },
|
206
|
+
[:send, '...'],
|
207
|
+
:c
|
208
|
+
],
|
209
|
+
:d
|
210
|
+
]) # => true
|
263
211
|
|
264
212
|
This also works with expressions:
|
265
213
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
)
|
271
|
-
# => [:a, :b, :c, :d]
|
272
|
-
```
|
214
|
+
Fast.match?(
|
215
|
+
ast,
|
216
|
+
'(send (send (send (send nil $_) $_) $_) $_)'
|
217
|
+
) # => [:a, :b, :c, :d]
|
273
218
|
|
274
219
|
### Debugging
|
275
220
|
|
276
221
|
If you find that a particular expression isn't working, you can use `debug` to
|
277
222
|
take a look at what Fast is doing:
|
278
223
|
|
279
|
-
|
280
|
-
Fast.debug { Fast.match?(s(:int, 1), [:int, 1]) }
|
281
|
-
```
|
224
|
+
Fast.debug { Fast.match?(s(:int, 1), [:int, 1]) }
|
282
225
|
|
283
226
|
Each comparison made while searching will be logged to your console (STDOUT) as
|
284
227
|
Fast goes through the AST:
|
285
228
|
|
286
|
-
|
287
|
-
|
288
|
-
1 == 1 # => true
|
289
|
-
```
|
229
|
+
int == (int 1) # => true
|
230
|
+
1 == 1 # => true
|
290
231
|
|
291
232
|
## Bind arguments to expressions
|
292
233
|
|
@@ -294,152 +235,161 @@ We can also dynamically interpolate arguments into our queries using the
|
|
294
235
|
interpolation token `%`. This works much like `sprintf` using indexes starting
|
295
236
|
from `1`:
|
296
237
|
|
297
|
-
|
298
|
-
Fast.match?(code('a = 1'), '(lvasgn %1 (int _))', :a)
|
299
|
-
# => true
|
300
|
-
```
|
238
|
+
Fast.match?(code('a = 1'), '(lvasgn %1 (int _))', :a) # => true
|
301
239
|
|
302
240
|
## Using previous captures in search
|
303
241
|
|
304
242
|
Imagine you're looking for a method that is just delegating something to
|
305
243
|
another method, like this `name` method:
|
306
244
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
end
|
311
|
-
```
|
245
|
+
def name
|
246
|
+
person.name
|
247
|
+
end
|
312
248
|
|
313
249
|
This can be represented as the following AST:
|
314
250
|
|
315
|
-
|
316
|
-
(
|
317
|
-
|
318
|
-
|
319
|
-
(send nil :person) :name))
|
320
|
-
```
|
251
|
+
(def :name
|
252
|
+
(args)
|
253
|
+
(send
|
254
|
+
(send nil :person) :name))
|
321
255
|
|
322
256
|
We can create a query that searches for such a method:
|
323
257
|
|
324
|
-
|
325
|
-
Fast.match?(ast,'(def $_ ... (send (send nil _) \1))')
|
326
|
-
# => [:name]
|
327
|
-
```
|
258
|
+
Fast.match?(ast,'(def $_ ... (send (send nil _) \1))') # => [:name]
|
328
259
|
|
329
260
|
## Fast.search
|
330
261
|
|
331
262
|
Search allows you to go search the entire AST, collecting nodes that matches given
|
332
263
|
expression. Any matching node is then returned:
|
333
264
|
|
334
|
-
|
335
|
-
Fast.search(code('a = 1'), '(int _)')
|
336
|
-
# => s(:int, 1)
|
337
|
-
```
|
265
|
+
Fast.search(code('a = 1'), '(int _)') # => s(:int, 1)
|
338
266
|
|
339
267
|
If you use captures along with a search, both the matching nodes and the
|
340
268
|
captures will be returned:
|
341
269
|
|
342
|
-
|
343
|
-
Fast.search(code('a = 1'), '(int $_)')
|
344
|
-
# => [s(:int, 1), 1]
|
345
|
-
```
|
270
|
+
Fast.search(code('a = 1'), '(int $_)') # => [s(:int, 1), 1]
|
346
271
|
|
347
272
|
## Fast.capture
|
348
273
|
|
349
274
|
To only pick captures and ignore the nodes, use `Fast.capture`:
|
350
275
|
|
351
|
-
|
352
|
-
|
353
|
-
# => 1
|
354
|
-
```
|
276
|
+
Fast.capture(code('a = 1'), '(int $_)') # => 1
|
277
|
+
|
355
278
|
## Fast.replace
|
356
279
|
|
357
|
-
|
358
|
-
Not sure how this section works, could you explain it in more detail?
|
280
|
+
Let's consider the following example:
|
359
281
|
|
360
|
-
|
361
|
-
|
282
|
+
def name
|
283
|
+
person.name
|
284
|
+
end
|
362
285
|
|
363
|
-
|
364
|
-
-->
|
286
|
+
And, we want to replace code to use `delegate` in the expression:
|
365
287
|
|
366
|
-
|
288
|
+
delegate :name, to: :person
|
367
289
|
|
368
|
-
|
369
|
-
|
290
|
+
We already target this example using `\1` on
|
291
|
+
[Search and refer to previous capture](#using-previous-captures-in-search) and
|
292
|
+
now it's time to know about how to rewrite content.
|
370
293
|
|
371
|
-
Fast.replace
|
372
|
-
|
294
|
+
The [Fast.replace](Fast#replace-class_method) yields a #{Fast::Rewriter} context.
|
295
|
+
The internal replace method accepts a range and every `node` have
|
296
|
+
a `location` with metadata about ranges of the node expression.
|
297
|
+
|
298
|
+
ast = Fast.ast("def name; person.name end")
|
299
|
+
# => s(:def, :name, s(:args), s(:send, s(:send, nil, :person), :name))
|
300
|
+
|
301
|
+
Generally, we use the `location.expression`:
|
302
|
+
|
303
|
+
ast.location.expression # => #<Parser::Source::Range (string) 0...25>
|
304
|
+
|
305
|
+
But location also brings some metadata about specific fragments:
|
306
|
+
|
307
|
+
ast.location.instance_variables
|
308
|
+
# => [:@keyword, :@operator, :@name, :@end, :@expression, :@node]
|
309
|
+
|
310
|
+
Range for the keyword that identifies the method definition:
|
311
|
+
|
312
|
+
ast.location.keyword # => #<Parser::Source::Range (string) 0...3>
|
313
|
+
|
314
|
+
You can always pick the source of a source range:
|
315
|
+
|
316
|
+
ast.location.keyword.source # => "def"
|
317
|
+
|
318
|
+
Or only the method name:
|
319
|
+
|
320
|
+
ast.location.name # => #<Parser::Source::Range (string) 4...8>
|
321
|
+
ast.location.name.source # => "name"
|
322
|
+
|
323
|
+
In the context of the rewriter, the objective is removing the method and inserting the new
|
324
|
+
delegate content. Then, the scope is `node.location.expression`:
|
325
|
+
|
326
|
+
Fast.replace ast, '(def $_ ... (send (send nil $_) \1))' do |node, captures|
|
327
|
+
attribute, object = captures
|
328
|
+
|
329
|
+
replace(
|
330
|
+
node.location.expression,
|
331
|
+
"delegate :#{attribute}, to: :#{object}"
|
332
|
+
)
|
333
|
+
end
|
373
334
|
|
374
|
-
replace(
|
375
|
-
node.location.expression,
|
376
|
-
"delegate :#{attribute}, to: :#{object}"
|
377
|
-
)
|
378
|
-
}
|
379
|
-
```
|
380
335
|
|
381
336
|
### Replacing file
|
382
337
|
|
383
338
|
Now let's imagine we have a file like `sample.rb` with the following code:
|
384
339
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
end
|
390
|
-
```
|
340
|
+
def good_bye
|
341
|
+
message = ["good", "bye"]
|
342
|
+
puts message.join(' ')
|
343
|
+
end
|
391
344
|
|
392
|
-
|
393
|
-
`puts`.
|
345
|
+
and we decide to inline the contents of the `message` variable right after
|
394
346
|
|
347
|
+
def good_bye
|
348
|
+
puts ["good", "bye"].join(' ')
|
349
|
+
end
|
395
350
|
|
396
|
-
To
|
351
|
+
To refactor and reach the proposed example, follow a few steps:
|
397
352
|
|
398
|
-
|
399
|
-
|
400
|
-
|
353
|
+
1. Remove the local variable assignment
|
354
|
+
2. Store the now-removed variable's value
|
355
|
+
3. Substitute the value where the variable was used before
|
401
356
|
|
402
|
-
|
403
|
-
assignment = nil
|
404
|
-
query = '({ lvasgn lvar } message )'
|
357
|
+
assignment = nil
|
405
358
|
|
406
|
-
Fast.replace_file
|
407
|
-
|
408
|
-
|
409
|
-
|
359
|
+
Fast.replace_file 'sample.rb', '({ lvasgn lvar } message )', -> (node, _) {
|
360
|
+
# Find a variable assignment
|
361
|
+
if node.type == :lvasgn
|
362
|
+
assignment = node.children.last
|
363
|
+
# Remove the node responsible for the assignment
|
364
|
+
remove(node.location.expression)
|
365
|
+
# Look for the variable being used
|
366
|
+
elsif node.type == :lvar
|
367
|
+
# Replace the variable with the contents of the variable
|
368
|
+
replace(
|
369
|
+
node.location.expression,
|
370
|
+
assignment.location.expression.source
|
371
|
+
)
|
372
|
+
end
|
373
|
+
end
|
410
374
|
|
411
|
-
|
412
|
-
|
413
|
-
# Look for the variable being used
|
414
|
-
elsif node.type == :lvar
|
415
|
-
# Replace the variable with the contents of the variable
|
416
|
-
replace(
|
417
|
-
node.location.expression,
|
418
|
-
assignment.location.expression.source
|
419
|
-
)
|
420
|
-
end
|
421
|
-
})
|
422
|
-
```
|
375
|
+
Keep in mind the current example returns a content output but do not rewrite the
|
376
|
+
file.
|
423
377
|
|
424
|
-
## Other
|
378
|
+
## Other utility functions
|
425
379
|
|
426
380
|
To manipulate ruby files, sometimes you'll need some extra tasks.
|
427
381
|
|
428
|
-
## Fast.
|
382
|
+
## Fast.ast_from_file(file)
|
429
383
|
|
430
384
|
This method parses code from a file and loads it into an AST representation.
|
431
385
|
|
432
|
-
|
433
|
-
Fast.ast_from_file('sample.rb')
|
434
|
-
```
|
386
|
+
Fast.ast_from_file('sample.rb')
|
435
387
|
|
436
388
|
## Fast.search_file
|
437
389
|
|
438
390
|
You can use `search_file` to for search for expressions inside files.
|
439
391
|
|
440
|
-
|
441
|
-
Fast.search_file(expression, 'file.rb')
|
442
|
-
```
|
392
|
+
Fast.search_file(expression, 'file.rb')
|
443
393
|
|
444
394
|
It's a combination of `Fast.ast_from_file` with `Fast.search`.
|
445
395
|
|
@@ -447,28 +397,22 @@ It's a combination of `Fast.ast_from_file` with `Fast.search`.
|
|
447
397
|
|
448
398
|
You can use `Fast.capture_file` to only return captures:
|
449
399
|
|
450
|
-
|
451
|
-
|
452
|
-
# => [:Rewriter, :ExpressionParser, :Find, :FindString, ...]
|
453
|
-
```
|
400
|
+
Fast.capture_file('(class (const nil $_))', 'lib/fast.rb')
|
401
|
+
# => [:Rewriter, :ExpressionParser, :Find, :FindString, ...]
|
454
402
|
|
455
403
|
## Fast.ruby_files_from(arguments)
|
456
404
|
|
457
|
-
`Fast.ruby_files_from(arguments)` can get all
|
405
|
+
The `Fast.ruby_files_from(arguments)` can get all ruby files from file list or folders:
|
458
406
|
|
459
|
-
|
460
|
-
|
461
|
-
# => ["lib/fast.rb"]
|
462
|
-
```
|
407
|
+
Fast.ruby_files_from('lib')
|
408
|
+
# => ["lib/fast/experiment.rb", "lib/fast/cli.rb", "lib/fast/version.rb", "lib/fast.rb"]
|
463
409
|
|
464
410
|
## `fast` in the command line
|
465
411
|
|
466
412
|
Fast also comes with a command line utility called `fast`. You can use it to
|
467
413
|
search and find code much like the library version:
|
468
414
|
|
469
|
-
|
470
|
-
$ fast '(def match?)' lib/fast.rb
|
471
|
-
```
|
415
|
+
fast '(def match?)' lib/fast.rb
|
472
416
|
|
473
417
|
The CLI tool takes the following flags
|
474
418
|
|
@@ -483,44 +427,35 @@ The CLI tool takes the following flags
|
|
483
427
|
You can use `--pry` to stop on a particular source node, and run Pry at that
|
484
428
|
location:
|
485
429
|
|
486
|
-
|
487
|
-
$ fast '(block (send nil it))' spec --pry
|
488
|
-
```
|
430
|
+
fast '(block (send nil it))' spec --pry
|
489
431
|
|
490
432
|
Inside the pry session you can access `result` for the first result that was
|
491
433
|
located, or `results` to get all of the occurrences found.
|
492
434
|
|
493
435
|
Let's take a look at `results`:
|
494
436
|
|
495
|
-
|
496
|
-
|
497
|
-
#
|
498
|
-
# s(:str, "parses
|
499
|
-
# s(:str, "parses
|
500
|
-
# s(:str, "parses
|
501
|
-
# s(:str, "parses [] as All"), ...]
|
502
|
-
```
|
437
|
+
results.map { |e| e.children[0].children[2] }
|
438
|
+
# => [s(:str, "parses ... as Find"),
|
439
|
+
# s(:str, "parses $ as Capture"),
|
440
|
+
# s(:str, "parses quoted values as strings"),
|
441
|
+
# s(:str, "parses {} as Any"),
|
442
|
+
# s(:str, "parses [] as All"), ...]
|
503
443
|
|
504
444
|
### Fast with RSpec
|
505
445
|
|
506
446
|
Let's say we wanted to get all the `it` blocks in our `RSpec` code that
|
507
447
|
currently do not have descriptions:
|
508
448
|
|
509
|
-
|
510
|
-
$ fast '(block (send nil it (nil)) (args) (!str)) ) )' spec
|
511
|
-
```
|
449
|
+
fast '(block (send nil it (nil)) (args) (!str)) ) )' spec
|
512
450
|
|
513
451
|
This will return the following:
|
514
452
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
it { expect(described_class).to be_match(code['"string"'], '(str "string")') }
|
522
|
-
# ... more results
|
523
|
-
```
|
453
|
+
# spec/fast_spec.rb:166
|
454
|
+
it { expect(described_class).to be_match(s(:int, 1), '(...)') }
|
455
|
+
# spec/fast_spec.rb:167
|
456
|
+
it { expect(described_class).to be_match(s(:int, 1), '(_ _)') }
|
457
|
+
# spec/fast_spec.rb:168
|
458
|
+
it { expect(described_class).to be_match(code['"string"'], '(str "string")') }
|
524
459
|
|
525
460
|
## Experiments
|
526
461
|
|
@@ -534,21 +469,19 @@ from our specs.
|
|
534
469
|
|
535
470
|
If the spec still pass we can confidently say that the hook is useless.
|
536
471
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
lookup 'spec'
|
472
|
+
Fast.experiment("RSpec/RemoveUselessBeforeAfterHook") do
|
473
|
+
# Lookup our spec files
|
474
|
+
lookup 'spec'
|
541
475
|
|
542
|
-
|
543
|
-
|
476
|
+
# Look for every block starting with before or after
|
477
|
+
search "(block (send nil {before after}))"
|
544
478
|
|
545
|
-
|
546
|
-
|
479
|
+
# Remove those blocks
|
480
|
+
edit { |node| remove(node.loc.expression) }
|
547
481
|
|
548
|
-
|
549
|
-
|
550
|
-
end
|
551
|
-
```
|
482
|
+
# Create a new file, and run RSpec against that new file
|
483
|
+
policy { |new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
484
|
+
end
|
552
485
|
|
553
486
|
- `lookup` can be used to pass in files or folders.
|
554
487
|
- `search` contains the expression you want to match
|
@@ -581,13 +514,8 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
581
514
|
|
582
515
|
On the console we have a few functions like `s` and `code` to make it easy ;)
|
583
516
|
|
584
|
-
|
585
|
-
|
586
|
-
```
|
587
|
-
|
588
|
-
```ruby
|
589
|
-
code("a = 1") # => s(:lvasgn, s(:int, 1))
|
590
|
-
```
|
517
|
+
bin/console
|
518
|
+
code("a = 1") # => s(:lvasgn, s(:int, 1))
|
591
519
|
|
592
520
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
593
521
|
|