ffast 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 238c006c217b8838c23488cbfafe15c3e694d69a
4
- data.tar.gz: '095e4dfdba32adf0ddf52da166c27a93c8bbd3bb'
2
+ SHA256:
3
+ metadata.gz: c75d9b66509133b2f2579a03d663a80ece65421f309819f6c04b67c641973816
4
+ data.tar.gz: d172f92548e5080074d9edc2ec23bbc0a3febd3cfcfe1296d743a0a219ebfb8f
5
5
  SHA512:
6
- metadata.gz: 73ce257e59e1bd96cbc62d4b23225cd50f5bfa551ca64eeb38da4693bc832b14203d2335d6e55e389b74fd5feba1e164ed2f1adbcdbeca0a14676115e1df5c02
7
- data.tar.gz: 3133c3f337da981e728ab42ed9bf61eafc907715c3c6bb7c210ff38d6a6c73d3e87cf88625d8cac4dcfa648d3f909b3e6625a502d96c9a2316e04436086c7f8f
6
+ metadata.gz: 12f1c29903c12fe53e0b1a6b9c8c54664eb34411dbb9ae88c813359fe0df342c4a0c4168f13598a357281992ba89f7e5a1bdf8196e4217ce6b451d60d1cef364
7
+ data.tar.gz: 98e8a7f6b9a374197124b2a0b293fececd46bf396f86d6981de27193b9dc5c92532bc66763a31f446dae4d638967ea601f1348f633bbee6ca383ab5a2d63e54d
data/.travis.yml CHANGED
@@ -1,8 +1,8 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.3.3
5
- before_install: gem install bundler -v 1.13.7
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
6
6
  before_script:
7
7
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
8
8
  - chmod +x ./cc-test-reporter
data/README.md CHANGED
@@ -7,6 +7,8 @@ Fast is a "Find AST" tool to help you search in the code abstract syntax tree.
7
7
  Ruby allow us to do the same thing in a few ways then it's hard to check
8
8
  how the code is written.
9
9
 
10
+ Check the official documentation: https://jonatas.github.io/fast.
11
+
10
12
  ## Syntax for find in AST
11
13
 
12
14
  The current version cover the following elements:
