ffast 0.0.4 → 0.0.5
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/README.md +14 -3
- data/bin/fast-experiment +1 -3
- data/docs/pry-integration.md +27 -0
- data/examples/build_stubbed_and_let_it_be_experiment.rb +51 -0
- data/lib/fast.rb +82 -32
- data/lib/fast/version.rb +1 -1
- data/mkdocs.yml +1 -0
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: a8e1cdbb944bd8939745c07350aaa66e4ff3e9e3313802ca23aaa7fbd33b742d
         | 
| 4 | 
            +
              data.tar.gz: fcd59e90fedc893492aa791e129d18a8b2e2e72e72b8ca61be6be8fd3e4ad6b7
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f33ebcf4e4275e783d75a761b78fc54a70f789f89ea735011a5a5628c7d2f8630271e29436aa1d3a15418f4186fc42764fad3ca5d72ef69bf2ccc860424299a3
         | 
| 7 | 
            +
              data.tar.gz: ab1c3275a3415aba183ac6e558284d4f30bb5e462454758808df430059f077eb141f18a8ec725ad7738e1e9e3fd350688d674f6691a4d6165e17f039db34c653
         | 
    
        data/README.md
    CHANGED
    
    | @@ -319,18 +319,29 @@ You can use `search_file` and pass the path for search for expressions inside | |
| 319 319 | 
             
            files.
         | 
| 320 320 |  | 
| 321 321 | 
             
            ```ruby
         | 
| 322 | 
            -
            Fast.search_file('file.rb' | 
| 322 | 
            +
            Fast.search_file(expression, 'file.rb')
         | 
| 323 323 | 
             
            ```
         | 
| 324 324 |  | 
| 325 325 | 
             
            It's simple combination of `Fast.ast_from_file` with `Fast.search`.
         | 
| 326 326 |  | 
| 327 | 
            +
             | 
| 328 | 
            +
            ## Fast.capture_file
         | 
| 329 | 
            +
             | 
| 330 | 
            +
            You can also use `Fast.capture_file` that is similar but return only the
         | 
| 331 | 
            +
            captures:
         | 
| 332 | 
            +
             | 
| 333 | 
            +
            ```ruby
         | 
| 334 | 
            +
             Fast.capture_file('(class (const nil $_))', 'lib/fast.rb')
         | 
| 335 | 
            +
             # => [:Rewriter, :ExpressionParser, :Find, :FindString, ...]
         | 
| 336 | 
            +
            ```
         | 
| 337 | 
            +
             | 
| 327 338 | 
             
            ## Fast.ruby_files_from(arguments)
         | 
| 328 339 |  | 
| 329 340 | 
             
            You'll be probably looking for multiple ruby files, then this method fetches
         | 
| 330 341 | 
             
            all internal `.rb` files 
         | 
| 331 342 |  | 
| 332 343 | 
             
            ```ruby
         | 
| 333 | 
            -
            Fast.ruby_files_from( | 
| 344 | 
            +
            Fast.ruby_files_from('lib') # => ["lib/fast.rb"]
         | 
| 334 345 | 
             
            ```
         | 
| 335 346 |  | 
| 336 347 | 
             
            ## `fast` in the command line
         | 
| @@ -423,7 +434,7 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run | |
| 423 434 |  | 
| 424 435 | 
             
            On the console we have a few functions like `s` and `code` to make it easy ;)
         | 
| 425 436 |  | 
| 426 | 
            -
            $ bin/console
         | 
| 437 | 
            +
                $ bin/console
         | 
| 427 438 |  | 
| 428 439 | 
             
            ```ruby
         | 
| 429 440 | 
             
            code("a = 1") # => s(:lvasgn, s(:int, 1))
         | 
    
        data/bin/fast-experiment
    CHANGED
    
    | @@ -13,9 +13,7 @@ experiment_files.each(&method(:require)) | |
| 13 13 | 
             
            experiments = []
         | 
| 14 14 |  | 
| 15 15 | 
             
            if arguments.any?
         | 
| 16 | 
            -
              while arguments.any? && !(File.exist?(arguments.first) || Dir.exist?(arguments.first))
         | 
| 17 | 
            -
                experiments << arguments.shift
         | 
| 18 | 
            -
              end
         | 
| 16 | 
            +
              experiments << arguments.shift while arguments.any? && !(File.exist?(arguments.first) || Dir.exist?(arguments.first))
         | 
| 19 17 | 
             
              experiments.map! { |name| Fast.experiments[name] }
         | 
| 20 18 | 
             
            else
         | 
| 21 19 | 
             
              experiments = Fast.experiments.values
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            You can create a custom command in pry to reuse fast in any session.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Start simply dropping it on your `.pryrc`:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ```ruby
         | 
