ffast 0.2.0 → 0.2.3
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/.github/workflows/release.yml +27 -0
- data/.github/workflows/ruby.yml +34 -0
- data/.gitignore +2 -0
- data/Fastfile +146 -3
- data/README.md +244 -132
- data/bin/console +6 -1
- data/bin/fast-experiment +3 -0
- data/bin/fast-mcp +7 -0
- data/fast.gemspec +24 -7
- data/lib/fast/cli.rb +129 -38
- data/lib/fast/experiment.rb +19 -2
- data/lib/fast/git.rb +1 -1
- data/lib/fast/mcp_server.rb +317 -0
- data/lib/fast/node.rb +258 -0
- data/lib/fast/prism_adapter.rb +310 -0
- data/lib/fast/rewriter.rb +64 -10
- data/lib/fast/scan.rb +203 -0
- data/lib/fast/shortcut.rb +23 -6
- data/lib/fast/source.rb +116 -0
- data/lib/fast/source_rewriter.rb +153 -0
- data/lib/fast/sql/rewriter.rb +98 -0
- data/lib/fast/sql.rb +165 -0
- data/lib/fast/summary.rb +435 -0
- data/lib/fast/version.rb +1 -1
- data/lib/fast.rb +165 -79
- data/mkdocs.yml +27 -3
- data/requirements-docs.txt +3 -0
- metadata +48 -62
- data/docs/command_line.md +0 -238
- data/docs/editors-integration.md +0 -46
- data/docs/experiments.md +0 -153
- data/docs/ideas.md +0 -80
- data/docs/index.md +0 -402
- data/docs/pry-integration.md +0 -27
- data/docs/research.md +0 -93
- data/docs/shortcuts.md +0 -323
- data/docs/similarity_tutorial.md +0 -176
- data/docs/syntax.md +0 -395
- data/docs/videos.md +0 -16
- data/examples/build_stubbed_and_let_it_be_experiment.rb +0 -51
- data/examples/experimental_replacement.rb +0 -46
- data/examples/find_usage.rb +0 -26
- data/examples/let_it_be_experiment.rb +0 -11
- data/examples/method_complexity.rb +0 -37
- data/examples/search_duplicated.rb +0 -15
- data/examples/similarity_research.rb +0 -58
- data/examples/simple_rewriter.rb +0 -6
- data/experiments/let_it_be_experiment.rb +0 -9
- data/experiments/remove_useless_hook.rb +0 -9
- data/experiments/replace_create_with_build_stubbed.rb +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da30bc7abc983e2ac54c1c4d1f454878d75dce0dd285a0e245d6ed54d73e4040
|
|
4
|
+
data.tar.gz: f286ce93d5ce2970d1c3a528d423bf8b8ed9239140f798f895a67e0aa1b8e2d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83c218849cd4a9184b34f9d760d537cae650fe8a96428aa83fdeff12ed6afc57129fff3ce4a0c6d6924225e178f80c114afb021e19ca41bc2677c85b0668ee99
|
|
7
|
+
data.tar.gz: 6d4499e8a11398c3f9bc8a04adcd5efd65ef1b385c9bc37370edc31f9375b8d844e8ed61981e33831b2b83961edd4245059a38167c2f7a5ffbc52dd49acb9fff
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Release Gem
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*' # Triggers when a new tag like v0.2.0 is pushed
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
name: Build and Release
|
|
11
|
+
runs-on: ubuntu-22.04
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read # Required to checkout the code
|
|
14
|
+
id-token: write # Required for RubyGems Trusted Publishing
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Ruby
|
|
21
|
+
uses: ruby/setup-ruby@v1
|
|
22
|
+
with:
|
|
23
|
+
ruby-version: '3.3'
|
|
24
|
+
bundler-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Publish to RubyGems
|
|
27
|
+
uses: rubygems/release-gem@v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Ruby
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [ "master" ]
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [ "master" ]
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
test:
|
|
21
|
+
runs-on: ubuntu-22.04
|
|
22
|
+
strategy:
|
|
23
|
+
matrix:
|
|
24
|
+
ruby-version: ['3.0', '3.1', '3.2', '3.3']
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- name: Set up Ruby
|
|
29
|
+
uses: ruby/setup-ruby@v1
|
|
30
|
+
with:
|
|
31
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
32
|
+
bundler-cache: true
|
|
33
|
+
- name: Run tests
|
|
34
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/Fastfile
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
begin
|
|
3
|
+
require 'fast/source'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
nil
|
|
6
|
+
end
|
|
2
7
|
|
|
3
8
|
# Fastfile is loaded when you start an expression with a dot.
|
|
4
9
|
#
|
|
@@ -6,7 +11,8 @@
|
|
|
6
11
|
# command line interactions with fast.
|
|
7
12
|
#
|
|
8
13
|
# Let's say you'd like to show the version that is over the version file
|
|
9
|
-
|
|
14
|
+
version_file = Dir['lib/*/version.rb'].first
|
|
15
|
+
Fast.shortcut(:version, '(casgn nil VERSION (str _))', version_file)
|
|
10
16
|
|
|
11
17
|
# Show all classes that inherits Fast::Find
|
|
12
18
|
Fast.shortcut(:finders, '(class ... (const nil Find)', 'lib')
|
|
@@ -18,10 +24,12 @@ Fast.shortcut(:finders, '(class ... (const nil Find)', 'lib')
|
|
|
18
24
|
|
|
19
25
|
# Simple shortcut that I used often to show how the expression parser works
|
|
20
26
|
Fast.shortcut(:parser, '(class (const nil ExpressionParser)', 'lib/fast.rb')
|
|
27
|
+
Fast.shortcut(:sql_parser, '(def parse', 'lib/fast/sql.rb')
|
|
21
28
|
|
|
22
29
|
# Use `fast .bump_version` to rewrite the version file
|
|
23
30
|
Fast.shortcut :bump_version do
|
|
24
|
-
|
|
31
|
+
new_version = nil
|
|
32
|
+
rewrite_file('(casgn nil VERSION (str _)', version_file) do |node|
|
|
25
33
|
target = node.children.last.loc.expression
|
|
26
34
|
pieces = target.source.split('.').map(&:to_i)
|
|
27
35
|
pieces.reverse.each_with_index do |fragment, i|
|
|
@@ -32,6 +40,141 @@ Fast.shortcut :bump_version do
|
|
|
32
40
|
pieces[-(i + 1)] = 0
|
|
33
41
|
end
|
|
34
42
|
end
|
|
35
|
-
|
|
43
|
+
new_version = pieces.join('.')
|
|
44
|
+
replace(target, "'#{new_version}'")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
print "Commit bumped version v#{new_version}? (y/n) "
|
|
48
|
+
if $stdin.gets.chomp.downcase == 'y'
|
|
49
|
+
system("git add #{version_file} && git commit -m 'Bump version to v#{new_version}'")
|
|
50
|
+
|
|
51
|
+
print "Tag version v#{new_version}? (y/n) "
|
|
52
|
+
if $stdin.gets.chomp.downcase == 'y'
|
|
53
|
+
system("git tag v#{new_version}")
|
|
54
|
+
|
|
55
|
+
print "Push commit and tag to origin? (y/n) "
|
|
56
|
+
if $stdin.gets.chomp.downcase == 'y'
|
|
57
|
+
system("git push origin HEAD")
|
|
58
|
+
system("git push origin v#{new_version}")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# List all shortcut with comments
|
|
65
|
+
Fast.shortcut :shortcuts do
|
|
66
|
+
fast_files.each do |file|
|
|
67
|
+
lines = File.readlines(file).map { |line| line.chomp.gsub(/\s*#/, '').strip }
|
|
68
|
+
result = capture_file('(send _ :shortcut $(sym _) ...)', file)
|
|
69
|
+
result = [result] unless result.is_a? Array
|
|
70
|
+
result.each do |capture|
|
|
71
|
+
target = capture.loc.expression
|
|
72
|
+
puts "fast .#{target.source[1..].ljust(30)} # #{lines[target.line - 2]}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Use to walkthrough the docs files with fast examples
|
|
78
|
+
# fast .intro
|
|
79
|
+
Fast.shortcut :intro do
|
|
80
|
+
ARGV << File.join(File.dirname(__FILE__), 'docs', 'walkthrough.md')
|
|
81
|
+
|
|
82
|
+
Fast.shortcuts[:walk].run
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Interactive command line walkthrough
|
|
86
|
+
# fast .walk docs/walkthrough.md
|
|
87
|
+
Fast.shortcut :walk do
|
|
88
|
+
file = ARGV.last
|
|
89
|
+
execute = ->(line) { system(line) }
|
|
90
|
+
walk = ->(line) { line.each_char { |c| sleep(0.02) and print(c) } }
|
|
91
|
+
File.readlines(file).each do |line|
|
|
92
|
+
case line
|
|
93
|
+
when /^fast /
|
|
94
|
+
walk[line]
|
|
95
|
+
execute[line]
|
|
96
|
+
when /^\$ /
|
|
97
|
+
walk[line]
|
|
98
|
+
execute[line[2..]]
|
|
99
|
+
when /^!{3}\s/
|
|
100
|
+
# Skip warnings that are only for web tutorials
|
|
101
|
+
else
|
|
102
|
+
walk[Fast.render_markdown_for_terminal(line)]
|
|
103
|
+
end
|
|
36
104
|
end
|
|
37
105
|
end
|
|
106
|
+
|
|
107
|
+
# Format SQL
|
|
108
|
+
Fast.shortcut :format_sql do
|
|
109
|
+
require 'fast/sql'
|
|
110
|
+
file = ARGV.last
|
|
111
|
+
method = File.exist?(file) ? :parse_sql_file : :parse_sql
|
|
112
|
+
ast = Fast.public_send(method, file)
|
|
113
|
+
ast = ast.first if ast.is_a? Array
|
|
114
|
+
eligible_kw = [:RESERVED_KEYWORD]
|
|
115
|
+
eligible_tokens = [:BY]
|
|
116
|
+
|
|
117
|
+
output = Fast::SQL.replace('_', ast) do |root|
|
|
118
|
+
sb = root.loc.expression.source_buffer
|
|
119
|
+
sb.tokens.each do |token|
|
|
120
|
+
if eligible_kw.include?(token.keyword_kind) || eligible_tokens.include?(token.token)
|
|
121
|
+
range = Fast::Source.range(sb, token.start, token.end)
|
|
122
|
+
replace(range, range.source.upcase)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
require 'fast/cli'
|
|
127
|
+
puts Fast.highlight(output, sql: true)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Anonymize SQL
|
|
131
|
+
# fast .anonymize_sql file.sql
|
|
132
|
+
Fast.shortcut :anonymize_sql do
|
|
133
|
+
require 'fast/sql'
|
|
134
|
+
file = ARGV.last
|
|
135
|
+
method = File.exist?(file) ? :parse_sql_file : :parse_sql
|
|
136
|
+
ast = Fast.public_send(method, file)
|
|
137
|
+
memo = {}
|
|
138
|
+
|
|
139
|
+
relnames = search("(relname $_)", ast).grep(String).uniq
|
|
140
|
+
pattern = "{relname (sval {#{relnames.map(&:inspect).join(' ')}})}"
|
|
141
|
+
puts "searching with #{pattern}"
|
|
142
|
+
|
|
143
|
+
content = Fast::SQL.replace(pattern, ast) do |node|
|
|
144
|
+
new_name = memo[node.source.tr(%|"'|, '')] ||= "x#{memo.size}"
|
|
145
|
+
new_name = "'#{new_name}'" if node.type == :sval
|
|
146
|
+
replace(node.loc.expression, new_name)
|
|
147
|
+
end
|
|
148
|
+
puts Fast.highlight(content, sql: true)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Give all details in a shorter format
|
|
152
|
+
# fast .summary file.rb
|
|
153
|
+
Fast.shortcut :summary do
|
|
154
|
+
file = ARGV.reverse.find { |arg| !arg.start_with?('-') && File.exist?(arg) }
|
|
155
|
+
if file && File.exist?(file)
|
|
156
|
+
Fast.summary(IO.read(file), file: file, level: fast_option_value(ARGV, '-l', '--level')).summarize
|
|
157
|
+
else
|
|
158
|
+
puts "Please provide a valid file to summarize."
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Group and classify multiple Ruby files without printing full bodies
|
|
163
|
+
# fast .scan lib app/models --no-color
|
|
164
|
+
Fast.shortcut :scan do
|
|
165
|
+
locations = ARGV.select { |arg| !arg.start_with?('-') && File.exist?(arg) }
|
|
166
|
+
if locations.any?
|
|
167
|
+
Fast.scan(locations, level: fast_option_value(ARGV, '-l', '--level')).scan
|
|
168
|
+
else
|
|
169
|
+
puts "Please provide at least one valid file or directory to scan."
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def fast_option_value(args, short_name, long_name)
|
|
174
|
+
args.each_with_index do |arg, index|
|
|
175
|
+
return args[index + 1] if arg == short_name || arg == long_name
|
|
176
|
+
return arg.split('=', 2).last if arg.start_with?("#{long_name}=")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
nil
|
|
180
|
+
end
|