laborantin 0.0.9 → 0.0.10

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.
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rake/gempackagetask'
3
3
 
4
4
  spec = Gem::Specification.new do |s|
5
5
  s.name = 'laborantin'
6
- s.version = '0.0.9'
6
+ s.version = '0.0.10'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.summary = "A measurement batch facilitator"
9
9
 
@@ -33,7 +33,7 @@ spec = Gem::Specification.new do |s|
33
33
  s.bindir = 'bin'
34
34
  s.executables = ['labor']
35
35
 
36
- s.has_rdoc = false
36
+ s.has_rdoc = true
37
37
  end
38
38
 
39
39
  Rake::GemPackageTask.new(spec) do |pkg|
data/TODO CHANGED
@@ -3,6 +3,4 @@ replay / pause / restart
3
3
  => interaction with rubydrill?
4
4
  => or direct stats
5
5
 
6
- export results via ftp
7
-
8
6
  creation of a script that doesn't depends on laborantin for export?
data/bin/labor CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'optparse'
4
3
  require 'rubygems'
5
4
  require 'laborantin'
6
5
  #require '../lib/laborantin'
6
+ require 'optparse'
7
7
  require 'fileutils'
8
8
  require 'find'
9
9
  require 'erb'
@@ -28,8 +28,27 @@ require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'environment')
28
28
  require File.join(File.dirname(__FILE__), 'laborantin', 'core', 'monkey_patches')
29
29
 
30
30
  module Laborantin
31
- VERSION = '0.0.9'
31
+ VERSION = '0.0.10'
32
32
  AUTHORS = ['Lucas Di Cioccio']
33
33
  WEBSITE = 'http://dicioccio.fr'
34
34
  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
+
35
54
  end
@@ -26,10 +26,22 @@ require 'logger'
26
26
  require 'fileutils'
27
27
 
28
28
  module Laborantin
29
+
30
+ # An Environment represents the surrounding of an experiment. Basically, it
31
+ # should only contains things that are hard to change during an experiment.
32
+ # Let's say you have two computers, A and B. A can be a server and B a client
33
+ # or vice-versa. Hence, this is a good choice for two differents environments.
34
+ #
35
+ # As a normal user, you should only subclass Environment, and let the script
36
+ # creates it for you. But it is easy to monkey-patch or to subclass by hand.
37
+ # If you want to do that, you must know that Environment @@all class variable
38
+ # holds a reference to every child class from Environment.
29
39
  class Environment
30
40
  @@all = []
31
- @@rootdir = '.'
32
41
 
42
+ # Populates loaded (i.e. put in @@all class variable when self.inherited
43
+ # is called) environment classes from existing results that are stored in
44
+ # the dir parameter.
33
45
  def self.scan_resdir(dir)
34
46
  ret = []
35
47
  Dir.entries(dir).each do |f|
@@ -48,20 +60,22 @@ module Laborantin
48
60
  end
49
61
 
50
62
  class << self
51
- attr_accessor :verifications, :description, :envdir, :hooks
52
63
 
53
- def rootdir=(dir='.')
54
- @@rootdir = dir
55
- end
64
+ # An array of methods called to ensure that the environment run is the
65
+ # wanted one (e.g. a way to specify a RUBY_PLATFORM).
66
+ # CURRENTLY NOT HERITED
67
+ attr_accessor :verifications
56
68
 
57
- def rootdir
58
- @@rootdir || '.'
59
- end
69
+ # A description used for printing summary and debug purposes. Will be
70
+ # used to create .tex report in the future.
71
+ # CURRENTLY NOT HERITED
72
+ attr_accessor :description
60
73
 
61
- def resultdir
62
- @@resultdir = File.join(@@rootdir, 'results')
63
- end
74
+ # A hash to store setup/teardown hooks.
75
+ # CURRENTLY NOT HERITED
76
+ attr_accessor :hooks
64
77
 
78
+ # Prepares attributes' default values whenever a subclass is created.
65
79
  def inherited(klass)
66
80
  klass.verifications = []
67
81
  klass.description = ''
@@ -69,18 +83,23 @@ module Laborantin
69
83
  @@all << klass
70
84
  end
71
85
 
86
+ # Registers new verifiers.
72
87
  def verify(*args)
73
88
  self.verifications = [*args].flatten
74
89
  end
75
90
 
91
+ # Sets the description.
76
92
  def describe(str)
77
93
  self.description = str
78
94
  end
79
95
 
96
+ # Registers setup hooks, called before any scenario is instantiated.
80
97
  def setup(*args)