| 7 | 
            +
            Pry::Commands.block_command "fast", "Fast search" do |expression, file|
         | 
| 8 | 
            +
              require "fast"
         | 
| 9 | 
            +
            	files = Fast.ruby_files_from(file || '.')
         | 
| 10 | 
            +
              files.each do |f|
         | 
| 11 | 
            +
                 results = Fast.search_file(expression, f)
         | 
| 12 | 
            +
            		 next if results.nil? || results.empty?
         | 
| 13 | 
            +
                 output.puts Fast.highlight("# #{f}")
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                 results.each do |result|
         | 
| 16 | 
            +
                   output.puts Fast.highlight(result)
         | 
| 17 | 
            +
                 end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| 20 | 
            +
            ```
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            And use it in the console:
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            `pry
         | 
| 25 | 
            +
            fast '(def match?)' lib/fast.rb`
         | 
| 26 | 
            +
            ```
         | 
| 27 | 
            +
             | 
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'fast'
         | 
| 4 | 
            +
            require 'open3'
         | 
| 5 | 
            +
            require 'ostruct'
         | 
| 6 | 
            +
            require 'fileutils'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # Usage instructions:
         | 
| 9 | 
            +
            # 1. Add the following to your project's Gemfile: gem 'ffast' (yes, two "f"s)
         | 
| 10 | 
            +
            # 2. Copy this file to your Rails project root directory
         | 
| 11 | 
            +
            # 3. Run: bundle exec ruby build_stubbed_and_let_it_be_experiment.rb
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            # List of spec files you want to experiment with. One per line.
         | 
| 14 | 
            +
            FILE_NAMES = %w[
         | 
| 15 | 
            +
              # spec/model/foo_spec.rb
         | 
| 16 | 
            +
              # spec/model/bar_spec.rb
         | 
| 17 | 
            +
            ]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            def execute_rspec(file_name)
         | 
| 20 | 
            +
              rspec_command = "bin/spring rspec --fail-fast --format progress #{file_name}"
         | 
| 21 | 
            +
              stdout_str, stderr_str, status = Open3.capture3(rspec_command)
         | 
| 22 | 
            +
              execution_time = /Finished in (.*?) seconds/.match(stdout_str)[1]
         | 
| 23 | 
            +
              print stderr_str.gsub(/Running via Spring preloader.*?$/, '').chomp unless status.success?
         | 
| 24 | 
            +
              OpenStruct.new(success: status.success?, execution_time: execution_time)
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            def delete_temp_files(original_file_name)
         | 
| 28 | 
            +
              file_path = File.dirname(original_file_name)
         | 
| 29 | 
            +
              file_name = File.basename(original_file_name)
         | 
| 30 | 
            +
              Dir.glob("#{file_path}/experiment*#{file_name}").each { |file| File.delete(file)}
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            FILE_NAMES.each do |original_file_name|
         | 
| 34 | 
            +
              Fast.experiment('RSpec/ReplaceCreateWithBuildStubbed') do
         | 
| 35 | 
            +
                lookup original_file_name
         | 
| 36 | 
            +
                search '(block (send nil let (sym _)) (args) $(send nil create))'
         | 
| 37 | 
            +
                edit { |_, (create)| replace(create.loc.selector, 'build_stubbed') }
         | 
| 38 | 
            +
                policy { |experiment_file_name| execute_rspec(experiment_file_name) }
         | 
| 39 | 
            +
              end.run
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              Fast.experiment('RSpec/LetItBe') do
         | 
| 42 | 
            +
                lookup original_file_name
         | 
| 43 | 
            +
                search '(block $(send nil let! (sym _)) (args) (send nil create))'
         | 
| 44 | 
            +
                edit { |_, (let)| replace(let.loc.selector, 'let_it_be') }
         | 
| 45 | 
            +
                policy { |experiment_file_name| execute_rspec(experiment_file_name) }
         | 
| 46 | 
            +
              end.run
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              delete_temp_files(original_file_name)
         | 
| 49 | 
            +
            end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
             | 
    
        data/lib/fast.rb
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'fileutils'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            # suppress output to avoid parser gem warnings'
         | 
| 4 6 | 
             
            def suppress_output
         | 
| 5 7 | 
             
              original_stdout = $stdout.clone
         | 
| @@ -21,7 +23,7 @@ end | |
| 21 23 | 
             
            module Fast
         | 
