class Fast::Experiment
Fast
experiment allow the user to combine single replacements and make multiple changes at the same time. Defining a policy is possible to check if the experiment was successfull and keep changing the file using a specific search.
The experiment have a combination algorithm that recursively check what combinations work with what combinations. It can delay years and because of that it tries a first replacement targeting all the cases in a single file.
You can define experiments and build experimental files to improve some code in an automated way. Let's create a hook to check if a `before` or `after` block is useless in a specific spec:
@example Remove useless before or after block RSpec hooks
# Let's say you want to experimentally remove some before or after block # in specs to check if some of them are weak or useless: # RSpec.describe "something" do # before { @a = 1 } # before { @b = 1 } # it { expect(@b).to be_eq(1) } # end # # The variable `@a` is not useful for the test, if I remove the block it # should continue passing. # # RSpec.describe "something" do # before { @b = 1 } # it { expect(@b).to be_eq(1) } # end # # But removing the next `before` block will fail: # RSpec.describe "something" do # before { @a = 1 } # it { expect(@b).to be_eq(1) } # end # And the experiments will have a policy to check if `rspec` run without # fail and only execute successfull replacements. Fast.experiment("RSpec/RemoveUselessBeforeAfterHook") do lookup 'spec' # all files in the spec folder search "(block (send nil {before after}))" edit {|node| remove(node.loc.expression) } policy {|new_file| system("rspec --fail-fast #{new_file}") } end
@example Replace FactoryBot create with build_stubbed method
# Let's say you want to try to automate some replacement of # `FactoryBot.create` to use `FactoryBot.build_stubbed`. # For specs let's consider the example we want to refactor: # let(:person) { create(:person, :with_email) } # And the intent is replace to use `build_stubbed` instead of `create`: # let(:person) { build_stubbed(:person, :with_email) } Fast.experiment('RSpec/ReplaceCreateWithBuildStubbed') do lookup 'spec' search '(block (send nil let (sym _)) (args) $(send nil create))' edit { |_, (create)| replace(create.loc.selector, 'build_stubbed') } policy { |new_file| system("rspec --format progress --fail-fast #{new_file}") } end
Attributes
Public Class Methods
# File lib/fast/experiment.rb, line 95 def initialize(name, &block) @name = name puts "\nStarting experiment: #{name}" instance_exec(&block) end
Public Instance Methods
@param block yields the node that matches and return the block in the instance context of a [Fast::Rewriter]
# File lib/fast/experiment.rb, line 114 def edit(&block) @replacement = block end
@return [Array<String>] with files from {#lookup} expression.
# File lib/fast/experiment.rb, line 131 def files @files ||= Fast.ruby_files_from(@files_or_folders) end
@param [String] files_or_folders
that will be combined to find the {#files}
# File lib/fast/experiment.rb, line 119 def lookup(files_or_folders) @files_or_folders = files_or_folders end
It calls the block after the replacement and use the result to drive the {Fast::ExperimentFile#ok_experiments} and {Fast::ExperimentFile#fail_experiments}. @param block yields a temporary file with the content replaced in the current round.
# File lib/fast/experiment.rb, line 126 def policy(&block) @ok_if = block end
Iterates over all {#files} to {#run_with} them. @return [void]
# File lib/fast/experiment.rb, line 137 def run files.map(&method(:run_with)) end
It combines current experiment with {ExperimentFile#run} @param [String] file to be analyzed by the experiment
# File lib/fast/experiment.rb, line 103 def run_with(file) ExperimentFile.new(file, self).run end
@param [String] expression with the node pattern to target nodes
# File lib/fast/experiment.rb, line 108 def search(expression) @expression = expression end