81
98
  self.hooks[:setup] = [*args].flatten
82
99
  end
83
100
 
101
+ # Registers teardown hooks, called after every scenarii has been
102
+ # performed and analyzed.
84
103
  def teardown(*args)
85
104
  self.hooks[:teardown] = [*args].flatten
86
105
  end
@@ -89,20 +108,31 @@ module Laborantin
89
108
  "#{self.name}:\n\t#{self.description}"
90
109
  end
91
110
 
111
+ # Returns all the known subklasses of Environment.
92
112
  def all
93
113
  @@all
94
114
  end
95
115
 
116
+ # The path where the results for instance of a subklass of Environment
117
+ # are stored (needs the Laborantin.resultdir).
96
118
  def envdir
97
- File.join(resultdir, self.name.duck_case)
119
+ File.join(Laborantin.resultdir, self.name.duck_case)
98
120
  end
99
-
100
121
  end
101
122
 
102
- #XXX: rundir is the easier way to override the rundir (instead of parsing a date)
103
- attr_accessor :rundir, :date, :loggers
123
+ # An attribute that holds the directory where the logfile and the scenarii results
124
+ # are stored. Can be overridden (e.g. Environment.scan_resdir does that).
125
+ attr_accessor :rundir
126
+
127
+ # A date that holds the creation of the instance, it is not meaningful
128
+ # when an env was created by a call to Environment.scan_resdir.
129
+ # TODO better
130
+ attr_accessor :date
131
+
132
+ # An array of loggers objects.
133
+ attr_accessor :loggers
104
134
 
105
- def initialize(dir='.')
135
+ def initialize
106
136
  @date = Time.now
107
137
  @rundir = File.join(self.class.envdir, date_str)
108
138
  @loggers = []
@@ -112,6 +142,13 @@ module Laborantin
112
142
  self.class.verifications.find{|v| not send(v)}.nil?
113
143
  end
114
144
 
145
+ # In the following order:
146
+ # * Creates the envdir if needed.
147
+ # * Adds some loggers.
148
+ # * Calls the setup hooks methods
149
+ # BEWARE : currently does not ensure unicity of envdir,
150
+ # so wait one sec between several runs of same env
151
+ #
115
152
  def prepare!
116
153
  FileUtils.mkdir_p(rundir) #TODO: ensure unicity
117
154
  @loggers << Logger.new(File.join(rundir, 'environment.log'))
@@ -121,14 +158,18 @@ module Laborantin
121
158
  call_hooks :setup
122
159
  end
123
160
 
161
+ # Calls the teardown hooks methods.
124
162
  def teardown!
125
163
  call_hooks :teardown
126
164
  end
127
165
 
166
+ # Send str log message at the levele lvl to every loggers.
128
167
  def log(str, lvl=:debug)
129
168
  @loggers.each{|l| l.send(lvl, str)}
130
169
  end
131
170
 
171
+ # Returns an array of Scenario objects from the results in the envdir.
172
+ # The scenarii classes must be loaded before, else, some results might be ignored.
132
173
  def populate
133
174
  Laborantin::Scenario.scan_env(self)
134
175
  end
@@ -21,10 +21,16 @@ Copyright (c) 2009, Lucas Di Cioccio
21
21
 
22
22
  =end
23
23
 
24
+ # Some monkey patches on String. Will be moved later to a subclass.
24
25
  class String
26
+ # Returns a camelized version of a duck_case self
27
+ # 'some_string'.camelize # => 'SomeString'
25
28
  def camelize
26
29
  self.split('_').map{|i| i.capitalize}.join('')
27
30
  end
31
+
32
+ # Returns a duck cased version of camel-like self
33
+ # 'SomeString'.duck_case # => 'some_string'
28
34
  def duck_case
29
35
  self.gsub(/([A-Z])/){|s| "_#{$1.downcase}"}.sub(/^_/,'')
30
36
  end
@@ -22,25 +22,39 @@ Copyright (c) 2009, Lucas Di Cioccio
22
22
  =end
23
23
 
24
24
  module Laborantin
25
+
26
+ # A ParameterRange instance is more or less a wrapper over an Array of allowed values.
25
27
  class ParameterRange
26
- attr_accessor :name, :description
28
+
29
+ # The name of the parameter (should be unique in a Scenario's parameters)
30
+ # Usually a symbol.
31
+ attr_accessor :name
32
+
33
+ # A description used for printing summary and debug purposes. Will be
34
+ # used to create .tex report in the future.
35
+ attr_accessor :description
36
+
27
37
  def initialize(name)
