ffast 0.0.3 → 0.0.4
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/.rubocop.yml +4 -0
- data/README.md +12 -0
- data/bin/fast +2 -57
- data/docs/experiments.md +6 -0
- data/docs/similarity_tutorial.md +3 -1
- data/docs/syntax.md +2 -2
- data/examples/similarity_research.rb +58 -0
- data/experiments/replace_create_with_build_stubbed.rb +10 -0
- data/fast.gemspec +16 -5
- data/lib/fast.rb +66 -12
- data/lib/fast/cli.rb +129 -0
- data/lib/fast/version.rb +5 -0
- metadata +85 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c7eee39cf27764a49009037c603ebd8f9afe0b8f0d78ac651c2bdee1d65f329
|
4
|
+
data.tar.gz: '09eefe65daeeb37ed5d49120cbee73018303156df11f25daa5258ce310a1be89'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 163730827f44a4d9a5491b47f96e6d3f10d883921710b5fa2e6f9573a586e71ff84460710288b3388ef209c90744f01d7dfc03ca5f5daff5a62c2179e3da43c5
|
7
|
+
data.tar.gz: 0ecc89dd396c151c07fcf6bc9de52c3dc183909bb29498715f2e703124db75e03847894a3d7394e79b9beabee750fa13db23d74bcc95b68a721553d6f5b8c46b
|
data/.rubocop.yml
CHANGED
@@ -15,6 +15,7 @@ Metrics/LineLength:
|
|
15
15
|
Metrics/BlockLength:
|
16
16
|
Exclude:
|
17
17
|
- 'spec/**/*'
|
18
|
+
- 'fast.gemspec'
|
18
19
|
|
19
20
|
Lint/InterpolationCheck:
|
20
21
|
Exclude:
|
@@ -35,3 +36,6 @@ RSpec/NestedGroups:
|
|
35
36
|
|
36
37
|
RSpec/ExampleLength:
|
37
38
|
Max: 20
|
39
|
+
|
40
|
+
RSpec/MultipleExpectations:
|
41
|
+
Enabled: false
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ The current version cover the following elements:
|
|
23
23
|
- `^` is to get the **parent node** of an expression
|
24
24
|
- `?` is for **maybe**
|
25
25
|
- `\1` to use the first **previous captured** element
|
26
|
+
- `%1` to bind the first extra argument
|
26
27
|
- `""` surround the value with double quotes to match literal strings
|
27
28
|
|
28
29
|
The syntax is inspired on [RuboCop Node Pattern](https://github.com/bbatsov/rubocop/blob/master/lib/rubocop/node_pattern.rb).
|
@@ -192,6 +193,17 @@ It will output each comparison to stdout:
|
|
192
193
|
int == (int 1) # => true
|
193
194
|
1 == 1 # => true
|
194
195
|
```
|
196
|
+
## Bind arguments to expressions
|
197
|
+
|
198
|
+
Sometimes we need to define useful functions and bind arguments that will be
|
199
|
+
based on dynamic decisions or other external input.
|
200
|
+
For such cases you can bind the arguments with `%` and the index start from `1`.
|
201
|
+
|
202
|
+
Example:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
Fast.match?(code['a = 1'], '(lvasgn %1 (int _))', :a) # true
|
206
|
+
```
|
195
207
|
|
196
208
|
## Use previous captures in search
|
197
209
|
|
data/bin/fast
CHANGED
@@ -4,61 +4,6 @@
|
|
4
4
|
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
5
5
|
|
6
6
|
require 'fast'
|
7
|
-
require '
|
7
|
+
require 'fast/cli'
|
8
8
|
|
9
|
-
|
10
|
-
show_sexp = arguments.delete('--ast')
|
11
|
-
pry = arguments.delete('--pry')
|
12
|
-
from_code = arguments.delete('--code') || arguments.delete('-c')
|
13
|
-
similar = arguments.delete('--similar') || arguments.delete('-s')
|
14
|
-
debug = arguments.delete('--debug') || arguments.delete('-d')
|
15
|
-
|
16
|
-
pattern = arguments.shift
|
17
|
-
|
18
|
-
if similar || from_code
|
19
|
-
ast = Fast.ast(pattern)
|
20
|
-
if similar
|
21
|
-
puts "Looking for code similar to #{pattern}" if debug
|
22
|
-
pattern = Fast.expression_from(ast)
|
23
|
-
elsif from_code
|
24
|
-
pattern = ast.to_sexp
|
25
|
-
puts 'The generated expression from AST was:', pattern if debug
|
26
|
-
end
|
27
|
-
end
|
28
|
-
arguments << '.' if arguments.empty?
|
29
|
-
|
30
|
-
files = Fast.ruby_files_from(*arguments)
|
31
|
-
|
32
|
-
expression = Fast.expression(pattern)
|
33
|
-
|
34
|
-
puts "Expression: #{expression.map(&:to_s).join(' ')}" if debug
|
35
|
-
|
36
|
-
files.each do |file|
|
37
|
-
results =
|
38
|
-
if debug
|
39
|
-
Fast.debug { Fast.search_file(expression, file) }
|
40
|
-
else
|
41
|
-
begin
|
42
|
-
Fast.search_file(expression, file)
|
43
|
-
rescue Parser::SyntaxError
|
44
|
-
if debug
|
45
|
-
puts "Ops! An error occurred trying to search in #{expression.inspect} in #{file}",
|
46
|
-
$ERROR_INFO,
|
47
|
-
$ERROR_POSITION
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
next unless results
|
53
|
-
|
54
|
-
results.each do |result|
|
55
|
-
next if result.nil? || result == []
|
56
|
-
|
57
|
-
if pry
|
58
|
-
require 'pry'
|
59
|
-
binding.pry # rubocop:disable Lint/Debugger
|
60
|
-
else
|
61
|
-
Fast.report(result, file: file, show_sexp: show_sexp)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
9
|
+
Fast::Cli.run! ARGV
|
data/docs/experiments.md
CHANGED
@@ -145,3 +145,9 @@ You can limit experiments or file escope:
|
|
145
145
|
fast-experiment RSpec/RemoveUselessBeforeAfterHook spec/models/**/*_spec.rb
|
146
146
|
```
|
147
147
|
|
148
|
+
Or a single file:
|
149
|
+
|
150
|
+
```
|
151
|
+
fast-experiment RSpec/ReplaceCreateWithBuildStubbed spec/models/my_spec.rb
|
152
|
+
```
|
153
|
+
|
data/docs/similarity_tutorial.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
This is a small tutorial to explore code similarity.
|
4
4
|
|
5
|
+
Check the code example [here](https://github.com/jonatas/fast/blob/master/examples/similarity_research.rb).
|
6
|
+
|
5
7
|
The major idea is register all expression styles and see if we can find some
|
6
8
|
similarity between the structures.
|
7
9
|
|
@@ -94,7 +96,7 @@ files required by bundler.
|
|
94
96
|
```ruby
|
95
97
|
similarities = {}
|
96
98
|
Gem.find_files('*.rb').each do |file|
|
97
|
-
Fast.search_file('',file).map do |n|
|
99
|
+
Fast.search_file('{block send if while case def defs class module}', file).map do |n|
|
98
100
|
key = Fast.expression_from(n)
|
99
101
|
similarities[key] ||= Set.new
|
100
102
|
similarities[key] << file
|
data/docs/syntax.md
CHANGED
@@ -181,7 +181,7 @@ ANSWER = 42
|
|
181
181
|
|
182
182
|
Let's hunt for integer nodes that the parent is also a method:
|
183
183
|
|
184
|
-
|
184
|
+
$ fast '[ ^^int def ]' example.rb
|
185
185
|
|
186
186
|
The match will filter only nodes that matches all internal expressions.
|
187
187
|
|
@@ -199,7 +199,7 @@ type `def`.
|
|
199
199
|
|
200
200
|
Looking the method representation we have:
|
201
201
|
|
202
|
-
|
202
|
+
$ fast def example.rb --ast
|
203
203
|
|
204
204
|
```ruby
|
205
205
|
# example.rb:3
|
@@ -0,0 +1,58 @@
|
|
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
|
+
|
@@ -0,0 +1,10 @@
|
|
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
|
data/fast.gemspec
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'fast/version'
|
6
|
+
|
3
7
|
Gem::Specification.new do |spec|
|
4
8
|
spec.name = 'ffast'
|
5
|
-
spec.version =
|
9
|
+
spec.version = Fast::VERSION
|
6
10
|
spec.required_ruby_version = '>= 2.3'
|
7
11
|
spec.authors = ['Jônatas Davi Paganini']
|
8
|
-
spec.email = ['
|
12
|
+
spec.email = ['jonatasdp@gmail.com']
|
13
|
+
spec.homepage = 'https://jonatas.github.io/fast/'
|
9
14
|
|
10
15
|
spec.summary = 'FAST: Find by AST.'
|
11
16
|
spec.description = 'Allow you to search for code using node pattern syntax.'
|
@@ -14,16 +19,22 @@ Gem::Specification.new do |spec|
|
|
14
19
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
15
20
|
f.match(%r{^(test|spec|features)/})
|
16
21
|
end
|
22
|
+
|
17
23
|
spec.bindir = 'bin'
|
18
24
|
spec.executables = ['fast', 'fast-experiment']
|
19
25
|
spec.require_paths = %w[lib experiments]
|
20
26
|
|
21
|
-
spec.add_development_dependency 'bundler', '~> 1.14'
|
22
|
-
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
27
|
spec.add_dependency 'coderay'
|
25
28
|
spec.add_dependency 'parser'
|
26
29
|
spec.add_dependency 'pry'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
32
|
+
spec.add_development_dependency 'guard'
|
33
|
+
spec.add_development_dependency 'guard-livereload'
|
34
|
+
spec.add_development_dependency 'guard-rspec'
|
35
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
36
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
37
|
+
spec.add_development_dependency 'rspec-its', '~> 1.2'
|
27
38
|
spec.add_development_dependency 'rubocop'
|
28
39
|
spec.add_development_dependency 'rubocop-rspec'
|
29
40
|
end
|
data/lib/fast.rb
CHANGED
@@ -19,7 +19,6 @@ end
|
|
19
19
|
|
20
20
|
# Fast is a tool to help you search in the code through the Abstract Syntax Tree
|
21
21
|
module Fast
|
22
|
-
VERSION = '0.3.0'
|
23
22
|
LITERAL = {
|
24
23
|
'...' => ->(node) { node&.children&.any? },
|
25
24
|
'_' => ->(node) { !node.nil? },
|
@@ -54,11 +53,13 @@ module Fast
|
|
54
53
|
\$ # capture
|
55
54
|
|
|
56
55
|
\\\d # find using captured expression
|
56
|
+
|
|
57
|
+
%\d # find using binded argument
|
57
58
|
/x
|
58
59
|
|
59
60
|
class << self
|
60
|
-
def match?(ast, search)
|
61
|
-
Matcher.new(ast, search).match?
|
61
|
+
def match?(ast, search, *args)
|
62
|
+
Matcher.new(ast, search, *args).match?
|
62
63
|
end
|
63
64
|
|
64
65
|
def replace(ast, search, replacement)
|
@@ -186,7 +187,7 @@ module Fast
|
|
186
187
|
"(#{node.type}#{' ' + children_expression if node.children.any?})"
|
187
188
|
when nil, 'nil'
|
188
189
|
'nil'
|
189
|
-
when Symbol, String,
|
190
|
+
when Symbol, String, Numeric
|
190
191
|
'_'
|
191
192
|
when Array, Hash
|
192
193
|
'...'
|
@@ -228,7 +229,7 @@ module Fast
|
|
228
229
|
# ExpressionParser empowers the AST search in Ruby.
|
229
230
|
# You can check a few classes inheriting `Fast::Find` and adding extra behavior.
|
230
231
|
# Parens encapsulates node search: `(node_type children...)` .
|
231
|
-
# Exclamation Mark to negate: `!(int _)` is
|
232
|
+
# Exclamation Mark to negate: `!(int _)` is equivalent to a `not integer` node.
|
232
233
|
# Curly Braces allows [Any]: `({int float} _)` or `{(int _) (float _)}`.
|
233
234
|
# Square Braquets allows [All]: [(int _) !(int 0)] # all integer less zero.
|
234
235
|
# Dollar sign can be used to capture values: `(${int float} _)` will capture the node type.
|
@@ -243,6 +244,7 @@ module Fast
|
|
243
244
|
|
244
245
|
# rubocop:disable Metrics/CyclomaticComplexity
|
245
246
|
# rubocop:disable Metrics/AbcSize
|
247
|
+
# rubocop:disable Metrics/MethodLength
|
246
248
|
def parse
|
247
249
|
case (token = next_token)
|
248
250
|
when '(' then parse_until_peek(')')
|
@@ -254,11 +256,13 @@ module Fast
|
|
254
256
|
when '?' then Maybe.new(parse)
|
255
257
|
when '^' then Parent.new(parse)
|
256
258
|
when '\\' then FindWithCapture.new(parse)
|
259
|
+
when /^%\d/ then FindFromArgument.new(token[1..-1])
|
257
260
|
else Find.new(token)
|
258
261
|
end
|
259
262
|
end
|
260
263
|
# rubocop:enable Metrics/CyclomaticComplexity
|
261
264
|
# rubocop:enable Metrics/AbcSize
|
265
|
+
# rubocop:enable Metrics/MethodLength
|
262
266
|
|
263
267
|
def parse_until_peek(token)
|
264
268
|
list = []
|
@@ -302,8 +306,14 @@ module Fast
|
|
302
306
|
end
|
303
307
|
|
304
308
|
def compare_symbol_or_head(node, expression)
|
305
|
-
|
306
|
-
|
309
|
+
case node
|
310
|
+
when Parser::AST::Node
|
311
|
+
node.type == expression.to_sym
|
312
|
+
when String
|
313
|
+
node == expression.to_s
|
314
|
+
else
|
315
|
+
node == expression
|
316
|
+
end
|
307
317
|
end
|
308
318
|
|
309
319
|
def debug_match_recursive(node, expression)
|
@@ -379,6 +389,30 @@ module Fast
|
|
379
389
|
end
|
380
390
|
end
|
381
391
|
|
392
|
+
# Use `%1` in the expression and the Matcher#prepare_arguments will
|
393
|
+
# interpolate the argument in the expression.
|
394
|
+
class FindFromArgument < Find
|
395
|
+
attr_writer :arguments
|
396
|
+
|
397
|
+
def initialize(token)
|
398
|
+
token = token.token if token.respond_to?(:token)
|
399
|
+
raise 'You must define index' unless token
|
400
|
+
|
401
|
+
@capture_argument = token.to_i - 1
|
402
|
+
raise 'Arguments start in one' if @capture_argument.negative?
|
403
|
+
end
|
404
|
+
|
405
|
+
def match?(node)
|
406
|
+
raise 'You must define arguments to match' unless @arguments
|
407
|
+
|
408
|
+
compare_symbol_or_head node, @arguments[@capture_argument]
|
409
|
+
end
|
410
|
+
|
411
|
+
def to_s
|
412
|
+
"find_with_arg[\\#{@capture_argument}]"
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
382
416
|
# Capture some expression while searching for it:
|
383
417
|
# Example: `(${int float} _)` will capture the node type
|
384
418
|
# Example: `$({int float} _)` will capture the node
|
@@ -458,7 +492,7 @@ module Fast
|
|
458
492
|
|
459
493
|
# Joins the AST and the search expression to create a complete match
|
460
494
|
class Matcher
|
461
|
-
def initialize(ast, fast)
|
495
|
+
def initialize(ast, fast, *args)
|
462
496
|
@ast = ast
|
463
497
|
@fast = if fast.is_a?(String)
|
464
498
|
Fast.expression(fast)
|
@@ -466,9 +500,22 @@ module Fast
|
|
466
500
|
[*fast].map(&Find.method(:new))
|
467
501
|
end
|
468
502
|
@captures = []
|
503
|
+
prepare_arguments(@fast, args) if args.any?
|
504
|
+
end
|
505
|
+
|
506
|
+
def prepare_arguments(expression, arguments)
|
507
|
+
case expression
|
508
|
+
when Array
|
509
|
+
expression.each do |item|
|
510
|
+
prepare_arguments(item, arguments)
|
511
|
+
end
|
512
|
+
when Fast::FindFromArgument
|
513
|
+
expression.arguments = arguments
|
514
|
+
when Fast::Find
|
515
|
+
prepare_arguments expression.token, arguments
|
516
|
+
end
|
469
517
|
end
|
470
518
|
|
471
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
472
519
|
# rubocop:disable Metrics/AbcSize
|
473
520
|
def match?(ast = @ast, fast = @fast)
|
474
521
|
head, *tail = fast
|
@@ -479,13 +526,20 @@ module Fast
|
|
479
526
|
|
480
527
|
child = ast.children
|
481
528
|
tail.each_with_index.all? do |token, i|
|
482
|
-
token
|
529
|
+
prepare_token(token)
|
483
530
|
token.is_a?(Array) ? match?(child[i], token) : token.match?(child[i])
|
484
531
|
end && find_captures
|
485
532
|
end
|
486
|
-
|
533
|
+
|
487
534
|
# rubocop:enable Metrics/AbcSize
|
488
535
|
|
536
|
+
def prepare_token(token)
|
537
|
+
case token
|
538
|
+
when Fast::FindWithCapture
|
539
|
+
token.previous_captures = find_captures
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
489
543
|
def captures?(fast = @fast)
|
490
544
|
case fast
|
491
545
|
when Capture then true
|
@@ -645,7 +699,7 @@ module Fast
|
|
645
699
|
|
646
700
|
def run
|
647
701
|
while (combinations = suggest_combinations).any?
|
648
|
-
if combinations.size >
|
702
|
+
if combinations.size > 50
|
649
703
|
puts "Ignoring #{@file} because it have #{combinations.size} possible combinations"
|
650
704
|
break
|
651
705
|
end
|
data/lib/fast/cli.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fast'
|
4
|
+
require 'fast/version'
|
5
|
+
require 'coderay'
|
6
|
+
require 'optparse'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
module Fast
|
10
|
+
# Command Line Interface for Fast
|
11
|
+
class Cli
|
12
|
+
attr_reader :pattern, :show_sexp, :pry, :from_code, :similar, :help
|
13
|
+
|
14
|
+
# rubocop:disable Metrics/MethodLength
|
15
|
+
# rubocop:disable Metrics/AbcSize
|
16
|
+
def initialize(args)
|
17
|
+
@opt = OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
|
18
|
+
opts.banner = 'Usage: fast expression <files> [options]'
|
19
|
+
opts.on('-d', '--debug', 'Debug fast engine') do
|
20
|
+
@debug = true
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('--ast', 'Print AST instead of code') do
|
24
|
+
@show_sexp = true
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('--pry', 'Jump into a pry session with results') do
|
28
|
+
@pry = true
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-c', '--code', 'Create a pattern from code example') do
|
32
|
+
if @pattern
|
33
|
+
@from_code = true
|
34
|
+
@pattern = Fast.ast(@pattern).to_sexp
|
35
|
+
debug 'Expression from AST:', @pattern
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('-s', '--similar', 'Search for similar code.') do
|
40
|
+
@similar = true
|
41
|
+
@pattern = Fast.expression_from(Fast.ast(@pattern))
|
42
|
+
debug "Looking for code similar to #{@pattern}"
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on_tail('--version', 'Show version') do
|
46
|
+
puts Fast::VERSION
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on_tail('-h', '--help', 'Show help. More at https://jonatas.github.io/fast') do
|
51
|
+
@help = true
|
52
|
+
end
|
53
|
+
|
54
|
+
@pattern, @files = args.reject { |arg| arg.start_with? '-' }
|
55
|
+
end
|
56
|
+
@opt.parse! args
|
57
|
+
|
58
|
+
@files = [*@files]
|
59
|
+
@files.reject! { |arg| arg.start_with?('-') }
|
60
|
+
end
|
61
|
+
|
62
|
+
# rubocop:enable Metrics/MethodLength
|
63
|
+
# rubocop:enable Metrics/AbcSize
|
64
|
+
|
65
|
+
def self.run!(argv)
|
66
|
+
argv = argv.dup
|
67
|
+
new(argv).run!
|
68
|
+
end
|
69
|
+
|
70
|
+
def run!
|
71
|
+
if @help || @files.empty? && @pattern.nil?
|
72
|
+
puts @opt.help
|
73
|
+
else
|
74
|
+
search
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def expression
|
79
|
+
Fast.expression(@pattern)
|
80
|
+
end
|
81
|
+
|
82
|
+
def search_file(file)
|
83
|
+
if debug_mode?
|
84
|
+
Fast.debug { Fast.search_file(expression, file) }
|
85
|
+
else
|
86
|
+
begin
|
87
|
+
Fast.search_file(expression, file)
|
88
|
+
rescue StandardError
|
89
|
+
debug "Ops! An error occurred trying to search in #{expression.inspect} in #{file}", $ERROR_INFO, $ERROR_POSITION
|
90
|
+
[]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def search
|
96
|
+
files.each do |file|
|
97
|
+
results = search_file(file)
|
98
|
+
next if results.nil? || results.empty?
|
99
|
+
|
100
|
+
results.each do |result|
|
101
|
+
if @pry
|
102
|
+
require 'pry'
|
103
|
+
binding.pry # rubocop:disable Lint/Debugger
|
104
|
+
else
|
105
|
+
report(result, file)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def files
|
112
|
+
Fast.ruby_files_from(*@files)
|
113
|
+
end
|
114
|
+
|
115
|
+
def debug_mode?
|
116
|
+
@debug == true
|
117
|
+
end
|
118
|
+
|
119
|
+
def debug(*info)
|
120
|
+
return unless @debug
|
121
|
+
|
122
|
+
puts info
|
123
|
+
end
|
124
|
+
|
125
|
+
def report(result, file)
|
126
|
+
Fast.report(result, file: file, show_sexp: @show_sexp)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/fast/version.rb
ADDED
metadata
CHANGED
@@ -1,65 +1,79 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jônatas Davi Paganini
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: coderay
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: parser
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
type: :
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '1.14'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '1.14'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: guard
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
75
|
version: '0'
|
62
|
-
type: :
|
76
|
+
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
@@ -67,13 +81,13 @@ dependencies:
|
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: guard-livereload
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
89
|
version: '0'
|
76
|
-
type: :
|
90
|
+
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
@@ -81,19 +95,61 @@ dependencies:
|
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
98
|
+
name: guard-rspec
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - ">="
|
88
102
|
- !ruby/object:Gem::Version
|
89
103
|
version: '0'
|
90
|
-
type: :
|
104
|
+
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - ">="
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec-its
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.2'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.2'
|
97
153
|
- !ruby/object:Gem::Dependency
|
98
154
|
name: rubocop
|
99
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,7 +180,7 @@ dependencies:
|
|
124
180
|
version: '0'
|
125
181
|
description: Allow you to search for code using node pattern syntax.
|
126
182
|
email:
|
127
|
-
-
|
183
|
+
- jonatasdp@gmail.com
|
128
184
|
executables:
|
129
185
|
- fast
|
130
186
|
- fast-experiment
|
@@ -155,12 +211,16 @@ files:
|
|
155
211
|
- examples/find_usage.rb
|
156
212
|
- examples/let_it_be_experiment.rb
|
157
213
|
- examples/method_complexity.rb
|
214
|
+
- examples/similarity_research.rb
|
158
215
|
- experiments/let_it_be_experiment.rb
|
159
216
|
- experiments/remove_useless_hook.rb
|
217
|
+
- experiments/replace_create_with_build_stubbed.rb
|
160
218
|
- fast.gemspec
|
161
219
|
- lib/fast.rb
|
220
|
+
- lib/fast/cli.rb
|
221
|
+
- lib/fast/version.rb
|
162
222
|
- mkdocs.yml
|
163
|
-
homepage:
|
223
|
+
homepage: https://jonatas.github.io/fast/
|
164
224
|
licenses:
|
165
225
|
- MIT
|
166
226
|
metadata: {}
|