Class: Fast::ExperimentFile
- Inherits:
-
Object
- Object
- Fast::ExperimentFile
- Defined in:
- lib/fast/experiment.rb
Overview
it can easily spend days handling multiple one to one combinations, because of that, after the first round of replacements the algorithm goes replacing all winner solutions in the same shot. If it fails, it goes combining one to one.
Combines an Experiment with a specific file. It coordinates and regulate multiple replacements in the same file. Everytime it #run a file, it uses #partial_replace and generate a new file with the new content. It executes the Fast::Experiment#policy block yielding the new file. Depending on the policy result, it adds the occurrence to #fail_experiments or #ok_experiments. When all possible occurrences are replaced in isolated experiments, it ##build_combinations with the winner experiments going to a next round of experiments with multiple partial replacements until find all possible combinations.
Instance Attribute Summary collapse
-
#experiment ⇒ Object
readonly
Returns the value of attribute experiment.
-
#fail_experiments ⇒ Object
readonly
Returns the value of attribute fail_experiments.
-
#ok_experiments ⇒ Object
readonly
Returns the value of attribute ok_experiments.
Instance Method Summary collapse
-
#build_combinations ⇒ Object
Increase the
@roundby 1 to Fast::ExperimentCombinations#generate_combinations. - #cleanup_generated_files! ⇒ Object
- #done! ⇒ Object
-
#experimental_filename(combination) ⇒ String
With a derived name with the combination number.
-
#failed_with(combination) ⇒ void
Track failed experiments to avoid run them again.
-
#initialize(file, experiment) ⇒ ExperimentFile
constructor
A new instance of ExperimentFile.
-
#ok_with(combination) ⇒ Object
Keep track of ok experiments depending on the current combination.
-
#partial_replace(*indices) ⇒ void
rubocop:disable Metrics/MethodLength.
- #run ⇒ Object
-
#run_partial_replacement_with(combination) ⇒ Object
Writes a new file with partial replacements based on the current combination.
- #search ⇒ String
- #search_cases ⇒ Array<Fast::Node>
-
#write_experiment_file(combination, new_content) ⇒ Object
Write new file name depending on the combination.
Constructor Details
#initialize(file, experiment) ⇒ ExperimentFile
Returns a new instance of ExperimentFile.
252 253 254 255 256 257 258 259 |
# File 'lib/fast/experiment.rb', line 252 def initialize(file, experiment) @file = file @ast = Fast.ast_from_file(file) if file @experiment = experiment @ok_experiments = [] @fail_experiments = [] @round = 0 end |
Instance Attribute Details
#experiment ⇒ Object (readonly)
Returns the value of attribute experiment.
250 251 252 |
# File 'lib/fast/experiment.rb', line 250 def experiment @experiment end |
#fail_experiments ⇒ Object (readonly)
Returns the value of attribute fail_experiments.
250 251 252 |
# File 'lib/fast/experiment.rb', line 250 def fail_experiments @fail_experiments end |
#ok_experiments ⇒ Object (readonly)
Returns the value of attribute ok_experiments.
250 251 252 |
# File 'lib/fast/experiment.rb', line 250 def ok_experiments @ok_experiments end |
Instance Method Details
#build_combinations ⇒ Object
Increase the @round by 1 to Fast::ExperimentCombinations#generate_combinations.
353 354 355 356 357 358 359 360 361 |
# File 'lib/fast/experiment.rb', line 353 def build_combinations @round += 1 ExperimentCombinations.new( round: @round, occurrences_count: search_cases.size, ok_experiments: @ok_experiments, fail_experiments: @fail_experiments ).generate_combinations end |
#cleanup_generated_files! ⇒ Object
331 332 333 334 335 |
# File 'lib/fast/experiment.rb', line 331 def cleanup_generated_files! Dir.glob(File.join(File.dirname(@file), "experiment_*_#{File.basename(@file)}")).each do |generated_file| File.delete(generated_file) if File.exist?(generated_file) end end |
#done! ⇒ Object
337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/fast/experiment.rb', line 337 def done! count_executed_combinations = @fail_experiments.size + @ok_experiments.size puts "Done with #{@file} after #{count_executed_combinations} combinations" unless perfect_combination = @ok_experiments.last # rubocop:disable Lint/AssignmentInCondition cleanup_generated_files! if experiment.autoclean? return end puts 'The following changes were applied to the file:' `diff #{experimental_filename(perfect_combination)} #{@file}` puts "mv #{experimental_filename(perfect_combination)} #{@file}" `mv #{experimental_filename(perfect_combination)} #{@file}` cleanup_generated_files! if experiment.autoclean? end |
#experimental_filename(combination) ⇒ String
Returns with a derived name with the combination number.
267 268 269 270 271 272 |
# File 'lib/fast/experiment.rb', line 267 def experimental_filename(combination) parts = @file.split('/') dir = parts[0..-2] filename = "experiment_#{[*combination].join('_')}_#{parts[-1]}" File.join(*dir, filename) end |
#failed_with(combination) ⇒ void
This method returns an undefined value.
Track failed experiments to avoid run them again.
289 290 291 |
# File 'lib/fast/experiment.rb', line 289 def failed_with(combination) @fail_experiments << combination end |
#ok_with(combination) ⇒ Object
Keep track of ok experiments depending on the current combination. It keep the combinations unique removing single replacements after the first round.
278 279 280 281 282 283 284 285 |
# File 'lib/fast/experiment.rb', line 278 def ok_with(combination) @ok_experiments << combination return unless combination.is_a?(Array) combination.each do |element| @ok_experiments.delete(element) end end |
#partial_replace(*indices) ⇒ void
This method returns an undefined value.
rubocop:disable Metrics/MethodLength
Execute partial replacements generating new file with the content replaced.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# File 'lib/fast/experiment.rb', line 303 def partial_replace(*indices) replacement = experiment.replacement new_content = Fast.replace_file experiment.expression, @file do |node, *captures| if indices.nil? || indices.empty? || indices.include?(match_index) if replacement.parameters.length == 1 instance_exec node, &replacement else instance_exec node, *captures, &replacement end end end return unless new_content write_experiment_file(indices, new_content) new_content end |
#run ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/fast/experiment.rb', line 363 def run cleanup_generated_files! if experiment.autoclean? while (combinations = build_combinations).any? if combinations.size > 1000 puts "Ignoring #{@file} because it has #{combinations.size} possible combinations" break end puts "#{@file} - Round #{@round} - Possible combinations: #{combinations.inspect}" while combination = combinations.shift # rubocop:disable Lint/AssignmentInCondition run_partial_replacement_with(combination) end end done! end |
#run_partial_replacement_with(combination) ⇒ Object
Writes a new file with partial replacements based on the current combination. Raise error if no changes was made with the given combination indices.
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/fast/experiment.rb', line 381 def run_partial_replacement_with(combination) content = partial_replace(*combination) experimental_file = experimental_filename(combination) File.open(experimental_file, 'w+') { |f| f.puts content } raise 'No changes were made to the file.' if FileUtils.compare_file(@file, experimental_file) result = experiment.ok_if.call(experimental_file) if result ok_with(combination) puts "✅ #{experimental_file} - Combination: #{combination}" else failed_with(combination) puts "🔴 #{experimental_file} - Combination: #{combination}" File.delete(experimental_file) if experiment.autoclean? && File.exist?(experimental_file) end end |
#search ⇒ String
Returns from Fast::Experiment#expression.
262 263 264 |
# File 'lib/fast/experiment.rb', line 262 def search experiment.expression end |
#search_cases ⇒ Array<Fast::Node>
294 295 296 |
# File 'lib/fast/experiment.rb', line 294 def search_cases Fast.search(experiment.expression, @ast) || [] end |
#write_experiment_file(combination, new_content) ⇒ Object
Write new file name depending on the combination
325 326 327 328 329 |
# File 'lib/fast/experiment.rb', line 325 def write_experiment_file(combination, new_content) filename = experimental_filename(combination) File.open(filename, 'w+') { |f| f.puts new_content } filename end |