| 22 24 | 
             
              LITERAL = {
         | 
| 23 25 | 
             
                '...' => ->(node) { node&.children&.any? },
         | 
| 24 | 
            -
                '_' | 
| 26 | 
            +
                '_' => ->(node) { !node.nil? },
         | 
| 25 27 | 
             
                'nil' => nil
         | 
| 26 28 | 
             
              }.freeze
         | 
| 27 29 |  | 
| @@ -55,7 +57,7 @@ module Fast | |
| 55 57 | 
             
                \\\d                  # find using captured expression
         | 
| 56 58 | 
             
                |
         | 
| 57 59 | 
             
                %\d                   # find using binded argument
         | 
| 58 | 
            -
              /x
         | 
| 60 | 
            +
              /x.freeze
         | 
| 59 61 |  | 
| 60 62 | 
             
              class << self
         | 
| 61 63 | 
             
                def match?(ast, search, *args)
         | 
| @@ -85,6 +87,11 @@ module Fast | |
| 85 87 | 
             
                  search node, pattern
         | 
| 86 88 | 
             
                end
         | 
| 87 89 |  | 
| 90 | 
            +
                def capture_file(pattern, file)
         | 
| 91 | 
            +
                  node = ast_from_file(file)
         | 
| 92 | 
            +
                  capture node, pattern
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 88 95 | 
             
                def search(node, pattern)
         | 
| 89 96 | 
             
                  if (match = Fast.match?(node, pattern))
         | 
| 90 97 | 
             
                    yield node, match if block_given?
         | 
| @@ -93,7 +100,7 @@ module Fast | |
| 93 100 | 
             
                    node.children
         | 
| 94 101 | 
             
                      .grep(Parser::AST::Node)
         | 
| 95 102 | 
             
                      .flat_map { |e| search(e, pattern) }
         | 
| 96 | 
            -
                      .compact. | 
| 103 | 
            +
                      .compact.flatten
         | 
| 97 104 | 
             
                  end
         | 
| 98 105 | 
             
                end
         | 
| 99 106 |  | 
| @@ -577,6 +584,7 @@ module Fast | |
| 577 584 |  | 
| 578 585 | 
             
                def initialize(name, &block)
         | 
| 579 586 | 
             
                  @name = name
         | 
| 587 | 
            +
                  puts "\nStarting experiment: #{name}"
         | 
| 580 588 | 
             
                  instance_exec(&block)
         | 
| 581 589 | 
             
                end
         | 
| 582 590 |  | 
| @@ -609,17 +617,63 @@ module Fast | |
| 609 617 | 
             
                end
         | 
| 610 618 | 
             
              end
         | 
| 611 619 |  | 
| 620 | 
            +
              # Suggest possible combinations of occurrences to replace.
         | 
| 621 | 
            +
              class ExperimentCombinations
         | 
| 622 | 
            +
                attr_reader :combinations
         | 
| 623 | 
            +
             | 
| 624 | 
            +
                def initialize(round:, occurrences_count:, ok_experiments:, fail_experiments:)
         | 
| 625 | 
            +
                  @round = round
         | 
| 626 | 
            +
                  @ok_experiments = ok_experiments
         | 
| 627 | 
            +
                  @fail_experiments = fail_experiments
         | 
| 628 | 
            +
                  @occurrences_count = occurrences_count
         | 
| 629 | 
            +
                end
         | 
| 630 | 
            +
             | 
| 631 | 
            +
                def generate_combinations
         | 
| 632 | 
            +
                  case @round
         | 
| 633 | 
            +
                  when 1
         | 
| 634 | 
            +
                    individual_replacements
         | 
| 635 | 
            +
                  when 2
         | 
| 636 | 
            +
                    all_ok_replacements_combined
         | 
| 637 | 
            +
                  else
         | 
| 638 | 
            +
                    ok_replacements_pair_combinations
         | 
| 639 | 
            +
                  end
         | 
| 640 | 
            +
                end
         | 
| 641 | 
            +
             | 
| 642 | 
            +
                # Replace a single occurrence at each iteration and identify which
         | 
| 643 | 
            +
                # individual replacements work.
         | 
| 644 | 
            +
                def individual_replacements
         | 
| 645 | 
            +
                  (1..@occurrences_count).to_a
         | 
| 646 | 
            +
                end
         | 
| 647 | 
            +
             | 
| 648 | 
            +
                # After identifying all individual replacements that work, try combining all
         | 
| 649 | 
            +
                # of them.
         | 
| 650 | 
            +
                def all_ok_replacements_combined
         | 