28
38
  @name = name
29
39
  @values = []
30
40
  @description = ''
31
41
  end
32
42
 
43
+ # Sets the allowed values. Currently only supports Array like.
44
+ # It is planned to support Iterable, but deterministic ones only.
33
45
  def values(*args)
34
46
  @values = args.flatten unless args.empty?
35
47
  @values
36
48
  end
37
49
 
50
+ # Iterates on allowed values for this parameter.
38
51
  def each
39
52
  @values.each do |v|
40
- yield v
53
+ yield v
41
54
  end
42
55
  end
43
56
 
57
+ # Sets the description.
44
58
  def describe(str)
45
59
  @description = str
46
60
  end
@@ -23,16 +23,20 @@ Copyright (c) 2009, Lucas Di Cioccio
23
23
 
24
24
 
25
25
  module Laborantin
26
+ # A kind of Hash that can yields all possible configuration recursively.
27
+ # It should contains ParameterRange like definitions objects.
26
28
  class ParameterHash < Hash
29
+ # Recursively yields all the possible configurations of parameters (a new hash).
30
+ # No order is supported on the recursion, and it is not planned to.
27
31
  def each_config(remaining=self.keys, cfg={}, &blk)
28
32
  key = remaining.pop
29
33
  if key
30
- self[key].each do |val|
31
- cfg[key] = val
32
- each_config(remaining.dup, cfg, &blk)
33
- end
34
+ self[key].each do |val|
35
+ cfg[key] = val
36
+ each_config(remaining.dup, cfg, &blk)
37
+ end
34
38
  else
35
- yield cfg
39
+ yield cfg
36
40
  end
37
41
  end
38
42
 
@@ -28,9 +28,25 @@ require 'fileutils'
28
28
  require 'yaml'
29
29
 
30
30
  module Laborantin
31
+
32
+ # A Scenario represents a measurement done in a given environment. Some of
33
+ # its parameters will change, and we are interested in varying these parameters and
34
+ # then study their impact.
35
+ #
36
+ # An user will usually creates a Scenario subklass which represents such
37
+ # a measurement. For that he must defines a run method that will yield consecutive
38
+ # lines added to the raw result file. Then this file can be processed to give
39
+ # intermediary or final results.
40
+ #
41
+ # Like the Environment, all the subklasses will be stored in a @@all
42
+ # class variable for convenience purpose.
31
43
  class Scenario
32
44
  @@all = []
33
45
 
46
+ # Scans the env's envdir (should be an Environment) for scenarii results.
47
+ # It will set their configuration according to the stored config.yaml
48
+ # in YAML format.
49
+ # Returns an array of such built scenarii.
34
50
  def self.scan_env(env)
35
51
  scs = []
36
52
  Dir.entries(env.rundir).each do |s|
@@ -50,8 +66,26 @@ module Laborantin
50
66
  end
51
67
 
52
68
  class << self
53
- attr_accessor :description, :parameters, :products, :hooks
54
69
 
70
+ # A description used for printing summary and debug purposes. Will be
71
+ # used to create .tex report in the future.
72
+ # CURRENTLY NOT HERITED
73
+ attr_accessor :description
74
+
75
+ # A hash to store setup/teardown hooks.
76
+ # CURRENTLY NOT HERITED
77
+ attr_accessor :hooks
78
+
79
+ # The set of parameters that will vary for this Scenario.
80
+ attr_accessor :parameters
81
+
82
+ # Some special products that are done after an analysis on a measurement
83
+ # scenario. The intended way is to store raw results (e.g. a command output) in
84
+ # a file and then parse them and store the parsed result in another file etc.
85
+ # TODO : products that compares scenarii
86
+ attr_accessor :products
87
+
88
+ # Prepares attributes' default values whenever a subclass is created.
55
89
  def inherited(klass)
56
90
  klass.parameters = ParameterHash.new
57
91
  klass.description = ''
@@ -60,19 +94,30 @@ module Laborantin
60
94
  @@all << klass
61
95
  end
62
96
 
97
+ # Sets the description.
63
98
  def describe(str)
64
99
  self.description = str
65
100
  end
66
101
 
102
+ # Registers setup hooks.
67
103
  def setup(*args)
68
104
  self.hooks[:setup] = [*args].flatten
69
105
  end
70
106
 
107
+ # Register teardown hooks.
71
108
  def teardown(*args)
72
109
  self.hooks[:teardown] = [*args].flatten
73
110
  end
74
111
 
