laborantin 0.0.14 → 0.0.21

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.
Files changed (46) hide show
  1. data/INFO +1 -0
  2. data/README +148 -4
  3. data/Rakefile +73 -27
  4. data/TODO +27 -6
  5. data/bin/labor +2 -404
  6. data/lib/laborantin.rb +13 -26
  7. data/lib/laborantin/core/analysis.rb +231 -0
  8. data/lib/laborantin/core/command.rb +234 -0
  9. data/lib/laborantin/core/completeable.rb +30 -0
  10. data/lib/laborantin/core/configurable.rb +28 -0
  11. data/lib/laborantin/core/datable.rb +13 -0
  12. data/lib/laborantin/core/describable.rb +11 -0
  13. data/lib/laborantin/core/environment.rb +90 -52
  14. data/lib/laborantin/core/hookable.rb +20 -0
  15. data/lib/laborantin/core/monkey_patches.rb +0 -1
  16. data/lib/laborantin/core/multi_name.rb +25 -0
  17. data/lib/laborantin/core/parameter.rb +5 -12
  18. data/lib/laborantin/core/parameter_hash.rb +6 -2
  19. data/lib/laborantin/core/scenario.rb +93 -70
  20. data/lib/laborantin/core/table.rb +84 -0
  21. data/lib/laborantin/extra/commands/git.rb +40 -0
  22. data/lib/laborantin/extra/commands/git/check.rb +25 -0
  23. data/lib/laborantin/extra/commands/git/run.rb +100 -0
  24. data/lib/laborantin/extra/vectorial_product.rb +31 -0
  25. data/lib/laborantin/runner.rb +247 -0
  26. data/lib/laborantin/runner/commands/analyze.rb +58 -0
  27. data/lib/laborantin/runner/commands/cleanup.rb +40 -0
  28. data/lib/laborantin/runner/commands/complete.rb +111 -0
  29. data/lib/laborantin/runner/commands/config.rb +169 -0
  30. data/lib/laborantin/runner/commands/create.rb +61 -0
  31. data/lib/laborantin/runner/commands/describe.rb +215 -0
  32. data/lib/laborantin/runner/commands/find.rb +82 -0
  33. data/lib/laborantin/runner/commands/load_classes.rb +75 -0
  34. data/lib/laborantin/runner/commands/load_results.rb +143 -0
  35. data/lib/laborantin/runner/commands/note.rb +35 -0
  36. data/lib/laborantin/runner/commands/replay.rb +89 -0
  37. data/lib/laborantin/runner/commands/rm.rb +107 -0
  38. data/lib/laborantin/runner/commands/run.rb +131 -0
  39. data/lib/laborantin/runner/commands/scan.rb +77 -0
  40. metadata +45 -13
  41. data/bin/files/README.erb +0 -29
  42. data/bin/files/TODO.erb +0 -2
  43. data/bin/files/config/ftp.yaml.erb +0 -6
  44. data/bin/files/config/xmpp.yaml.erb +0 -7
  45. data/bin/files/environments/environment.rb.erb +0 -10
  46. data/bin/files/scenarii/scenario.rb.erb +0 -13
@@ -21,34 +21,21 @@ Copyright (c) 2009, Lucas Di Cioccio
21
21
 
22
22
  =end
23
23
 
24
- require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'scenario')
25
- require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'parameter')
26
- require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'parameter_hash')
27
- require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'environment')
28
- require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'monkey_patches')
24
+ [ 'laborantin/core/scenario',
25
+ 'laborantin/core/parameter',
26
+ 'laborantin/core/parameter_hash',
27
+ 'laborantin/core/environment',
28
+ 'laborantin/core/analysis',
29
+ 'laborantin/core/command',
30
+ 'laborantin/core/monkey_patches'
31
+ ].each do |dep|
32
+ require dep
33
+ end
34
+
29
35
 
30
36
  module Laborantin
31
- VERSION = '0.0.14'
37
+ VERSION = '0.0.21'
32
38
  AUTHORS = ['Lucas Di Cioccio']
33
- WEBSITE = 'http://dicioccio.fr'
39
+ WEBSITE = 'http://dicioccio.fr/laborantin'
34
40
  LICENSE = 'GNU GPL version 3'
35
-
36
- @@rootdir = '.'
37
-
38
- # The root of the arborescence for Laborantin. Usually the directory created
39
- # via the scripts.
40
- def self.rootdir
41
- @@rootdir || '.'
42
- end
43
-
44
- # Specifies the rootdir, dir is a path (instance of String, not a Dir object).
45
- def self.rootdir=(dir='.')
46
- @@rootdir = dir
47
- end
48
-
49
- # The path to the results (needs the Laborantin.rootdir).
50
- def self.resultdir
51
- File.join(Laborantin.rootdir, 'results')
52
- end
53
-
54
41
  end