| 651 | 
            +
                  [@ok_experiments.uniq.sort]
         | 
| 652 | 
            +
                end
         | 
| 653 | 
            +
             | 
| 654 | 
            +
                # Combining all successful individual replacements has failed. Lets divide
         | 
| 655 | 
            +
                # and conquer.
         | 
| 656 | 
            +
                def ok_replacements_pair_combinations
         | 
| 657 | 
            +
                  @ok_experiments
         | 
| 658 | 
            +
                    .combination(2)
         | 
| 659 | 
            +
                    .map { |e| e.flatten.uniq.sort }
         | 
| 660 | 
            +
                    .uniq - @fail_experiments - @ok_experiments
         | 
| 661 | 
            +
                end
         | 
| 662 | 
            +
              end
         | 
| 663 | 
            +
             | 
| 612 664 | 
             
              # Encapsulate the join of an Experiment with an specific file.
         | 
| 613 665 | 
             
              # This is important to coordinate and regulate multiple experiments in the same file.
         | 
| 614 666 | 
             
              # It can track successfull experiments and failures and suggest new combinations to keep replacing the file.
         | 
| 615 667 | 
             
              class ExperimentFile
         | 
| 616 668 | 
             
                attr_reader :ok_experiments, :fail_experiments, :experiment
         | 
| 669 | 
            +
             | 
| 617 670 | 
             
                def initialize(file, experiment)
         | 
| 618 671 | 
             
                  @file = file
         | 
| 619 672 | 
             
                  @ast = Fast.ast_from_file(file) if file
         | 
| 620 673 | 
             
                  @experiment = experiment
         | 
| 621 674 | 
             
                  @ok_experiments = []
         | 
| 622 675 | 
             
                  @fail_experiments = []
         | 
| 676 | 
            +
                  @round = 0
         | 
| 623 677 | 
             
                end
         | 
| 624 678 |  | 
| 625 679 | 
             
                def search
         | 
| @@ -677,33 +731,34 @@ module Fast | |
| 677 731 | 
             
                  filename
         | 
| 678 732 | 
             
                end
         | 
| 679 733 |  | 
| 680 | 
            -
                def suggest_combinations
         | 
| 681 | 
            -
                  if @ok_experiments.empty? && @fail_experiments.empty?
         | 
| 682 | 
            -
                    (1..search_cases.size).to_a
         | 
| 683 | 
            -
                  else
         | 
| 684 | 
            -
                    @ok_experiments
         | 
| 685 | 
            -
                      .combination(2)
         | 
| 686 | 
            -
                      .map { |e| e.flatten.uniq.sort }
         | 
| 687 | 
            -
                      .uniq - @fail_experiments - @ok_experiments
         | 
| 688 | 
            -
                  end
         | 
| 689 | 
            -
                end
         | 
| 690 | 
            -
             | 
| 691 734 | 
             
                def done!
         | 
| 692 735 | 
             
                  count_executed_combinations = @fail_experiments.size + @ok_experiments.size
         | 
| 693 | 
            -
                  puts "Done with #{@file} after #{count_executed_combinations}"
         | 
| 736 | 
            +
                  puts "Done with #{@file} after #{count_executed_combinations} combinations"
         | 
| 694 737 | 
             
                  return unless perfect_combination = @ok_experiments.last # rubocop:disable Lint/AssignmentInCondition
         | 
| 695 738 |  | 
| 739 | 
            +
                  puts 'The following changes were applied to the file:'
         | 
| 740 | 
            +
                  `diff #{experimental_filename(perfect_combination)} #{@file}`
         | 
| 696 741 | 
             
                  puts "mv #{experimental_filename(perfect_combination)} #{@file}"
         | 
| 697 742 | 
             
                  `mv #{experimental_filename(perfect_combination)} #{@file}`
         | 
| 698 743 | 
             
                end
         | 
| 699 744 |  | 
| 745 | 
            +
                def build_combinations
         | 
| 746 | 
            +
                  @round += 1
         | 
| 747 | 
            +
                  ExperimentCombinations.new(
         | 
| 748 | 
            +
                    round: @round,
         | 
| 749 | 
            +
                    occurrences_count: search_cases.size,
         | 
| 750 | 
            +
                    ok_experiments: @ok_experiments,
         | 
| 751 | 
            +
                    fail_experiments: @fail_experiments
         | 
| 752 | 
            +
                  ).generate_combinations
         | 
| 753 | 
            +
                end
         | 
| 754 | 
            +
             | 
| 700 755 | 
             
                def run
         | 
