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: {}
         |