@@ -27,18 +29,6 @@ The syntax is inspired on [RuboCop Node Pattern](https://github.com/bbatsov/rubo
27
29
 
28
30
  ## Installation
29
31
 
30
- Add this line to your application's Gemfile:
31
-
32
- ```ruby
33
- gem 'ffast'
34
- ```
35
-
36
- And then execute:
37
-
38
- $ bundle
39
-
40
- Or install it yourself as:
41
-
42
32
  $ gem install ffast
43
33
 
44
34
  ## How it works
@@ -230,7 +220,7 @@ name:
230
220
  Fast.match?(ast,'(def $_ ... (send (send nil _) \1))') # => [:name]
231
221
  ```
232
222
 
233
- ### Fast.search
223
+ ## Fast.search
234
224
 
235
225
  Search allows you to go deeply in the AST, collecting nodes that matches with
236
226
  the expression. It also returns captures if they exist.
@@ -245,14 +235,14 @@ If you use captures, it returns the node and the captures respectively:
245
235
  Fast.search(code('a = 1'), '(int $_)') # => [s(:int, 1), 1]
246
236
  ```
247
237
 
248
- ### Fast.capture
238
+ ## Fast.capture
249
239
 
250
240
  To pick just the captures and ignore the nodes, use `Fast.capture`:
251
241
 
252
242
  ```ruby
253
243
  Fast.capture(code('a = 1'), '(int $_)') # => 1
254
244
  ```
255
- ### Fast.replace
245
+ ## Fast.replace
256
246
 
257
247
  And if I want to refactor a code and use `delegate <attribute>, to: <object>`, try with replace:
258
248
 
@@ -303,7 +293,7 @@ Fast.replace_file('sample.rb', '({ lvasgn lvar } message )',
303
293
 
304
294
  To manipulate ruby files, some times you'll need some extra tasks.
305
295
 
306
- ### Fast.ast_from_File(file)
296
+ ## Fast.ast_from_File(file)
307
297
 
308
298
  This method parses the code and load into a AST representation.
309
299
 
@@ -311,7 +301,7 @@ This method parses the code and load into a AST representation.
311
301
  Fast.ast_from_file('sample.rb')
312
302
  ```
313
303
 
314
- ### Fast.search_file
304
+ ## Fast.search_file
315
305
 
316
306
  You can use `search_file` and pass the path for search for expressions inside
317
307
  files.
@@ -322,7 +312,7 @@ Fast.search_file('file.rb', expression)
322
312
 
323
313
  It's simple combination of `Fast.ast_from_file` with `Fast.search`.
324
314
 
325
- ### Fast.ruby_files_from(arguments)
315
+ ## Fast.ruby_files_from(arguments)
326
316
 
327
317
  You'll be probably looking for multiple ruby files, then this method fetches
328
318
  all internal `.rb` files
@@ -342,6 +332,38 @@ $ fast '(def match?)' lib/fast.rb
342
332
 
343
333
  - Use `-d` or `--debug` for enable debug mode.
344
334
  - Use `--ast` to output the AST instead of the original code
335
+ - Use `--pry` to jump debugging the first result with pry
336
+ - Use `-c` to search from code example
337
+ - Use `-s` to search similar code
338
+
339
+ ```
340
+ $ fast '(block (send nil it))' spec --pry
341
+ ```
342
+ And inside pry session, you can use `result` as the first result or `results`
343
+ to use all occurrences found.
344
+
345
+ ```ruby
346
+ results.map{|e|e.children[0].children[2]}
347
+ # => [s(:str, "parses ... as Find"),
348
+ # s(:str, "parses $ as Capture"),
349
+ # s(:str, "parses quoted values as strings"),
350
+ # s(:str, "parses {} as Any"),
351
+ # s(:str, "parses [] as All"), ...]
352
+ ```
353
+
354
+ Getting all `it` blocks without description:
355
+
356
+ $ fast '(block (send nil it (nil)) (args ) (!str)) ) )' spec
357
+
358
+ ```ruby
359
+ # spec/fast_spec.rb:166
360
+ it { expect(described_class).to be_match(s(:int, 1), '(...)') }
361
+ # spec/fast_spec.rb:167
362
+ it { expect(described_class).to be_match(s(:int, 1), '(_ _)') }
363
+ # spec/fast_spec.rb:168
364
+ it { expect(described_class).to be_match(code['"string"'], '(str "string")') }
365
+ # ... more results
366
+ ```
345
367
 
346
368
  ## Experiments
347
369
 
@@ -406,3 +428,5 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jonata
406
428
 
407
429
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
408
430
 
431
+
432
+ See more on the [official documentation](https://jonatas.github.io/fast).
data/bin/fast CHANGED
@@ -7,37 +7,58 @@ require 'fast'
7
7
  require 'coderay'
8
8
 
9
9
  arguments = ARGV
10
- pattern = arguments.shift
11
10
  show_sexp = arguments.delete('--ast')
12
11
  pry = arguments.delete('--pry')
12
+ from_code = arguments.delete('--code') || arguments.delete('-c')
13
+ similar = arguments.delete('--similar') || arguments.delete('-s')
13
14
  debug = arguments.delete('--debug') || arguments.delete('-d')
14
- files = Fast.ruby_files_from(*arguments || '.')
15
15
 
16
- pattern = Fast.expression(pattern)
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)
17
31
 
18
- puts "Expression: #{pattern.map(&:to_s)}" if debug
32
+ expression = Fast.expression(pattern)
33
+
34
+ puts "Expression: #{expression.map(&:to_s).join(' ')}" if debug
19
35
 
20
36
  files.each do |file|
21
37
  results =
22
38
  if debug
23
- Fast.debug { Fast.search_file(pattern, file) }
39
+ Fast.debug { Fast.search_file(expression, file) }
24
40
  else
25
41
  begin
26
- Fast.search_file(pattern, file)
42
+ Fast.search_file(expression, file)
27
43
  rescue Parser::SyntaxError
28
- puts "Ops! An error occurred trying to search in #{pattern.inspect} in #{file}", $ERROR_INFO, $ERROR_POSITION
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
29
49
  end
30
50
  end
31
51
 
32
52
  next unless results
33
53
 
34
- results.each do |node|
54
+ results.each do |result|
35
55
  next if result.nil? || result == []
56
+
36
57
  if pry
37
58
  require 'pry'
38
59
  binding.pry # rubocop:disable Lint/Debugger
39
60
  else
40
- Fast.report(node, file: file, show_sexp: show_sexp)
61
+ Fast.report(result, file: file, show_sexp: show_sexp)
41
62
  end
42
63
  end
43
64
  end
@@ -0,0 +1,112 @@
1
+ # Command line
2
+
3
+ It will also inject a executable named `fast` and you can use it to search and
4
+ find code using the concept:
5
+
6
+ ```
7
+ $ fast '(def match?)' lib/fast.rb
8
+ ```
9
+
10
+ - Use `-d` or `--debug` for enable debug mode.
11
+ - Use `--ast` to output the AST instead of the original code
12
+ - Use `--pry` to jump debugging the first result with pry
13
+ - Use `-c` to search from code example
14
+ - Use `-s` to search similar code
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
+ # spec/fast_spec.rb:167
40
+ it { expect(described_class).to be_match(s(:int, 1), '(_ _)') }
41
+ # spec/fast_spec.rb:168
42
+ it { expect(described_class).to be_match(code['"string"'], '(str "string")') }
43
+ # ... more results
44
+ ```
45
+
46
+ ## `--debug`
47
+
48
+ This option will print all matching details while validating each node.
49
+
50
+ ```
51
+ $ echo 'object.method' > sample.rb
52
+ $ fast -d '(send (send nil _) _)' sample.rb
53
+ ```
54
+
55
+ It will bring details of the expression compiled and each node being validated:
56
+
57
+ ```
58
+ Expression: f[send] [#<Fast::Find:0x00007f8c53047158 @token="send">, #<Fast::Find:0x00007f8c530470e0 @token="nil">, #<Fast::Find:0x00007f8c53047090 @token="_">] f[_]
59
+ send == (send
60
+ (send nil :object) :method) # => true
61
+ f[send] == (send
62
+ (send nil :object) :method) # => true
63
+ send == (send nil :object) # => true
64
+ f[send] == (send nil :object) # => true
65
+ == # => true
66
+ f[nil] == # => true
67
+ #<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
68
+ f[_] == object # => true
69
+ [#<Fast::Find:0x00007f8c53047158 @token="send">, #<Fast::Find:0x00007f8c530470e0 @token="nil">, #<Fast::Find:0x00007f8c53047090 @token="_">] == (send nil :object) # => true
70
+ #<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
71
+ f[_] == method # => true
72
+ # sample.rb:1
73
+ object.method
74
+ ```
75
+
76
+ ## `-s` for similarity
77
+
78
+ Sometimes you want to search for some similar code like `(send (send (send nil _) _) _)` and we could simply say `a.b.c`.
79
+
80
+ The option `-s` build an expression from the code ignoring final values.
81
+
82
+ $ echo 'object.method' > sample.rb
83
+ $ fast -s 'a.b' sample.rb
84
+
85
+ ```ruby
86
+ # sample.rb:1
87
+ object.method
88
+ ```
89
+
90
+ See also [Code Similarity](simi)ilarity_tutorial.md) tutorial.
91
+
92
+ # `-c` to search from code example
93
+
94
+ You can search for the exact expression with `-c`
95
+
96
+ $ fast -c 'object.method' sample.rb
97
+
98
+ ```ruby
99
+ # sample.rb:1
100
+ object.method
101
+ ```
102
+
103
+ Combining with `-d`, in the header you can see the generated expression.
104
+
105
+ ```
106
+ $ fast -d -c 'object.method' sample.rb | head -n 3
107
+
108
+ The generated expression from AST was:
109
+ (send
110
+ (send nil :object) :method)
111
+ ```
112
+
@@ -0,0 +1,147 @@
1
+ # Experiments
2
+
3
+ Experiments allow us to play with AST and do some code transformation, execute
4
+ some code and continue combining successful transformations.
5
+
6
+ The major idea is try a new approach without any promise and if it works
7
+ continue transforming the code.
8
+
9
+ ## Replace `FactoryBot#create` with `build_stubbed`.
10
+
11
+ Let's look into the following spec example:
12
+
13
+ ```ruby
14
+ describe "my spec" do
15
+ let(:user) { create(:user) }
16
+ let(:address) { create(:address) }
17
+ # ...
18
+ end
19
+ ```
20
+
21
+ Let's say we're amazed with `FactoryBot#build_stubbed` and want to build a small
22
+ bot to make the changes in a entire code base. Skip some database
23
+ touches while testing huge test suites are always a good idea.
24
+
25
+ First we can hunt for the cases we want to find:
26
+
27
+ ```
28
+ $ ruby-parse -e "create(:user)"
29
+ (send nil :create
30
+ (sym :user))
31
+ ```
32
+
33
+ Using `fast` in the command line to see real examples in the `spec` folder:
34
+
35
+ ```
36
+ $ fast "(send nil create)" spec
37
+ ```
38
+
39
+ If you don't have a real project but want to test, just create a sample ruby
40
+ file with the code example above.
41
+
42
+ Running it in a big codebase will probably find a few examples of blocks.
43
+
44
+ The next step is build a replacement of each independent occurrence to use
45
+ `build_stubbed` instead of create and combine the successful ones, run again and
46
+ combine again, until try all kind of successful replacements combined.
47
+
48
+ Considering we have the following code in `sample_spec.rb`:
49
+
50
+ ```ruby
51
+ describe "my spec" do
52
+ let(:user) { create(:user) }
53
+ let(:address) { create(:address) }
54
+ # ...
55
+ end
56
+ ```
57
+
58
+ Let's create the experiment that will contain the nodes that are target to be
59
+ executed and what we want to do when we find the node.
60
+
61
+ ```ruby
62
+ experiment = Fast.experiment('RSpec/ReplaceCreateWithBuildStubbed') do
63
+ search '(send nil create)'
64
+ edit { |node| replace(node.loc.selector, 'build_stubbed') }
65
+ end
66
+ ```
67
+
68
+ If we use `Fast.replace_file` it will replace all occurrences in the same run
69
+ and that's one of the motivations behind create the `ExperimentFile` class.
70
+
71
+ Executing a partial replacement of the first occurrence:
72
+
73
+ ```ruby
74
+ experiment_file = Fast::ExperimentFile.new('sample_spec.rb', experiment) }
75
+ puts experiment_file.partial_replace(1)
76
+ ```
77
+
78
+ The command will output the following code:
79
+
80
+ ```ruby
81
+ describe "my spec" do
82
+ let(:user) { build_stubbed(:user) }
83
+ let(:address) { create(:address) }
84
+ # ...
85
+ end
86
+ ```
87
+
88
+ ## Remove useless before block
89
+
90
+ Imagine the following code sample:
91
+
92
+ ```ruby
93
+ describe "my spec" do
94
+ before { create(:user) }
95
+ # ...
96
+ after { User.delete_all }
97
+ end
98
+ ```
99
+
100
+ And now, we can define an experiment that removes the entire code block and run
101
+ the experimental specs.
102
+
103
+ ```ruby
104
+ experiment = Fast.experiment('RSpec/RemoveUselessBeforeAfterHook') do
105
+ lookup 'spec'
106
+ search '(block (send nil {before after}))'
107
+ edit { |node| remove(node.loc.expression) }
108
+ policy { |new_file| system("bin/spring rspec --fail-fast #{new_file}") }
109
+ end
110
+ ```
111
+
112
+ To run the experiment you can simply say:
113
+
114
+ ```ruby
115
+ experiment.run
116
+ ```
117
+
118
+ Or drop the code into `experiments` folder and use the `fast-experiment` command
119
+ line tool.
120
+
121
+ $ fast-experiment RSpec/RemoveUselessBeforeAfterHook spec
122
+
123
+ ## DSL
124
+
125
+ - In the `lookup` you can pass files or folders.
126
+ - The `search` contains the expression you want to match
127
+ - With `edit` block you can apply the code change
128
+ - And the `policy` is executed to check if the current change is valuable
129
+
130
+ If the file contains multiple `before` or `after` blocks, each removal will
131
+ occur independently and the successfull removals will be combined as a
132
+ secondary change. The process repeates until find all possible combinations.
133
+
134
+ See more examples in [experiments](experiments) folder.
135
+
136
+ To run multiple experiments, use `fast-experiment` runner:
137
+
138
+ ```
139
+ fast-experiment <experiment-names> <files-or-folders>
140
+ ```
141
+
142
+ You can limit experiments or file escope:
143
+
144
+ ```
145
+ fast-experiment RSpec/RemoveUselessBeforeAfterHook spec/models/**/*_spec.rb
146
+ ```
147
+