ffast 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docs/ideas.md +80 -0
- data/docs/research.md +93 -0
- data/docs/shortcuts.md +64 -29
- data/fast.gemspec +1 -1
- data/lib/fast.rb +3 -2
- data/lib/fast/version.rb +1 -1
- data/mkdocs.yml +2 -0
- metadata +10 -9
- data/.github/FUNDING.yml +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18d062e72ef849cb0b6533af8c6dc37a6612036faead3c53e1d0427a001e8f24
|
4
|
+
data.tar.gz: 55b4dfdd79ee4b789fa7652760fb3516a49f68b7503d827042b78c5101fecee6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 813ca8cbe0bad2ce3f6dce1477820b68c00ac06021a3fd4263df9ea5634a6510ae0cac6cd178ffc8342b31bf7e956522a482d809dbcbe3500a488b6bc609a78a
|
7
|
+
data.tar.gz: 7fb04918062ca0f972622fe69e8d6bb77631b1e7df9ba9aa5b1e37385c0cdbcd802324d6e46801a89e53d4f30b7fe92edf51ad3eba0252cf6fce5183c76f60de
|
data/docs/ideas.md
ADDED
@@ -0,0 +1,80 @@
|
|
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
|
+
## 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
|
+
## Ast Diff
|
35
|
+
|
36
|
+
Allow to compare and return a summary of differences between two trees.
|
37
|
+
|
38
|
+
It would be useful to identify renamings or other small changes, like only
|
39
|
+
changes in comments that does not affect the file and possibly be ignored for
|
40
|
+
some operations like run or not run tests.
|
41
|
+
|
42
|
+
## Transition synapses
|
43
|
+
|
44
|
+
Following the previous idea, it would be great if we can understand the
|
45
|
+
transition synapses and make it easily available to catch up with previous
|
46
|
+
learnings.
|
47
|
+
|
48
|
+
https://github.com/jonatas/chewy-diff/blob/master/lib/chewy/diff.rb
|
49
|
+
|
50
|
+
This example, shows adds and removals from specific node targets between two
|
51
|
+
different files.
|
52
|
+
|
53
|
+
If we start tracking AST transition synapses and associating with "Fixes" or
|
54
|
+
"Reverts" we can predict introduction of new bugs by inpecting if the
|
55
|
+
introduction of new patterns that can be possibly reverted or improved.
|
56
|
+
|
57
|
+
## Fast Rewriter with pure strings
|
58
|
+
|
59
|
+
As the AST rewriter adopts a custom block that needs to implement ruby code,
|
60
|
+
we can expand the a query language for rewriting files without need to take the
|
61
|
+
custom Ruby block.
|
62
|
+
|
63
|
+
Example:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
Fast.gsub_expression('remove(@expression)') # (node) => { remove(node.location.expression) }
|
67
|
+
```
|
68
|
+
|
69
|
+
And later we can bind it in the command line to allow implement custom
|
70
|
+
replacements without need to write a ruby file.
|
71
|
+
|
72
|
+
```
|
73
|
+
fast (def my_target_method) lib spec --rewrite "remove(@expression)"
|
74
|
+
```
|
75
|
+
|
76
|
+
or
|
77
|
+
|
78
|
+
```
|
79
|
+
fast (def my_target_method) lib spec --rewrite "replace(@name, 'renamed_method')"
|
80
|
+
```
|
data/docs/research.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
# Research
|
3
|
+
|
4
|
+
I love to research about codebase as data and prototyping ideas several times
|
5
|
+
doesn't fit in simple [shortcuts](/shortcuts).
|
6
|
+
|
7
|
+
Here is my first research that worth sharing:
|
8
|
+
|
9
|
+
## Combining Runtime metadata with AST complex searches
|
10
|
+
|
11
|
+
This example covers how to find RSpec `allow` combined with `and_return` missing
|
12
|
+
the `with` clause specifying the nested parameters.
|
13
|
+
|
14
|
+
Here is the [gist](https://gist.github.com/jonatas/c1e580dcb74e20d4f2df4632ceb084ef)
|
15
|
+
if you want to go straight and run it.
|
16
|
+
|
17
|
+
Scenario for simple example:
|
18
|
+
|
19
|
+
Given I have the following class:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class Account
|
23
|
+
def withdraw(value)
|
24
|
+
if @total >= value
|
25
|
+
@total -= value
|
26
|
+
:ok
|
27
|
+
else
|
28
|
+
:not_allowed
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
And I'm testing it with `allow` and some possibilities:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# bad
|
38
|
+
allow(Account).to receive(:withdraw).and_return(:ok)
|
39
|
+
# good
|
40
|
+
allow(Account).to receive(:withdraw).with(100).and_return(:ok)
|
41
|
+
```
|
42
|
+
|
43
|
+
**Objective:** find all bad cases of **any** class that does not respect the method
|
44
|
+
parameters signature.
|
45
|
+
|
46
|
+
First, let's understand the method signature of a method:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Account.instance_method(:withdraw).parameters
|
50
|
+
# => [[:req, :value]]
|
51
|
+
```
|
52
|
+
|
53
|
+
Now, we can build a small script to use the node pattern to match the proper
|
54
|
+
specs that are using such pattern and later visit their method signatures.
|
55
|
+
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
Fast.class_eval do
|
59
|
+
# Captures class and method name when find syntax like:
|
60
|
+
# `allow(...).to receive(...)` that does not end with `.with(...)`
|
61
|
+
pattern_with_captures = <<~FAST
|
62
|
+
(send (send nil allow (const nil $_)) to
|
63
|
+
(send (send nil receive (sym $_)) !with))
|
64
|
+
FAST
|
65
|
+
|
66
|
+
pattern = expression(pattern_with_captures.tr('$',''))
|
67
|
+
|
68
|
+
ruby_files_from('spec').each do |file|
|
69
|
+
results = search_file(pattern, file) || [] rescue next
|
70
|
+
results.each do |n|
|
71
|
+
clazz, method = capture(n, pattern_with_captures)
|
72
|
+
if klazz = Object.const_get(clazz.to_s) rescue nil
|
73
|
+
if klazz.respond_to?(method)
|
74
|
+
params = klazz.method(method).parameters
|
75
|
+
if params.any?{|e|e.first == :req}
|
76
|
+
code = n.loc.expression
|
77
|
+
range = [code.first_line, code.last_line].uniq.join(",")
|
78
|
+
boom_message = "BOOM! #{clazz}.#{method} does not include the REQUIRED parameters!"
|
79
|
+
puts boom_message, "#{file}:#{range}", code.source
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
!!! hint "Preload your environment **before** run the script"
|
89
|
+
|
90
|
+
Keep in mind that you should run it with your environment preloaded otherwise it
|
91
|
+
will skip the classes.
|
92
|
+
You can add elses for `const_get` and `respond_to` and report weird cases if
|
93
|
+
your environment is not preloading properly.
|
data/docs/shortcuts.md
CHANGED
@@ -11,6 +11,70 @@ first param is not a shortcut. It should start with `.`.
|
|
11
11
|
I'm building several researches and I'll make the examples open here to show
|
12
12
|
several interesting cases in action.
|
13
13
|
|
14
|
+
## List your fast shortcuts
|
15
|
+
|
16
|
+
As the interface is very rudimentar, let's build a shortcut to print what
|
17
|
+
shortcuts are available. This is a good one to your `$HOME/Fastfile`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# List all shortcut with comments
|
21
|
+
Fast.shortcut :shortcuts do
|
22
|
+
fast_files.each do |file|
|
23
|
+
lines = File.readlines(file).map{|line|line.chomp.gsub(/\s*#/,'').strip}
|
24
|
+
result = capture_file('(send ... shortcut $(sym _', file)
|
25
|
+
result = [result] unless result.is_a?Array
|
26
|
+
result.each do |capture|
|
27
|
+
target = capture.loc.expression
|
28
|
+
puts "fast .#{target.source[1..-1].ljust(30)} # #{lines[target.line-2]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
And using it on `fast` project that loads both `~/Fastfile` and the Fastfile from the project:
|
35
|
+
|
36
|
+
```
|
37
|
+
fast .version # Let's say you'd like to show the version that is over the version file
|
38
|
+
fast .parser # Simple shortcut that I used often to show how the expression parser works
|
39
|
+
fast .bump_version # Use `fast .bump_version` to rewrite the version file
|
40
|
+
fast .shortcuts # List all shortcut with comments
|
41
|
+
```
|
42
|
+
|
43
|
+
## Search for references
|
44
|
+
|
45
|
+
I always miss bringing something simple as `grep keyword` where I can leave a simple string and it can
|
46
|
+
search in all types of nodes and report interesting things about it.
|
47
|
+
|
48
|
+
Let's consider a very flexible search that can target any code related to some
|
49
|
+
keyword. Considering that we're talking about code indentifiers:
|
50
|
+
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# Search all references about some keyword or regular expression
|
54
|
+
Fast.shortcut(:ref) do
|
55
|
+
require 'fast/cli'
|
56
|
+
Kernel.class_eval do
|
57
|
+
def matches_args? identifier
|
58
|
+
search = ARGV.last
|
59
|
+
regex = Regexp.new(search, Regexp::IGNORECASE)
|
60
|
+
case identifier
|
61
|
+
when Symbol, String
|
62
|
+
regex.match?(identifier) || identifier.to_s.include?(search)
|
63
|
+
when Astrolabe::Node
|
64
|
+
regex.match?(identifier.to_sexp)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
pattern = <<~FAST
|
69
|
+
{
|
70
|
+
({class def sym str} #matches_args?)'
|
71
|
+
({const send} nil #matches_args?)'
|
72
|
+
}
|
73
|
+
FAST
|
74
|
+
Fast::Cli.run!([pattern, '.', '--parallel'])
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
14
78
|
## Rails: Show validations from models
|
15
79
|
|
16
80
|
If the shortcut does not define a block, it works as a holder for arguments from
|
@@ -73,35 +137,6 @@ module Fast
|
|
73
137
|
end
|
74
138
|
```
|
75
139
|
|
76
|
-
## List Shortcuts
|
77
|
-
|
78
|
-
As the interface is very rudimentar, let's build a shortcut to print what
|
79
|
-
shortcuts are available. This is a good one to your `$HOME/Fastfile`:
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
# List all shortcut with comments
|
83
|
-
Fast.shortcut :shortcuts do
|
84
|
-
fast_files.each do |file|
|
85
|
-
lines = File.readlines(file).map{|line|line.chomp.gsub(/\s*#/,'').strip}
|
86
|
-
result = capture_file('(send ... shortcut $(sym _', file)
|
87
|
-
result = [result] unless result.is_a?Array
|
88
|
-
result.each do |capture|
|
89
|
-
target = capture.loc.expression
|
90
|
-
puts "fast .#{target.source[1..-1].ljust(30)} # #{lines[target.line-2]}"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
```
|
95
|
-
|
96
|
-
And using it on `fast` project that loads both `~/Fastfile` and the Fastfile from the project:
|
97
|
-
|
98
|
-
```
|
99
|
-
fast .version # Let's say you'd like to show the version that is over the version file
|
100
|
-
fast .parser # Simple shortcut that I used often to show how the expression parser works
|
101
|
-
fast .bump_version # Use `fast .bump_version` to rewrite the version file
|
102
|
-
fast .shortcuts # List all shortcut with comments
|
103
|
-
```
|
104
|
-
|
105
140
|
## RSpec: Find unused shared contexts
|
106
141
|
|
107
142
|
If you build shared contexts often, probably you can forget some left overs.
|
data/fast.gemspec
CHANGED
@@ -28,12 +28,12 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_dependency 'coderay'
|
29
29
|
spec.add_dependency 'parallel'
|
30
30
|
spec.add_dependency 'parser'
|
31
|
-
spec.add_dependency 'pry'
|
32
31
|
|
33
32
|
spec.add_development_dependency 'bundler'
|
34
33
|
spec.add_development_dependency 'guard'
|
35
34
|
spec.add_development_dependency 'guard-livereload'
|
36
35
|
spec.add_development_dependency 'guard-rspec'
|
36
|
+
spec.add_development_dependency 'pry'
|
37
37
|
spec.add_development_dependency 'rake'
|
38
38
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
39
|
spec.add_development_dependency 'rspec-its', '~> 1.2'
|
data/lib/fast.rb
CHANGED
@@ -226,14 +226,15 @@ module Fast
|
|
226
226
|
# @param files can be file paths or directories.
|
227
227
|
# When the argument is a folder, it recursively fetches all `.rb` files from it.
|
228
228
|
def ruby_files_from(*files)
|
229
|
-
|
229
|
+
dir_filter = File.method(:directory?)
|
230
|
+
directories = files.select(&dir_filter)
|
230
231
|
|
231
232
|
if directories.any?
|
232
233
|
files -= directories
|
233
234
|
files |= directories.flat_map { |dir| Dir["#{dir}/**/*.rb"] }
|
234
235
|
files.uniq!
|
235
236
|
end
|
236
|
-
files
|
237
|
+
files.reject(&dir_filter)
|
237
238
|
end
|
238
239
|
|
239
240
|
# Extracts a node pattern expression from a given node supressing identifiers and primitive types.
|
data/lib/fast/version.rb
CHANGED
data/mkdocs.yml
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
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: 2020-
|
11
|
+
date: 2020-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: astrolabe
|
@@ -67,13 +67,13 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
|
-
type: :
|
76
|
+
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: guard
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name: guard
|
98
|
+
name: guard-livereload
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name: guard-
|
112
|
+
name: guard-rspec
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: pry
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -243,7 +243,6 @@ executables:
|
|
243
243
|
extensions: []
|
244
244
|
extra_rdoc_files: []
|
245
245
|
files:
|
246
|
-
- ".github/FUNDING.yml"
|
247
246
|
- ".gitignore"
|
248
247
|
- ".projections.json"
|
249
248
|
- ".rspec"
|
@@ -264,8 +263,10 @@ files:
|
|
264
263
|
- docs/command_line.md
|
265
264
|
- docs/editors-integration.md
|
266
265
|
- docs/experiments.md
|
266
|
+
- docs/ideas.md
|
267
267
|
- docs/index.md
|
268
268
|
- docs/pry-integration.md
|
269
|
+
- docs/research.md
|
269
270
|
- docs/shortcuts.md
|
270
271
|
- docs/similarity_tutorial.md
|
271
272
|
- docs/syntax.md
|
data/.github/FUNDING.yml
DELETED