ffast 0.0.7 → 0.0.8
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/bin/fast-experiment +1 -0
- data/lib/fast.rb +3 -233
- data/lib/fast/cli.rb +1 -3
- data/lib/fast/experiment.rb +249 -0
- data/lib/fast/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e62b5be6bf84eabaa2ed601696164c248a6969cc504343f636b1d487bb4fbf2
|
4
|
+
data.tar.gz: a86d3f27396de8cb9aa097c02d1baf19c39ad7090f03b5894e1eddb087150f6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a2a7db73282e36fd8ece6d9d78b55eb52e98227e5be77d77058a82ae97af634e94de7dd5f98640a844739956f5e8c5bf9f2fd91e88971d21da6a762cf69fdbb
|
7
|
+
data.tar.gz: 635bd2bb2a9e26975731fa4096f7980281ef929d2ae13d54f8f4897e8695d6e824deb15ed4cea6e6df9114191e99e210510a5878dc703910b711e1a17c446a68
|
data/bin/fast-experiment
CHANGED
data/lib/fast.rb
CHANGED
@@ -69,7 +69,7 @@ module Fast
|
|
69
69
|
Matcher.new(ast, search, *args).match?
|
70
70
|
end
|
71
71
|
|
72
|
-
def replace(ast, search, replacement)
|
72
|
+
def replace(ast, search, &replacement)
|
73
73
|
buffer = Parser::Source::Buffer.new('replacement')
|
74
74
|
buffer.source = ast.loc.expression.source
|
75
75
|
to_replace = search(ast, search)
|
@@ -82,9 +82,9 @@ module Fast
|
|
82
82
|
rewriter.rewrite(buffer, ast)
|
83
83
|
end
|
84
84
|
|
85
|
-
def replace_file(file, search, replacement)
|
85
|
+
def replace_file(file, search, &replacement)
|
86
86
|
ast = ast_from_file(file)
|
87
|
-
replace(ast, search, replacement)
|
87
|
+
replace(ast, search, &replacement)
|
88
88
|
end
|
89
89
|
|
90
90
|
def search_file(pattern, file)
|
@@ -152,13 +152,6 @@ module Fast
|
|
152
152
|
def expression(string)
|
153
153
|
ExpressionParser.new(string).parse
|
154
154
|
end
|
155
|
-
|
156
|
-
def experiment(name, &block)
|
157
|
-
@experiments ||= {}
|
158
|
-
@experiments[name] = Experiment.new(name, &block)
|
159
|
-
end
|
160
|
-
|
161
|
-
attr_reader :experiments
|
162
155
|
attr_accessor :debugging
|
163
156
|
|
164
157
|
def debug
|
@@ -540,7 +533,6 @@ module Fast
|
|
540
533
|
end
|
541
534
|
end
|
542
535
|
|
543
|
-
# rubocop:disable Metrics/AbcSize
|
544
536
|
def match?(ast = @ast, fast = @fast)
|
545
537
|
head, *tail = fast
|
546
538
|
return false unless head.match?(ast)
|
@@ -555,8 +547,6 @@ module Fast
|
|
555
547
|
end && find_captures
|
556
548
|
end
|
557
549
|
|
558
|
-
# rubocop:enable Metrics/AbcSize
|
559
|
-
|
560
550
|
def prepare_token(token)
|
561
551
|
case token
|
562
552
|
when Fast::FindWithCapture
|
@@ -582,224 +572,4 @@ module Fast
|
|
582
572
|
end
|
583
573
|
end
|
584
574
|
end
|
585
|
-
|
586
|
-
# You can define experiments and build experimental files to improve some code in
|
587
|
-
# an automated way. Let's create a hook to check if a `before` or `after` block
|
588
|
-
# is useless in a specific spec:
|
589
|
-
#
|
590
|
-
# ```ruby
|
591
|
-
# Fast.experiment("RSpec/RemoveUselessBeforeAfterHook") do
|
592
|
-
# lookup 'some_spec.rb'
|
593
|
-
# search "(block (send nil {before after}))"
|
594
|
-
# edit {|node| remove(node.loc.expression) }
|
595
|
-
# policy {|new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
596
|
-
# end
|
597
|
-
# ```
|
598
|
-
class Experiment
|
599
|
-
attr_writer :files
|
600
|
-
attr_reader :name, :replacement, :expression, :files_or_folders, :ok_if
|
601
|
-
|
602
|
-
def initialize(name, &block)
|
603
|
-
@name = name
|
604
|
-
puts "\nStarting experiment: #{name}"
|
605
|
-
instance_exec(&block)
|
606
|
-
end
|
607
|
-
|
608
|
-
def run_with(file)
|
609
|
-
ExperimentFile.new(file, self).run
|
610
|
-
end
|
611
|
-
|
612
|
-
def search(expression)
|
613
|
-
@expression = expression
|
614
|
-
end
|
615
|
-
|
616
|
-
def edit(&block)
|
617
|
-
@replacement = block
|
618
|
-
end
|
619
|
-
|
620
|
-
def lookup(files_or_folders)
|
621
|
-
@files_or_folders = files_or_folders
|
622
|
-
end
|
623
|
-
|
624
|
-
def policy(&block)
|
625
|
-
@ok_if = block
|
626
|
-
end
|
627
|
-
|
628
|
-
def files
|
629
|
-
@files ||= Fast.ruby_files_from(@files_or_folders)
|
630
|
-
end
|
631
|
-
|
632
|
-
def run
|
633
|
-
files.map(&method(:run_with))
|
634
|
-
end
|
635
|
-
end
|
636
|
-
|
637
|
-
# Suggest possible combinations of occurrences to replace.
|
638
|
-
class ExperimentCombinations
|
639
|
-
attr_reader :combinations
|
640
|
-
|
641
|
-
def initialize(round:, occurrences_count:, ok_experiments:, fail_experiments:)
|
642
|
-
@round = round
|
643
|
-
@ok_experiments = ok_experiments
|
644
|
-
@fail_experiments = fail_experiments
|
645
|
-
@occurrences_count = occurrences_count
|
646
|
-
end
|
647
|
-
|
648
|
-
def generate_combinations
|
649
|
-
case @round
|
650
|
-
when 1
|
651
|
-
individual_replacements
|
652
|
-
when 2
|
653
|
-
all_ok_replacements_combined
|
654
|
-
else
|
655
|
-
ok_replacements_pair_combinations
|
656
|
-
end
|
657
|
-
end
|
658
|
-
|
659
|
-
# Replace a single occurrence at each iteration and identify which
|
660
|
-
# individual replacements work.
|
661
|
-
def individual_replacements
|
662
|
-
(1..@occurrences_count).to_a
|
663
|
-
end
|
664
|
-
|
665
|
-
# After identifying all individual replacements that work, try combining all
|
666
|
-
# of them.
|
667
|
-
def all_ok_replacements_combined
|
668
|
-
[@ok_experiments.uniq.sort]
|
669
|
-
end
|
670
|
-
|
671
|
-
# Combining all successful individual replacements has failed. Lets divide
|
672
|
-
# and conquer.
|
673
|
-
def ok_replacements_pair_combinations
|
674
|
-
@ok_experiments
|
675
|
-
.combination(2)
|
676
|
-
.map { |e| e.flatten.uniq.sort }
|
677
|
-
.uniq - @fail_experiments - @ok_experiments
|
678
|
-
end
|
679
|
-
end
|
680
|
-
|
681
|
-
# Encapsulate the join of an Experiment with an specific file.
|
682
|
-
# This is important to coordinate and regulate multiple experiments in the same file.
|
683
|
-
# It can track successfull experiments and failures and suggest new combinations to keep replacing the file.
|
684
|
-
class ExperimentFile
|
685
|
-
attr_reader :ok_experiments, :fail_experiments, :experiment
|
686
|
-
|
687
|
-
def initialize(file, experiment)
|
688
|
-
@file = file
|
689
|
-
@ast = Fast.ast_from_file(file) if file
|
690
|
-
@experiment = experiment
|
691
|
-
@ok_experiments = []
|
692
|
-
@fail_experiments = []
|
693
|
-
@round = 0
|
694
|
-
end
|
695
|
-
|
696
|
-
def search
|
697
|
-
experiment.expression
|
698
|
-
end
|
699
|
-
|
700
|
-
def experimental_filename(combination)
|
701
|
-
parts = @file.split('/')
|
702
|
-
dir = parts[0..-2]
|
703
|
-
filename = "experiment_#{[*combination].join('_')}_#{parts[-1]}"
|
704
|
-
File.join(*dir, filename)
|
705
|
-
end
|
706
|
-
|
707
|
-
def ok_with(combination)
|
708
|
-
@ok_experiments << combination
|
709
|
-
return unless combination.is_a?(Array)
|
710
|
-
|
711
|
-
combination.each do |element|
|
712
|
-
@ok_experiments.delete(element)
|
713
|
-
end
|
714
|
-
end
|
715
|
-
|
716
|
-
def failed_with(combination)
|
717
|
-
@fail_experiments << combination
|
718
|
-
end
|
719
|
-
|
720
|
-
def search_cases
|
721
|
-
Fast.search(@ast, experiment.expression) || []
|
722
|
-
end
|
723
|
-
|
724
|
-
# rubocop:disable Metrics/AbcSize
|
725
|
-
# rubocop:disable Metrics/MethodLength
|
726
|
-
def partial_replace(*indices)
|
727
|
-
replacement = experiment.replacement
|
728
|
-
new_content = Fast.replace_file @file, experiment.expression, ->(node, *captures) do # rubocop:disable Style/Lambda
|
729
|
-
if indices.nil? || indices.empty? || indices.include?(match_index)
|
730
|
-
if replacement.parameters.length == 1
|
731
|
-
instance_exec node, &replacement
|
732
|
-
else
|
733
|
-
instance_exec node, *captures, &replacement
|
734
|
-
end
|
735
|
-
end
|
736
|
-
end
|
737
|
-
return unless new_content
|
738
|
-
|
739
|
-
write_experiment_file(indices, new_content)
|
740
|
-
new_content
|
741
|
-
end
|
742
|
-
# rubocop:enable Metrics/AbcSize
|
743
|
-
# rubocop:enable Metrics/MethodLength
|
744
|
-
|
745
|
-
def write_experiment_file(index, new_content)
|
746
|
-
filename = experimental_filename(index)
|
747
|
-
File.open(filename, 'w+') { |f| f.puts new_content }
|
748
|
-
filename
|
749
|
-
end
|
750
|
-
|
751
|
-
def done!
|
752
|
-
count_executed_combinations = @fail_experiments.size + @ok_experiments.size
|
753
|
-
puts "Done with #{@file} after #{count_executed_combinations} combinations"
|
754
|
-
return unless perfect_combination = @ok_experiments.last # rubocop:disable Lint/AssignmentInCondition
|
755
|
-
|
756
|
-
puts 'The following changes were applied to the file:'
|
757
|
-
`diff #{experimental_filename(perfect_combination)} #{@file}`
|
758
|
-
puts "mv #{experimental_filename(perfect_combination)} #{@file}"
|
759
|
-
`mv #{experimental_filename(perfect_combination)} #{@file}`
|
760
|
-
end
|
761
|
-
|
762
|
-
def build_combinations
|
763
|
-
@round += 1
|
764
|
-
ExperimentCombinations.new(
|
765
|
-
round: @round,
|
766
|
-
occurrences_count: search_cases.size,
|
767
|
-
ok_experiments: @ok_experiments,
|
768
|
-
fail_experiments: @fail_experiments
|
769
|
-
).generate_combinations
|
770
|
-
end
|
771
|
-
|
772
|
-
def run
|
773
|
-
while (combinations = build_combinations).any?
|
774
|
-
if combinations.size > 1000
|
775
|
-
puts "Ignoring #{@file} because it has #{combinations.size} possible combinations"
|
776
|
-
break
|
777
|
-
end
|
778
|
-
puts "#{@file} - Round #{@round} - Possible combinations: #{combinations.inspect}"
|
779
|
-
while combination = combinations.shift # rubocop:disable Lint/AssignmentInCondition
|
780
|
-
run_partial_replacement_with(combination)
|
781
|
-
end
|
782
|
-
end
|
783
|
-
done!
|
784
|
-
end
|
785
|
-
|
786
|
-
def run_partial_replacement_with(combination) # rubocop:disable Metrics/AbcSize
|
787
|
-
content = partial_replace(*combination)
|
788
|
-
experimental_file = experimental_filename(combination)
|
789
|
-
|
790
|
-
File.open(experimental_file, 'w+') { |f| f.puts content }
|
791
|
-
|
792
|
-
raise 'No changes were made to the file.' if FileUtils.compare_file(@file, experimental_file)
|
793
|
-
|
794
|
-
result = experiment.ok_if.call(experimental_file)
|
795
|
-
|
796
|
-
if result.success
|
797
|
-
ok_with(combination)
|
798
|
-
puts "✅ #{experimental_file} - Combination: #{combination} - Time: #{result.execution_time}s"
|
799
|
-
else
|
800
|
-
failed_with(combination)
|
801
|
-
puts "🔴 #{experimental_file} - Combination: #{combination}"
|
802
|
-
end
|
803
|
-
end
|
804
|
-
end
|
805
575
|
end
|
data/lib/fast/cli.rb
CHANGED
@@ -0,0 +1,249 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fast'
|
4
|
+
|
5
|
+
module Fast
|
6
|
+
class << self
|
7
|
+
# Fast.experiment is a shortcut to define new experiments and allow them to
|
8
|
+
# work together in experiment combinantions.
|
9
|
+
#
|
10
|
+
# The following experiment look into `spec` folder and try to remove
|
11
|
+
# `before` and `after` blocks on testing code. Sometimes they're not
|
12
|
+
# effective and we can avoid the hard work of do it manually.
|
13
|
+
#
|
14
|
+
# If the spec does not fail, it keeps the change.
|
15
|
+
#
|
16
|
+
# Fast.experiment("RSpec/RemoveUselessBeforeAfterHook") do
|
17
|
+
# lookup 'spec'
|
18
|
+
# search "(block (send nil {before after}))"
|
19
|
+
# edit { |node| remove(node.loc.expression) }
|
20
|
+
# policy { |new_file| system("rspec --fail-fast #{new_file}") }
|
21
|
+
# end
|
22
|
+
def experiment(name, &block)
|
23
|
+
@experiments ||= {}
|
24
|
+
@experiments[name] = Experiment.new(name, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :experiments
|
28
|
+
end
|
29
|
+
|
30
|
+
# You can define experiments and build experimental files to improve some code in
|
31
|
+
# an automated way. Let's create a hook to check if a `before` or `after` block
|
32
|
+
# is useless in a specific spec:
|
33
|
+
#
|
34
|
+
# ```ruby
|
35
|
+
# Fast.experiment("RSpec/RemoveUselessBeforeAfterHook") do
|
36
|
+
# lookup 'some_spec.rb'
|
37
|
+
# search "(block (send nil {before after}))"
|
38
|
+
# edit {|node| remove(node.loc.expression) }
|
39
|
+
# policy {|new_file| system("bin/spring rspec --fail-fast #{new_file}") }
|
40
|
+
# end
|
41
|
+
# ```
|
42
|
+
class Experiment
|
43
|
+
attr_writer :files
|
44
|
+
attr_reader :name, :replacement, :expression, :files_or_folders, :ok_if
|
45
|
+
|
46
|
+
def initialize(name, &block)
|
47
|
+
@name = name
|
48
|
+
puts "\nStarting experiment: #{name}"
|
49
|
+
instance_exec(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def run_with(file)
|
53
|
+
ExperimentFile.new(file, self).run
|
54
|
+
end
|
55
|
+
|
56
|
+
def search(expression)
|
57
|
+
@expression = expression
|
58
|
+
end
|
59
|
+
|
60
|
+
def edit(&block)
|
61
|
+
@replacement = block
|
62
|
+
end
|
63
|
+
|
64
|
+
def lookup(files_or_folders)
|
65
|
+
@files_or_folders = files_or_folders
|
66
|
+
end
|
67
|
+
|
68
|
+
def policy(&block)
|
69
|
+
@ok_if = block
|
70
|
+
end
|
71
|
+
|
72
|
+
def files
|
73
|
+
@files ||= Fast.ruby_files_from(@files_or_folders)
|
74
|
+
end
|
75
|
+
|
76
|
+
def run
|
77
|
+
files.map(&method(:run_with))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Suggest possible combinations of occurrences to replace.
|
82
|
+
class ExperimentCombinations
|
83
|
+
attr_reader :combinations
|
84
|
+
|
85
|
+
def initialize(round:, occurrences_count:, ok_experiments:, fail_experiments:)
|
86
|
+
@round = round
|
87
|
+
@ok_experiments = ok_experiments
|
88
|
+
@fail_experiments = fail_experiments
|
89
|
+
@occurrences_count = occurrences_count
|
90
|
+
end
|
91
|
+
|
92
|
+
def generate_combinations
|
93
|
+
case @round
|
94
|
+
when 1
|
95
|
+
individual_replacements
|
96
|
+
when 2
|
97
|
+
all_ok_replacements_combined
|
98
|
+
else
|
99
|
+
ok_replacements_pair_combinations
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Replace a single occurrence at each iteration and identify which
|
104
|
+
# individual replacements work.
|
105
|
+
def individual_replacements
|
106
|
+
(1..@occurrences_count).to_a
|
107
|
+
end
|
108
|
+
|
109
|
+
# After identifying all individual replacements that work, try combining all
|
110
|
+
# of them.
|
111
|
+
def all_ok_replacements_combined
|
112
|
+
[@ok_experiments.uniq.sort]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Combining all successful individual replacements has failed. Lets divide
|
116
|
+
# and conquer.
|
117
|
+
def ok_replacements_pair_combinations
|
118
|
+
@ok_experiments
|
119
|
+
.combination(2)
|
120
|
+
.map { |e| e.flatten.uniq.sort }
|
121
|
+
.uniq - @fail_experiments - @ok_experiments
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Encapsulate the join of an Experiment with an specific file.
|
126
|
+
# This is important to coordinate and regulate multiple experiments in the same file.
|
127
|
+
# It can track successfull experiments and failures and suggest new combinations to keep replacing the file.
|
128
|
+
class ExperimentFile
|
129
|
+
attr_reader :ok_experiments, :fail_experiments, :experiment
|
130
|
+
|
131
|
+
def initialize(file, experiment)
|
132
|
+
@file = file
|
133
|
+
@ast = Fast.ast_from_file(file) if file
|
134
|
+
@experiment = experiment
|
135
|
+
@ok_experiments = []
|
136
|
+
@fail_experiments = []
|
137
|
+
@round = 0
|
138
|
+
end
|
139
|
+
|
140
|
+
def search
|
141
|
+
experiment.expression
|
142
|
+
end
|
143
|
+
|
144
|
+
def experimental_filename(combination)
|
145
|
+
parts = @file.split('/')
|
146
|
+
dir = parts[0..-2]
|
147
|
+
filename = "experiment_#{[*combination].join('_')}_#{parts[-1]}"
|
148
|
+
File.join(*dir, filename)
|
149
|
+
end
|
150
|
+
|
151
|
+
def ok_with(combination)
|
152
|
+
@ok_experiments << combination
|
153
|
+
return unless combination.is_a?(Array)
|
154
|
+
|
155
|
+
combination.each do |element|
|
156
|
+
@ok_experiments.delete(element)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def failed_with(combination)
|
161
|
+
@fail_experiments << combination
|
162
|
+
end
|
163
|
+
|
164
|
+
def search_cases
|
165
|
+
Fast.search(@ast, experiment.expression) || []
|
166
|
+
end
|
167
|
+
|
168
|
+
# rubocop:disable Metrics/AbcSize
|
169
|
+
# rubocop:disable Metrics/MethodLength
|
170
|
+
def partial_replace(*indices)
|
171
|
+
replacement = experiment.replacement
|
172
|
+
new_content = Fast.replace_file @file, experiment.expression do |node, *captures|
|
173
|
+
if indices.nil? || indices.empty? || indices.include?(match_index)
|
174
|
+
if replacement.parameters.length == 1
|
175
|
+
instance_exec node, &replacement
|
176
|
+
else
|
177
|
+
instance_exec node, *captures, &replacement
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
return unless new_content
|
182
|
+
|
183
|
+
write_experiment_file(indices, new_content)
|
184
|
+
new_content
|
185
|
+
end
|
186
|
+
# rubocop:enable Metrics/AbcSize
|
187
|
+
# rubocop:enable Metrics/MethodLength
|
188
|
+
|
189
|
+
def write_experiment_file(index, new_content)
|
190
|
+
filename = experimental_filename(index)
|
191
|
+
File.open(filename, 'w+') { |f| f.puts new_content }
|
192
|
+
filename
|
193
|
+
end
|
194
|
+
|
195
|
+
def done!
|
196
|
+
count_executed_combinations = @fail_experiments.size + @ok_experiments.size
|
197
|
+
puts "Done with #{@file} after #{count_executed_combinations} combinations"
|
198
|
+
return unless perfect_combination = @ok_experiments.last # rubocop:disable Lint/AssignmentInCondition
|
199
|
+
|
200
|
+
puts 'The following changes were applied to the file:'
|
201
|
+
`diff #{experimental_filename(perfect_combination)} #{@file}`
|
202
|
+
puts "mv #{experimental_filename(perfect_combination)} #{@file}"
|
203
|
+
`mv #{experimental_filename(perfect_combination)} #{@file}`
|
204
|
+
end
|
205
|
+
|
206
|
+
def build_combinations
|
207
|
+
@round += 1
|
208
|
+
ExperimentCombinations.new(
|
209
|
+
round: @round,
|
210
|
+
occurrences_count: search_cases.size,
|
211
|
+
ok_experiments: @ok_experiments,
|
212
|
+
fail_experiments: @fail_experiments
|
213
|
+
).generate_combinations
|
214
|
+
end
|
215
|
+
|
216
|
+
def run
|
217
|
+
while (combinations = build_combinations).any?
|
218
|
+
if combinations.size > 1000
|
219
|
+
puts "Ignoring #{@file} because it has #{combinations.size} possible combinations"
|
220
|
+
break
|
221
|
+
end
|
222
|
+
puts "#{@file} - Round #{@round} - Possible combinations: #{combinations.inspect}"
|
223
|
+
while combination = combinations.shift # rubocop:disable Lint/AssignmentInCondition
|
224
|
+
run_partial_replacement_with(combination)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
done!
|
228
|
+
end
|
229
|
+
|
230
|
+
def run_partial_replacement_with(combination) # rubocop:disable Metrics/AbcSize
|
231
|
+
content = partial_replace(*combination)
|
232
|
+
experimental_file = experimental_filename(combination)
|
233
|
+
|
234
|
+
File.open(experimental_file, 'w+') { |f| f.puts content }
|
235
|
+
|
236
|
+
raise 'No changes were made to the file.' if FileUtils.compare_file(@file, experimental_file)
|
237
|
+
|
238
|
+
result = experiment.ok_if.call(experimental_file)
|
239
|
+
|
240
|
+
if result.success
|
241
|
+
ok_with(combination)
|
242
|
+
puts "✅ #{experimental_file} - Combination: #{combination} - Time: #{result.execution_time}s"
|
243
|
+
else
|
244
|
+
failed_with(combination)
|
245
|
+
puts "🔴 #{experimental_file} - Combination: #{combination}"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/lib/fast/version.rb
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.8
|
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: 2019-03-
|
11
|
+
date: 2019-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: astrolabe
|
@@ -235,6 +235,7 @@ files:
|
|
235
235
|
- fast.gemspec
|
236
236
|
- lib/fast.rb
|
237
237
|
- lib/fast/cli.rb
|
238
|
+
- lib/fast/experiment.rb
|
238
239
|
- lib/fast/version.rb
|
239
240
|
- mkdocs.yml
|
240
241
|
homepage: https://jonatas.github.io/fast/
|