@@ -0,0 +1,231 @@
1
+ #core/analysis.rb
2
+
3
+ =begin
4
+
5
+ This file is part of Laborantin.
6
+
7
+ Laborantin is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Laborantin is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Laborantin. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ Copyright (c) 2009, Lucas Di Cioccio
21
+
22
+ =end
23
+
24
+ autoload :ERB, 'erb'
25
+
26
+ require 'laborantin/core/describable'
27
+ require 'laborantin/core/multi_name'
28
+
29
+ module Laborantin
30
+ # An Analysis is a handy way to reload and filter the various scenarii that were
31
+ # run. You can easily filter on them.
32
+ class Analysis
33
+ extend Metaprog::Describable
34
+ extend Metaprog::MultiName
35
+
36
+ class << self
37
+ # A hash with two items, this might change later but KISS for now.
38
+ attr_accessor :selectors
39
+
40
+ # An array
41
+ attr_accessor :analyses
42
+
43
+ # Add a selector to filter for the analysis only the runs that pass the selector.
44
+ # * sym objects (sym currently must be :environments or
45
+ # :scenarii).
46
+ # * ary is a set of classes, only runs of this classes will be loaded
47
+ # * if a block is passed, only the instances for which the block is
48
+ # evaluated as true will be selected (the block must take one parameter:
49
+ # the tested instance)
50
+ def select(sym, ary=[], &blk)
51
+ @selectors ||= {}
52
+ @selectors[sym] = {:klasses => ary, :blk => blk}
53
+ end
54
+
55
+ # Adds an analysis to this class.
56
+ # str is a description of the added analysis params is a hash of
57
+ # parameters for this analysis, specifically, the :type parameters allows
58
+ # you to differenciate the kind of analysis for repors TODO: more info on
59
+ # that, tells that we can directly use Analysis.plot and Analysis.table
60
+ # methods
61
+ def analyze(str, params = {}, &blk)
62
+ @analyses << {:str => str, :params => params, :blk => blk}
63
+ end
64
+
65
+ def plot(title, args, &blk)
66
+ args ||= {}
67
+ hash = args.merge({:type => :plot})
68
+ analyze(title, hash, &blk)
69
+ end
70
+
71
+ def table(title, args, &blk)
72
+ args ||= {}
73
+ hash = args.merge({:type => :table})
74
+ analyze(title, hash, &blk)
75
+ end
76
+
77
+ def plots
78
+ analyses.select{|a| a[:params][:type] == :plots}
79
+ end
80
+
81
+ def tables
82
+ analyses.select{|a| a[:params][:type] == :tables}
83
+ end
84
+
85
+ @@all = []
86
+
87
+ def inherited(klass)
88
+ @@all << klass
89
+ klass.select(:environments,[Laborantin::Environment])
90
+ klass.select(:scenarii,[Laborantin::Scenario])
91
+ klass.analyses = []
92
+ end
93
+
94
+ def all
95
+ @@all
96
+ end
97
+ end # << self
98
+
99
+ # An array of the Environments that could be loaded from the result directory.
100
+ attr_accessor :environments
101
+
102
+ # An array of the Scenarii that could be loaded from the result directory.
103
+ attr_reader :scenarii
104
+
105
+ # TODO : recode this, maybe as nothing to do here
106
+ def analyze
107
+ self.class.analyses.each do |a|
108
+ puts "(#{a[:str]})"
109
+ instance_eval &a[:blk]
110
+ puts "done"
111
+ end
112
+ end
113
+
114
+ # TODO: more flexible
115
+ def report(tpl_path=nil)
116
+ tpl = ERB.new(File.read(tpl_path))
117
+ File.open("reports/#{self.class.name}.html", 'w') do |f|
118
+ f.puts tpl.result(binding)
119
+ end
120
+ end
121
+
122
+ def output_dirname
123
+ self.class.cli_name
124
+ end
125
+
126
+ def output_dirpath
127
+ File.join('.', 'reports', output_dirname)
128
+ end
129
+
130
+ def create_output_dir
131
+ FileUtils.mkdir_p(output_dirpath) unless File.directory?(output_dirpath)
132
+ end
133
+
134
+ def output_path(name)
135
+ File.join(output_dirpath, name)
136
+ end
137
+
138
+ def output(name, mode='r')
139
+ create_output_dir
140
+ File.open(output_path(name), mode) do |f|
141
+ yield f
142
+ end
143
+ end
144
+
145
+ def table(name, struct)
146
+ Table.new(name, struct, self.output_path(name))
147
+ end
148
+
149
+ private
150
+
151
+ # Just loads the environments and scenarii from the resultdir.
152
+ def initialize(*args, &blk)
153
+ load_from_results
154
+ set_instance_vars
155
+ end
156
+
157
+ # Load first the environments, then the scenarii.
158
+ def load_from_results
159
+ load_environments
160
+ load_scenarii
161
+ end
162
+
163
+ # Sets the various handy instance variables:
164
+ # * @plots
165
+ # * @tables
166
+ def set_instance_vars
167
+ @plots = self.class.plots.dup
168
+ @tables = self.class.tables.dup
169
+ end
170
+
171
+ # Will try to load environments and set the @environments variable to an
172
+ # array of all the Environment instance that match the :environments class
173
+ # selector (set with Analysis#select).
174
+ def load_environments
175
+ envs = Laborantin::Environment.scan_resdir('results')
176
+ @environments = envs.select do |env|
177
+ select_instance?(env, self.class.selectors[:environments])
178
+ end
179
+ end
180
+
181
+ # Same as load_environments, but for Scenario instances and :scenarii
182
+ # selector.
183
+ def load_scenarii
184
+ scii = @environments.map{|e| e.populate}.flatten
185
+ @scenarii = scii.select do |sc|
186
+ select_instance?(sc, self.class.selectors[:scenarii])
187
+ end
188
+ end
189
+
190
+ # Handy test to see if an object (that should be an instance of Environment
191
+ # or Scenario) matches the selector.
192
+ def select_instance?(obj, selector)
193
+ blk = selector[:blk]
194
+ (selector[:klasses].any?{|k| obj.is_a? k} ) and
195
+ (blk ? blk.call(obj) : true)
196
+ end
197
+
198
+ # Nice way to iterate on @scenarii
199
+ def each_scenario
200
+ @scenarii.each do |sc|
201
+ yield sc
202
+ end
203
+ end
204
+
205
+ # Nice way to iterate on @environments
206
+ def each_environment
207
+ @environments.each do |env|
208
+ yield env
209
+ end
210
+ end
211
+
212
+ # The list of parameters spanned from the scenarii.
213
+ # e.g. scenario 1, params = A => a1, B => b1
214
+ # scenario 2, params = A => a2, B => b2
215
+ # scenario 3, params = C => c3
216
+ # parameters = A => [a1, a2], B => [b1, b2], C => [c3]
217
+ def parameters
218
+ unless @parameters
219
+ @parameters = {}
220
+ each_scenario do |sc|
221
+ sc.params.each_pair do |name, value|
222
+ @parameters[name] ||= []
223
+ @parameters[name] << value unless @parameters[name].include?(value)
224
+ end
225
+ end
226
+ end
227
+ @parameters
228
+ end
229
+
230
+ end
231
+ end
@@ -0,0 +1,234 @@
1
+ #core/command.rb
2
+
3
+ =begin
4
+
5
+ This file is part of Laborantin.
6
+
7
+ Laborantin is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Laborantin is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Laborantin. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ Copyright (c) 2009, Lucas Di Cioccio
21
+
22
+ =end
23
+
24
+ require 'laborantin/core/completeable'
25
+
26
+ module Laborantin
27
+ # The Command is a way to extend the labor script to help you modularize your
28
+ # work, and call the commands both via the command line, or in your scripts.
29
+ # This class provides parsing facilities. Namespacing (i.e., mapping between
30
+ # classes and command line is done in the Runner . Internal labor Commands
31
+ # are actually implemented this way.
32
+ class Command
33
+
34
+ # An Option for a Command, to help us building a nice DSL for you.
35
+ class Option
36
+
37
+ include Metaprog::Completeable
38
+
39
+ # The name of the option.
40
+ attr_reader :name
41
+
42
+ # The short option flag of the command line (e.g. '-t')
43
+ attr_reader :cli_short
44
+
45
+ # The long option flag of the command line (e.g. '--trust')
46
+ attr_reader :cli_long
47
+
48
+ # The description to provide feedback/help.
49
+ attr_reader :description
50
+
51
+ # The default value of this option.
52
+ attr_reader :default_value
53
+
54
+ # The type to expect for a value (give a class among those understood by OptParse)
55
+ attr_reader :default_type
56
+
57
+ def initialize(name)
58
+ @name = name
59
+ end
60
+
61
+ # Sets the cli_short option flag (e.g. short '-t')
62
+ def short(str)
63
+ @cli_short = str
64
+ end
65
+
66
+ # Sets the cli_long option flag (e.g. long '--trust')
67
+ def long(str)
68
+ @cli_long = str
69
+ end
70
+
71
+ # Sets the description of the option.
72
+ def describe(str)
73
+ @description = str
74
+ end
75
+
76
+ # Sets the default value of the option.
77
+ def default(val)
78
+ @default_value = val
79
+ end
80
+
81
+ # Sets the type to expect for the option.
82
+ def type(klass)
83
+ @default_type = klass
84
+ end
85
+
86
+ # Returns the description followed by the default value in parenthesis.
87
+ def description_with_default
88
+ "#{description} (default: #{default_value})."
89
+ end
90
+ end
91
+
92
+ # An Array to store all the commands.
93
+ @@all = []
94
+
95
+ extend Enumerable
96
+
97
+ # Once we've extended Enumerable, it's easy to find new commands.
98
+ def self.each
99
+ @@all.each{|e| yield e}
100
+ end
101
+
102
+ class << self
103
+
104
+ include Metaprog::Completeable
105
+
106
+ # The description string of a command, will be used on command line help.
107
+ attr_accessor :description
108
+
109
+ # Wether or not this command is a plumbery one.
110
+ attr_accessor :plumbery
111
+
112
+ # An Array contining the possible options of this command.
113
+ attr_accessor :options
114
+
115
+ # The block of execution for the instances of this commands.
116
+ # By default, this will raise an exception.
117
+ # TODO: transform the block into a default method.
118
+ attr_accessor :block
119
+
120
+ # The command name, can be set, this is useful when the class has no name
121
+ # (e.g. an object created with Class.new(Command)), or if you want to use
122
+ # another name for this command.
123
+ attr_accessor :command_name
124
+
125
+ # By default, the name of the class, or an empty string if none (this can
126
+ # lead to big issues, so I'm still considering returning nil if a command
127
+ # has no name nor command_name.
128
+ def command_name
129
+ (@command_name || self.name ) || ''
130
+ end
131
+
132
+ # When a new Command class is created, sets up default value, and store
133
+ # the new class in the known commands.
134
+ def inherited(klass)
135
+ klass.description = ''
136
+ klass.options = []
137
+ klass.block = lambda { raise RuntimeError.new("no execution block for #{klass}") }
138
+ @@all << klass
139
+ end
140
+
141
+ # Set the description string.
142
+ def describe(str=nil)
143
+ self.description = str
144
+ end
145
+
146
+ # Sets the plumbery flag.
147
+ def plumbery!(val=true)
148
+ self.plumbery = val
149
+ end
150
+
151
+ # Returns true if the plumbery flag is true.
152
+ def plumbery?
153
+ self.plumbery && true
154
+ end
155
+
156
+ # Opposite of plumbery?
157
+ def porcelain?
158
+ not self.plumbery?
159
+ end
160
+
161
+ # Set the execution block (executed in the scope of the instance).
162
+ # XXX this might well be changed into requiring ppl to define an "execute" method.
163
+ def execute(&blk)
164
+ self.block = blk
165
+ end
166
+
167
+ # Assign a new option to this command class, which name is name
168
+ # (preferably, give Symbol or String). The blk is evaluated in the scope
169
+ # of a new Option instance.
170
+ # Giving a name is required, because it is also the key of the command.opts hash.
171
+ def option(name, &blk)
172
+ arg = Option.new(name)
173
+ arg.instance_eval &blk
174
+ self.options << arg
175
+ end
176
+ end # << self
177
+
178
+ # A hash containing a merge of the defaults options of the class and the
179
+ # extra options provided to the run method.
180
+ # Think of it like the '--path=/some/path' options of a command line.
181
+ # Options are labeled but not ordered, arguments are the opposite.
182
+ attr_reader :opts
183
+
184
+ # An array containing the arguments of the command.
185
+ # Think of it like the trailing arguments of a command line.
186
+ # Arguments are ordered but not labeled, options are the opposite.
187
+ attr_reader :args
188
+
189
+ # The Runner object that will executes the command.
190
+ attr_accessor :runner
191
+
192
+ # Initializes a new instance of command. Will first set the runner if
193
+ # provided, then will initialize default options and arguments.
194
+ def initialize(runner=nil)
195
+ @runner = runner
196
+ initialize_opts
197
+ initialize_args
198
+ end
199
+
200
+ # Runs the command by merging the options with extra_opts and evaluating
201
+ # the block stored in the class.
202
+ def run(args=[], extra_opts={})
203
+ @opts.merge!(extra_opts)
204
+ @args = args
205
+ self.instance_eval &self.class.block
206
+ end
207
+
208
+ # Forward a line to the runner if it respond to a puts method too.
209
+ # Fallback on Kernel.puts if there is no runner or the runner does
210
+ # not respond to puts.
211
+ def puts(line)
212
+ if runner and runner.respond_to?(:puts)
213
+ runner.puts(line)
214
+ else
215
+ Kernel.puts(line)
216
+ end
217
+ end
218
+
219
+ private
220
+
221
+ # Just initialize the opts to an empty Array
222
+ def initialize_args
223
+ @args = []
224
+ end
225
+
226
+ # Grabs the default values of the options from the class of the command.
227
+ def initialize_opts
228
+ @opts = {}
229
+ self.class.options.each do |arg|
230
+ @opts[arg.name] = arg.default_value
231
+ end
232
+ end
233
+ end
234
+ end