| 701 | 
            -
                  while (combinations =  | 
| 702 | 
            -
                    if combinations.size >  | 
| 703 | 
            -
                      puts "Ignoring #{@file} because it  | 
| 756 | 
            +
                  while (combinations = build_combinations).any?
         | 
| 757 | 
            +
                    if combinations.size > 1000
         | 
| 758 | 
            +
                      puts "Ignoring #{@file} because it has #{combinations.size} possible combinations"
         | 
| 704 759 | 
             
                      break
         | 
| 705 760 | 
             
                    end
         | 
| 706 | 
            -
                    puts "#{@file} - Possible combinations: #{combinations.inspect}"
         | 
| 761 | 
            +
                    puts "#{@file} - Round #{@round} - Possible combinations: #{combinations.inspect}"
         | 
| 707 762 | 
             
                    while combination = combinations.shift # rubocop:disable Lint/AssignmentInCondition
         | 
| 708 763 | 
             
                      run_partial_replacement_with(combination)
         | 
| 709 764 | 
             
                    end
         | 
| @@ -711,28 +766,23 @@ module Fast | |
| 711 766 | 
             
                  done!
         | 
| 712 767 | 
             
                end
         | 
| 713 768 |  | 
| 714 | 
            -
                # rubocop:disable Metrics/ | 
| 715 | 
            -
                # rubocop:disable Metrics/AbcSize
         | 
| 716 | 
            -
                def run_partial_replacement_with(combination)
         | 
| 717 | 
            -
                  puts "#{@file} applying partial replacement with: #{combination}"
         | 
| 769 | 
            +
                def run_partial_replacement_with(combination) # rubocop:disable Metrics/AbcSize
         | 
| 718 770 | 
             
                  content = partial_replace(*combination)
         | 
| 719 771 | 
             
                  experimental_file = experimental_filename(combination)
         | 
| 720 | 
            -
                  puts `diff #{experimental_file} #{@file}`
         | 
| 721 | 
            -
                  if experimental_file == IO.read(@file)
         | 
| 722 | 
            -
                    raise 'Returned the same file thinking:'
         | 
| 723 | 
            -
                  end
         | 
| 724 772 |  | 
| 725 773 | 
             
                  File.open(experimental_file, 'w+') { |f| f.puts content }
         | 
| 726 774 |  | 
| 727 | 
            -
                  if  | 
| 775 | 
            +
                  raise 'No changes were made to the file.' if FileUtils.compare_file(@file, experimental_file)
         | 
| 776 | 
            +
             | 
| 777 | 
            +
                  result = experiment.ok_if.call(experimental_file)
         | 
| 778 | 
            +
             | 
| 779 | 
            +
                  if result.success
         | 
| 728 780 | 
             
                    ok_with(combination)
         | 
| 729 | 
            -
                    puts "✅ #{combination} #{ | 
| 781 | 
            +
                    puts "✅ #{experimental_file} - Combination: #{combination} - Time: #{result.execution_time}s"
         | 
| 730 782 | 
             
                  else
         | 
| 731 783 | 
             
                    failed_with(combination)
         | 
| 732 | 
            -
                    puts "🔴 #{ | 
| 784 | 
            +
                    puts "🔴 #{experimental_file} - Combination: #{combination}"
         | 
| 733 785 | 
             
                  end
         | 
| 734 786 | 
             
                end
         | 
| 735 | 
            -
                # rubocop:enable Metrics/MethodLength
         | 
| 736 | 
            -
                # rubocop:enable Metrics/AbcSize
         | 
| 737 787 | 
             
              end
         | 
| 738 788 | 
             
            end
         | 
    
        data/lib/fast/version.rb
    CHANGED
    
    
    
        data/mkdocs.yml
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ffast
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.5
         | 
| 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:  | 
| 11 | 
            +
            date: 2019-01-05 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: coderay
         | 
| @@ -205,8 +205,10 @@ files: | |
| 205 205 | 
             
            - docs/command_line.md
         | 
| 206 206 | 
             
            - docs/experiments.md
         | 
| 207 207 | 
             
            - docs/index.md
         | 
| 208 | 
            +
            - docs/pry-integration.md
         | 
| 208 209 | 
             
            - docs/similarity_tutorial.md
         | 
| 209 210 | 
             
            - docs/syntax.md
         | 
| 211 | 
            +
            - examples/build_stubbed_and_let_it_be_experiment.rb
         | 
| 210 212 | 
             
            - examples/experimental_replacement.rb
         | 
| 211 213 | 
             
            - examples/find_usage.rb
         | 
| 212 214 | 
             
            - examples/let_it_be_experiment.rb
         |