Module: Fast
- Defined in:
- lib/fast.rb,
lib/fast/cli.rb,
lib/fast/git.rb,
lib/fast/sql.rb,
lib/fast/node.rb,
lib/fast/scan.rb,
lib/fast/source.rb,
lib/fast/summary.rb,
lib/fast/version.rb,
lib/fast/rewriter.rb,
lib/fast/shortcut.rb,
lib/fast/experiment.rb,
lib/fast/mcp_server.rb,
lib/fast/sql/rewriter.rb,
lib/fast/prism_adapter.rb,
lib/fast/source_rewriter.rb
Overview
Allow to replace code managing multiple replacements and combining replacements. Useful for large codebase refactor and multiple replacements in the same file.
Defined Under Namespace
Modules: PrismAdapter, SQL, Source, SymbolExtension Classes: All, Any, Capture, Cli, Experiment, ExperimentCombinations, ExperimentFile, ExpressionParser, Find, FindFromArgument, FindString, FindWithCapture, InstanceMethodCall, Matcher, Maybe, McpServer, MethodCall, Node, Not, Parent, Rewriter, Scan, Shortcut, SourceRewriter, Summary, SyntaxError
Constant Summary collapse
- NODE_PARENTS =
ObjectSpace::WeakMap.new
- LITERAL =
Literals are shortcuts allowed inside ExpressionParser
{ '...' => ->(node) { node&.children&.any? }, '_' => ->(node) { !node.nil? }, 'nil' => nil }.freeze
- TOKENIZER =
Allowed tokens in the node pattern domain
%r/ [\+\-\/\*\\!] # operators or negation | ===? # == or === | \d+\.\d* # decimals and floats | "[^"]+" # strings | _ # something not nil: match | \.{3} # a node with children: ... | \[|\] # square brackets `[` and `]` for all | \^ # node has children with | \? # maybe expression | [\d\w_]+[=\\!\?]? # method names or numbers | :[^(){}\[\]\s;]+ # symbols | \/[^\/ \n]+\/ # regex literals | ; # semicolons | \(|\) # parens `(` and `)` for tuples | \{|\} # curly brackets `{` and `}` for any | \$ # capture | \#\w[\d\w_]+[\\!\?]? # custom method call | \.\w[\d\w_]+\? # instance method call | \\\d # find using captured expression | %\d # bind extra arguments to the expression /x.freeze
- VERSION =
'0.2.7'- LOOKUP_FAST_FILES_DIRECTORIES =
Where to search for
Fastfilearchives?- Current directory that the command is being runned
- Home folder
- Using the
FAST_FILE_DIRvariable to set an extra folder
[ Dir.pwd, ENV['HOME'], ENV['FAST_FILE_DIR'], File.join(File.dirname(__FILE__), '..', '..') ].reverse.compact.map(&File.method(:expand_path)).uniq.freeze
Class Attribute Summary collapse
-
.debugging ⇒ Object
Returns the value of attribute debugging.
-
.experiments ⇒ Object
readonly
Returns the value of attribute experiments.
Class Method Summary collapse
-
.ast(content, buffer_name: '(string)') ⇒ Fast::Node
From the parsed content.
-
.ast_from_file(file) ⇒ Fast::Node
caches the content based on the filename.
- .ast_node?(node) ⇒ Boolean
-
.build_grouped_search(method_name, pattern, on_result) ⇒ Proc
Binding
patternargument from a givenmethod_name. - .builder_for(buffer_name) ⇒ Object
-
.capture(pattern, node) ⇒ Array<Object>
Only captures from a search.
-
.capture_all(pattern, locations = ['.'], parallel: true, on_result: nil) ⇒ Hash<String,Object>
Capture with pattern on a directory or multiple files.
-
.capture_file(pattern, file) ⇒ Array<Object>
Capture elements from searches in files.
-
.debug ⇒ Object
Utility function to inspect search details using debug block.
-
.experiment(name, &block) ⇒ Object
Fast.experiment is a shortcut to define new experiments and allow them to work together in experiment combinations.
- .expression(string) ⇒ Object
-
.expression_from(node) ⇒ String
Extracts a node pattern expression from a given node supressing identifiers and primitive types.
-
.fast_files ⇒ Array<String>
With existent Fastfiles from LOOKUP_FAST_FILES_DIRECTORIES.
-
.first_position_from_expression(node) ⇒ Object
If a node is the first on it's line, print since the beginning of the line to show the proper whitespaces for identing the next lines of the code.
-
.fold_ast(node, level: nil, current_level: 1) ⇒ Fast::Node
Folds the AST to a maximum depth, replacing deeper branches with
.... -
.fold_source(node, level: 1) ⇒ String
Folds ruby source code to a maximum depth, replacing deep block bodies with
# .... -
.group_results(group_files, locations, parallel: true) ⇒ Hash[String, Array]
Compact grouped results by file allowing parallel processing.
-
.highlight(node, show_sexp: false, colorize: true, sql: false, level: nil) ⇒ Object
Highligh some source code based on the node.
-
.last_position_from_expression(node) ⇒ Object
If a method call contains a heredoc, it should print the STR around it too.
-
.load_fast_files! ⇒ Object
Loads
Fastfilesfrom Fast.fast_files list. -
.match?(pattern, ast, *args) ⇒ Boolean
Verify if a given AST matches with a specific pattern.
- .parse_file(file) ⇒ Object
- .parse_ruby(content, buffer_name: '(string)') ⇒ Object
-
.parse_sql(statement, buffer_name: "(sql)") ⇒ Fast::Node
ast = Fast.parse_sql("select 'hello AST'") => s(:select_stmt, s(:target_list, s(:res_target, s(:val, s(:a_const, s(:val, s(:string, s(:str, "hello AST"))))))))
srepresents a Fast::Node with additional methods to access the tokens and location of the node. -
.parse_sql_file(file) ⇒ Fast::Node
Shortcut to parse a sql file.
- .parser_ast(content, buffer_name: '(string)') ⇒ Object
- .parser_ast_from_file(file) ⇒ Object
- .parser_class ⇒ Object
- .parser_const_name ⇒ Object
- .parser_require_path ⇒ Object
- .parser_version_supported?(const_name) ⇒ Boolean
- .prism_ast(content, buffer_name: '(string)') ⇒ Object
- .render_markdown_for_terminal(line) ⇒ Object
-
.replace(pattern, ast, source = nil, &replacement) ⇒ String
Replaces content based on a pattern.
-
.replace_file(pattern, file, &replacement) ⇒ Object
Replaces the source of an Fast.ast_from_file with and the same source if the pattern does not match.
-
.replace_sql(pattern, ast, &replacement) ⇒ Object
Fast.replace_sql('ival', Fast.parse_sql('select 1'), &->(node){ replace(node.location.expression, '2') }) # => "select 2".
-
.replace_sql_file(pattern, file, &replacement) ⇒ Object
String with the sql content updated in case the pattern matches.
-
.report(result, show_link: false, show_permalink: false, show_sexp: false, file: nil, headless: false, bodyless: false, colorize: true, level: nil) ⇒ Object
Combines Fast.highlight with files printing file name in the head with the source line.
-
.rewrite_file(pattern, file, &replacement) ⇒ Object
Combines #replace_file output overriding the file if the output is different from the original file content.
- .rewriter_for(pattern, ast, source = nil, &replacement) ⇒ Fast::Rewriter
-
.ruby_files_from(*files) ⇒ Array<String>
When the argument is a folder, it recursively fetches all
.rbfiles from it. - .scan(locations, command_name: '.scan', level: nil) ⇒ Object
-
.search(pattern, node, *args) { ... } ⇒ Object
Search recursively into a node and its children.
-
.search_all(pattern, locations = ['.'], parallel: true, on_result: nil) ⇒ Hash<String,Array<Fast::Node>>
Search with pattern on a directory or multiple files.
-
.search_file(pattern, file) ⇒ Array<Fast::Node>
Search with pattern directly on file.
-
.shortcut(identifier, *args, &block) ⇒ Object
Store predefined searches with default paths through shortcuts.
-
.shortcuts ⇒ Hash<String,Shortcut>
Stores shortcuts in a simple hash where the key is the identifier and the value is the object itself.
-
.sql_rewriter_for(pattern, ast, &replacement) ⇒ Fast::SQLRewriter
Which can be used to rewrite the SQL.
- .summary(code_or_ast, file: nil, command_name: '.summary', level: nil) ⇒ Object
- .validate_ruby!(content, buffer_name: '(string)') ⇒ Object
-
.wrap_source_range(node) ⇒ Object
Fixes initial spaces to print the line since the beginning and fixes end of the expression including heredoc strings.
Class Attribute Details
.debugging ⇒ Object
Returns the value of attribute debugging.
307 308 309 |
# File 'lib/fast.rb', line 307 def debugging @debugging end |
.experiments ⇒ Object (readonly)
Returns the value of attribute experiments.
30 31 32 |
# File 'lib/fast/experiment.rb', line 30 def experiments @experiments end |
Class Method Details
.ast(content, buffer_name: '(string)') ⇒ Fast::Node
Returns from the parsed content.
143 144 145 |
# File 'lib/fast.rb', line 143 def ast(content, buffer_name: '(string)') parse_ruby(content, buffer_name: buffer_name) end |
.ast_from_file(file) ⇒ Fast::Node
caches the content based on the filename. Also, it can parse SQL files.
161 162 163 |
# File 'lib/fast.rb', line 161 def ast_from_file(file) parse_file(file) end |
.ast_node?(node) ⇒ Boolean
66 67 68 |
# File 'lib/fast.rb', line 66 def ast_node?(node) node.respond_to?(:type) && node.respond_to?(:children) end |
.build_grouped_search(method_name, pattern, on_result) ⇒ Proc
Returns binding pattern argument from a given method_name.
209 210 211 212 213 214 215 216 217 218 |
# File 'lib/fast.rb', line 209 def build_grouped_search(method_name, pattern, on_result) search_pattern = method(method_name).curry.call(pattern) proc do |file| results = search_pattern.call(file) next if results.nil? || results.empty? on_result&.(file, results) { file => results } end end |
.builder_for(buffer_name) ⇒ Object
152 153 154 |
# File 'lib/fast.rb', line 152 def builder_for(buffer_name) raise NoMethodError, "Fast.builder_for(#{buffer_name.inspect}) was removed; Fast now parses Ruby with Prism" end |
.capture(pattern, node) ⇒ Array<Object>
Only captures from a search
287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/fast.rb', line 287 def capture(pattern, node) return [] if node.nil? if (match = match?(pattern, node)) match == true ? node : match else node.each_child_node .flat_map { |child| capture(pattern, child) } .compact.flatten end end |
.capture_all(pattern, locations = ['.'], parallel: true, on_result: nil) ⇒ Hash<String,Object>
Capture with pattern on a directory or multiple files
200 201 202 203 |
# File 'lib/fast.rb', line 200 def capture_all(pattern, locations = ['.'], parallel: true, on_result: nil) group_results(build_grouped_search(:capture_file, pattern, on_result), locations, parallel: parallel) end |
.capture_file(pattern, file) ⇒ Array<Object>
Capture elements from searches in files. Keep in mind you need to use $
in the pattern to make it work.
252 253 254 255 256 257 258 259 260 261 |
# File 'lib/fast.rb', line 252 def capture_file(pattern, file) node = ast_from_file(file) return [] unless node case node when Array node.map { |n| capture(pattern, n) }.flatten.compact else capture pattern, node end end |
.debug ⇒ Object
Utility function to inspect search details using debug block.
It prints output of all matching cases.
int == (int 1) # => true 1 == 1 # => true
319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/fast.rb', line 319 def debug return yield if debugging self.debugging = true result = nil Find.class_eval do alias_method :original_match_recursive, :match_recursive alias_method :match_recursive, :debug_match_recursive result = yield alias_method :match_recursive, :original_match_recursive # rubocop:disable Lint/DuplicateMethods end self.debugging = false result end |
.experiment(name, &block) ⇒ Object
Fast.experiment is a shortcut to define new experiments and allow them to work together in experiment combinations.
The following experiment look into spec folder and try to remove
before and after blocks on testing code. Sometimes they're not
effective and we can avoid the hard work of do it manually.
If the spec does not fail, it keeps the change.
25 26 27 28 |
# File 'lib/fast/experiment.rb', line 25 def experiment(name, &block) @experiments ||= {} @experiments[name] = Experiment.new(name, &block) end |
.expression(string) ⇒ Object
299 300 301 302 303 304 305 |
# File 'lib/fast.rb', line 299 def expression(string) parser = ExpressionParser.new(string) res = parser.parse raise SyntaxError, parser. if parser.tokens_left? res end |
.expression_from(node) ⇒ String
Extracts a node pattern expression from a given node supressing identifiers and primitive types. Useful to index abstract patterns or similar code structure.
425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/fast.rb', line 425 def expression_from(node) case node when ->(candidate) { ast_node?(candidate) } children_expression = node.children.map(&method(:expression_from)).join(' ') "(#{node.type}#{" #{children_expression}" if node.children.any?})" when nil, 'nil' 'nil' when Symbol, String, Numeric '_' end end |
.fast_files ⇒ Array<String>
Returns with existent Fastfiles from LOOKUP_FAST_FILES_DIRECTORIES.
33 34 35 36 37 |
# File 'lib/fast/shortcut.rb', line 33 def fast_files @fast_files ||= LOOKUP_FAST_FILES_DIRECTORIES.compact .map { |dir| File.join(dir, 'Fastfile') } .select(&File.method(:exist?)) end |
.first_position_from_expression(node) ⇒ Object
If a node is the first on it's line, print since the beginning of the line to show the proper whitespaces for identing the next lines of the code.
67 68 69 70 71 72 73 74 |
# File 'lib/fast/cli.rb', line 67 def first_position_from_expression(node) expression = node.loc.expression if node.respond_to?(:parent) && node.parent && node.parent.loc.expression.line != expression.line expression.begin_pos - expression.column else expression.begin_pos end end |
.fold_ast(node, level: nil, current_level: 1) ⇒ Fast::Node
Folds the AST to a maximum depth, replacing deeper branches with ...
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/fast.rb', line 354 def fold_ast(node, level: nil, current_level: 1) return node unless ast_node?(node) && node.respond_to?(:updated) return node if level.nil? if current_level >= level if node.children.any? node.updated(nil, [:'...']) else node end else new_children = node.children.map { |c| fold_ast(c, level: level, current_level: current_level + 1) } node.updated(nil, new_children) end end |
.fold_source(node, level: 1) ⇒ String
Folds ruby source code to a maximum depth, replacing deep block bodies with # ...
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/fast.rb', line 374 def fold_source(node, level: 1) return node if node.is_a?(String) return node.loc.expression.source rescue node.to_s unless ast_node?(node) && node.respond_to?(:loc) source = node.loc.expression.source.dup root_begin = node.loc.expression.begin_pos regions = [] walker = ->(n, current_level) do return unless ast_node?(n) if current_level >= level body_node = nil case n.type when :class, :module, :def, :defs, :block body_node = n.children.last when :begin body_node = n end if body_node && body_node.loc && body_node.loc.respond_to?(:expression) && body_node.loc.expression regions << [ body_node.loc.expression.begin_pos - root_begin, body_node.loc.expression.end_pos - root_begin ] return end end n.children.each { |c| walker.call(c, current_level + 1) if ast_node?(c) } end walker.call(node, 1) regions.sort_by { |r| -r[0] }.each do |start_pos, end_pos| source[start_pos...end_pos] = "# ..." end source end |
.group_results(group_files, locations, parallel: true) ⇒ Hash[String, Array]
Compact grouped results by file allowing parallel processing. parallel or not. while it process several locations in parallel.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/fast.rb', line 227 def group_results(group_files, locations, parallel: true) files = ruby_files_from(*locations) results = if parallel require 'parallel' unless defined?(Parallel) Parallel.map(files) do |file| group_files.call(file) rescue StandardError => e warn "Error processing #{file}: #{e.}" if Fast.debugging nil end else files.map do |file| group_files.call(file) rescue StandardError => e warn "Error processing #{file}: #{e.}" if Fast.debugging nil end end results.compact.inject(&:merge!) || {} end |
.highlight(node, show_sexp: false, colorize: true, sql: false, level: nil) ⇒ Object
Highligh some source code based on the node. Useful for printing code with syntax highlight.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/fast/cli.rb', line 28 def highlight(node, show_sexp: false, colorize: true, sql: false, level: nil) output = if node.respond_to?(:loc) && !show_sexp if level Fast.fold_source(node, level: level) else wrap_source_range(node).source end elsif show_sexp && level && Fast.ast_node?(node) Fast.fold_ast(node, level: level).to_s elsif show_sexp node.to_s else node.to_s end return output unless colorize CodeRay.scan(output, sql ? :sql : :ruby).term end |
.last_position_from_expression(node) ⇒ Object
If a method call contains a heredoc, it should print the STR around it too.
60 61 62 63 |
# File 'lib/fast/cli.rb', line 60 def last_position_from_expression(node) internal_heredoc = node.each_descendant(:str).select { |n| n.loc.respond_to?(:heredoc_end) } internal_heredoc.map { |n| n.loc.heredoc_end.end_pos }.max if internal_heredoc.any? end |
.load_fast_files! ⇒ Object
Loads Fastfiles from fast_files list
40 41 42 43 44 45 46 47 48 |
# File 'lib/fast/shortcut.rb', line 40 def load_fast_files! @loaded_fast_files ||= [] fast_files.each do |file| next if @loaded_fast_files.include?(file) load file @loaded_fast_files << file end end |
.match?(pattern, ast, *args) ⇒ Boolean
Verify if a given AST matches with a specific pattern
169 170 171 |
# File 'lib/fast.rb', line 169 def match?(pattern, ast, *args) Matcher.new(pattern, ast, *args).match? end |
.parse_file(file) ⇒ Object
103 104 105 106 107 108 109 110 111 |
# File 'lib/fast.rb', line 103 def parse_file(file) return parser_ast_from_file(file) if file.end_with?('.sql') @cache ||= {} @cache[file] ||= begin parse_ruby(IO.read(file), buffer_name: file) end end |
.parse_ruby(content, buffer_name: '(string)') ⇒ Object
80 81 82 |
# File 'lib/fast.rb', line 80 def parse_ruby(content, buffer_name: '(string)') prism_ast(content, buffer_name: buffer_name) end |
.parse_sql(statement, buffer_name: "(sql)") ⇒ Fast::Node
ast = Fast.parse_sql("select 'hello AST'")
=> s(:select_stmt,
s(:target_list,
s(:res_target,
s(:val,
s(:a_const,
s(:val,
s(:string,
s(:str, "hello AST"))))))))
s represents a Fast::Node with additional methods to access the tokens
and location of the node.
ast.search(:string).first.location.expression
=> #<Fast::Source::Range (sql) 7...18>
50 51 52 |
# File 'lib/fast/sql.rb', line 50 def parse_sql(statement, buffer_name: "(sql)") SQL.parse(statement, buffer_name: buffer_name) end |
.parse_sql_file(file) ⇒ Fast::Node
Shortcut to parse a sql file
12 13 14 |
# File 'lib/fast/sql.rb', line 12 def parse_sql_file(file) SQL.parse_file(file) end |
.parser_ast(content, buffer_name: '(string)') ⇒ Object
84 85 86 |
# File 'lib/fast.rb', line 84 def parser_ast(content, buffer_name: '(string)') prism_ast(content, buffer_name: buffer_name) end |
.parser_ast_from_file(file) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/fast.rb', line 88 def parser_ast_from_file(file) @parser_cache ||= {} @parser_cache[file] ||= begin method = if file.end_with?('.sql') require_relative 'fast/sql' unless respond_to?(:parse_sql) :parse_sql else :parser_ast end Fast.public_send(method, IO.read(file), buffer_name: file) end end |
.parser_class ⇒ Object
123 124 125 |
# File 'lib/fast.rb', line 123 def parser_class raise NoMethodError, 'Fast.parser_class was removed; Fast now parses Ruby with Prism' end |
.parser_const_name ⇒ Object
131 132 133 |
# File 'lib/fast.rb', line 131 def parser_const_name raise NoMethodError, 'Fast.parser_const_name was removed; Fast now parses Ruby with Prism' end |
.parser_require_path ⇒ Object
127 128 129 |
# File 'lib/fast.rb', line 127 def parser_require_path raise NoMethodError, 'Fast.parser_require_path was removed; Fast now parses Ruby with Prism' end |
.parser_version_supported?(const_name) ⇒ Boolean
135 136 137 |
# File 'lib/fast.rb', line 135 def parser_version_supported?(const_name) raise NoMethodError, "Fast.parser_version_supported?(#{const_name.inspect}) was removed; Fast now parses Ruby with Prism" end |
.prism_ast(content, buffer_name: '(string)') ⇒ Object
70 71 72 73 74 75 76 77 78 |
# File 'lib/fast.rb', line 70 def prism_ast(content, buffer_name: '(string)') require_relative 'fast/prism_adapter' result = Fast::PrismAdapter.parse(content, buffer_name: buffer_name) return result if result prism_errors = Prism.parse(content).errors = prism_errors.map(&:message).uniq.join("\n") raise SyntaxError, end |
.render_markdown_for_terminal(line) ⇒ Object
50 51 52 53 54 55 |
# File 'lib/fast/shortcut.rb', line 50 def render_markdown_for_terminal(line) require 'tty-markdown' TTY::Markdown.parse(line) rescue LoadError line end |
.replace(pattern, ast, source = nil, &replacement) ⇒ String
Replaces content based on a pattern.
20 21 22 23 24 |
# File 'lib/fast/rewriter.rb', line 20 def replace(pattern, ast, source = nil, &replacement) rewritten = rewriter_for(pattern, ast, source, &replacement).rewrite! Fast.validate_ruby!(rewritten, buffer_name: ast.buffer_name) if rewritten rewritten end |
.replace_file(pattern, file, &replacement) ⇒ Object
Replaces the source of an ast_from_file with and the same source if the pattern does not match.
38 39 40 41 |
# File 'lib/fast/rewriter.rb', line 38 def replace_file(pattern, file, &replacement) ast = ast_from_file(file) replace(pattern, ast, IO.read(file), &replacement) end |
.replace_sql(pattern, ast, &replacement) ⇒ Object
Fast.replace_sql('ival', Fast.parse_sql('select 1'), &->(node){ replace(node.location.expression, '2') }) # => "select 2"
26 27 28 |
# File 'lib/fast/sql.rb', line 26 def replace_sql(pattern, ast, &replacement) SQL.replace(pattern, ast, &replacement) end |
.replace_sql_file(pattern, file, &replacement) ⇒ Object
Returns string with the sql content updated in case the pattern matches.
31 32 33 |
# File 'lib/fast/sql.rb', line 31 def replace_sql_file(pattern, file, &replacement) SQL.replace_file(pattern, file, &replacement) end |
.report(result, show_link: false, show_permalink: false, show_sexp: false, file: nil, headless: false, bodyless: false, colorize: true, level: nil) ⇒ Object
Combines highlight with files printing file name in the head with the source line.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/fast/cli.rb', line 85 def report(result, show_link: false, show_permalink: false, show_sexp: false, file: nil, headless: false, bodyless: false, colorize: true, level: nil) # rubocop:disable Metrics/ParameterLists if file if result.is_a?(Symbol) && !result.respond_to?(:loc) result.extend(SymbolExtension) end line = result.loc.expression.line if Fast.ast_node?(result) && result.respond_to?(:loc) if show_link puts(result.link) elsif show_permalink puts(result.permalink) elsif !headless puts(highlight("# #{file}:#{line}", colorize: colorize)) end end puts(highlight(result, show_sexp: show_sexp, colorize: colorize, level: level)) unless bodyless end |
.rewrite_file(pattern, file, &replacement) ⇒ Object
Combines #replace_file output overriding the file if the output is different from the original file content.
45 46 47 48 49 |
# File 'lib/fast/rewriter.rb', line 45 def rewrite_file(pattern, file, &replacement) previous_content = IO.read(file) content = replace_file(pattern, file, &replacement) File.open(file, 'w+') { |f| f.puts content } if content != previous_content end |
.rewriter_for(pattern, ast, source = nil, &replacement) ⇒ Fast::Rewriter
27 28 29 30 31 32 33 34 |
# File 'lib/fast/rewriter.rb', line 27 def rewriter_for(pattern, ast, source = nil, &replacement) rewriter = Rewriter.new rewriter.source = source rewriter.ast = ast rewriter.search = pattern rewriter.replacement = replacement rewriter end |
.ruby_files_from(*files) ⇒ Array<String>
When the argument is a folder, it recursively fetches all .rb files from it.
337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/fast.rb', line 337 def ruby_files_from(*files) dir_filter = File.method(:directory?) directories = files.select(&dir_filter) if directories.any? files -= directories files |= directories.flat_map { |dir| Dir["#{dir}/**/*.rb"] } files.uniq! end files.reject(&dir_filter) end |
.scan(locations, command_name: '.scan', level: nil) ⇒ Object
118 119 120 121 |
# File 'lib/fast.rb', line 118 def scan(locations, command_name: '.scan', level: nil) require_relative 'fast/scan' Scan.new(locations, command_name: command_name, level: level) end |
.search(pattern, node, *args) { ... } ⇒ Object
Search recursively into a node and its children. If the node matches with the pattern it returns the node, otherwise it recursively collect possible children nodes
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/fast.rb', line 267 def search(pattern, node, *args) return [] if node.nil? if (match = match?(pattern, node, *args)) yield node, match if block_given? match != true ? [node, match] : [node] else case node when Array node.flat_map { |child| search(pattern, child, *args) } else node.each_child_node .flat_map { |child| search(pattern, child, *args) } .compact.flatten end end end |
.search_all(pattern, locations = ['.'], parallel: true, on_result: nil) ⇒ Hash<String,Array<Fast::Node>>
Search with pattern on a directory or multiple files
191 192 193 194 |
# File 'lib/fast.rb', line 191 def search_all(pattern, locations = ['.'], parallel: true, on_result: nil) group_results(build_grouped_search(:search_file, pattern, on_result), locations, parallel: parallel) end |
.search_file(pattern, file) ⇒ Array<Fast::Node>
Search with pattern directly on file
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/fast.rb', line 175 def search_file(pattern, file) node = ast_from_file(file) return [] unless node case node when Array node.map { |n| search(pattern, n) }.flatten.compact else search pattern, node end end |
.shortcut(identifier, *args, &block) ⇒ Object
Store predefined searches with default paths through shortcuts. define your Fastfile in you root folder or
21 22 23 |
# File 'lib/fast/shortcut.rb', line 21 def shortcut(identifier, *args, &block) shortcuts[identifier] = Shortcut.new(*args, &block) end |
.shortcuts ⇒ Hash<String,Shortcut>
Stores shortcuts in a simple hash where the key is the identifier and the value is the object itself.
28 29 30 |
# File 'lib/fast/shortcut.rb', line 28 def shortcuts @shortcuts ||= {} end |
.sql_rewriter_for(pattern, ast, &replacement) ⇒ Fast::SQLRewriter
Returns which can be used to rewrite the SQL.
18 19 20 |
# File 'lib/fast/sql.rb', line 18 def sql_rewriter_for(pattern, ast, &replacement) SQL.rewriter_for(pattern, ast, &replacement) end |
.summary(code_or_ast, file: nil, command_name: '.summary', level: nil) ⇒ Object
113 114 115 116 |
# File 'lib/fast.rb', line 113 def summary(code_or_ast, file: nil, command_name: '.summary', level: nil) require_relative 'fast/summary' Summary.new(code_or_ast, file: file, command_name: command_name, level: level) end |
.validate_ruby!(content, buffer_name: '(string)') ⇒ Object
147 148 149 150 |
# File 'lib/fast.rb', line 147 def validate_ruby!(content, buffer_name: '(string)') prism_ast(content, buffer_name: buffer_name) true end |
.wrap_source_range(node) ⇒ Object
Fixes initial spaces to print the line since the beginning and fixes end of the expression including heredoc strings.
50 51 52 53 54 55 56 57 |
# File 'lib/fast/cli.rb', line 50 def wrap_source_range(node) expression = node.loc.expression Fast::Source.range( expression.source_buffer, first_position_from_expression(node), last_position_from_expression(node) || expression.end_pos ) end |