75
-
112
+ # Defines a new ParameterRange instance for this Scenario.
113
+ # A block should be passed that will be evaluated in this
114
+ # ParameterRange instance's context.
115
+ #
116
+ # parameter(:size) do
117
+ # values 10, 20, 30
118
+ # describe "We expect a linear RTT increase with the size"
119
+ # end
120
+ #
76
121
  def parameter(name, &blk)
77
122
  raise ArgumentError.new("Parameter #{name} already exists") if self.parameters[name]
78
123
  param = ParameterRange.new(name)
@@ -80,6 +125,9 @@ module Laborantin
80
125
  self.parameters[name] = param
81
126
  end
82
127
 
128
+ # Defines the products names.
129
+ # IMPORTANT: products are built in the provided order, and they must be
130
+ # valid instance methods name for a Scenario object (hence user defined).
83
131
  def produces(*args)
84
132
  self.products = [*args].flatten
85
133
  end
@@ -88,17 +136,34 @@ module Laborantin
88
136
  "#{self.name}:\n\t#{self.description}\n#{self.parameters}"
89
137
  end
90
138
 
139
+ # Returns all the known subklasses of Scenario.
91
140
  def all
92
141
  @@all
93
142
  end
94
143
 
144
+ # Returns the path where the results of the instances of this Scenario
145
+ # will be stored given that we are in the env Environment. If env is nil,
146
+ # will use '.' as rootdir for the Scenario results.
95
147
  def scenardir(env=nil)
96
148
  envdir = env.rundir || '.'
97
149
  File.join(envdir, self.name.duck_case)
98
150
  end
99
151
  end # class <<
100
152
 
101
- attr_accessor :params, :environment, :date, :rundir
153
+ # A hash of parameters for this run.
154
+ attr_accessor :params
155
+
156
+ # The environment in which we run this scenario.
157
+ attr_accessor :environment
158
+
159
+ # A date that holds the creation of the instance, it is not meaningful
160
+ # when an env was created by a call to Environment.scan_resdir.
161
+ # TODO better
162
+ attr_accessor :date
163
+
164
+ # An attribute that holds the directory where the config and the results
165
+ # are stored. Can be overridden (e.g. Scenario.scan_env does that).
166
+ attr_accessor :rundir
102
167
 
103
168
  def initialize(env, params={})
104
169
  @environment = env
@@ -107,6 +172,13 @@ module Laborantin
107
172
  @rundir = File.join(self.class.scenardir(environment), date_str)
108
173
  end
109
174
 
175
+ # In the following order:
176
+ # * Log some info in the environment
177
+ # * Creates the rundir to store the result and the config
178
+ # * Stores the configuration as well as the run date
179
+ # BEWARE : currently does not ensure unicity of rundir,
180
+ # so wait one sec between several runs of same Scenario
181
+ #
110
182
  def prepare!
111
183
  log(self.class.description, :info) unless self.class.description.empty?
112
184
  log self.params.inspect, :info
@@ -116,6 +188,15 @@ module Laborantin
116
188
  File.open( config_path, 'w') {|f| f.puts YAML.dump( [date, params] ) }
117
189
  end
118
190
 
191
+ # In the following order:
192
+ # * Calls the setup hooks
193
+ # * Logs some info
194
+ # * Creates the raw result file
195
+ # * Calls the run method (user defined)
196
+ # * ... for each yielded line, store it into the raw result file
197
+ # * once completed (or on error) closes the raw result file
198
+ # * Logs some info
199
+ # * Calls the teardown hooks
119
200
  def perform!
120
201
  call_hooks :setup
121
202
  log "Starting measurement"
@@ -128,6 +209,10 @@ module Laborantin
128
209
  call_hooks :teardown
129
210
  end
130
211
 
212
+ # For each product define with Scenario.produces, and in its order,
213
+ # create a file with a canonic name in the scenario rundir.
214
+ # Call the instance method which has the product name.
215
+ # Appends each yielded line from this method.
131
216
  def analyze!
132
217
  self.class.products.each do |name|
133
218
  log "Producing #{name}"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: laborantin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Di Cioccio Lucas
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-27 00:00:00 +02:00
12
+ date: 2009-07-28 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -40,7 +40,7 @@ files:
40
40
  - lib/laborantin/core/parameter_hash.rb
41
41
  - lib/laborantin/core/scenario.rb
42
42
  - lib/laborantin/core/monkey_patches.rb
43
- has_rdoc: false
43
+ has_rdoc: true
44
44
  homepage: http://rubyforge.org/projects/laborantin
45
45
  post_install_message:
46
46
  rdoc_options: []