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
@@ -0,0 +1,30 @@
1
+
2
+ module Laborantin
3
+ module Metaprog
4
+ module Completeable
5
+ # A block to propose completion on this option
6
+ attr_reader :completion_block
7
+
8
+ # Stores the block argument in the completion_block, usage is for DSLs
9
+ def complete(&blk)
10
+ @completion_block = blk
11
+ end
12
+
13
+
14
+ # Provides completion facility for comma-separated lists of args, and returns the propositions
15
+ # - removes items already in list
16
+ # - prepends commas for items not already in list
17
+ def completion_propositions_iterating_on(cmd, list)
18
+ envs_on_cli = cmd.split.last.split(',').reject{|s| s.start_with?('-')}
19
+ last_env_on_cli = envs_on_cli.last unless cmd.end_with?(',')
20
+ last_env_on_cli ||= ''
21
+ complete_envs_on_cli = envs_on_cli - [last_env_on_cli]
22
+
23
+ list = list.select{|str| str.start_with?(last_env_on_cli)}
24
+ candidate_envs = list - envs_on_cli
25
+ candidate_envs.map{|str| (complete_envs_on_cli + [str]).join(',') }
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+
2
+ module Laborantin
3
+ module Metaprog
4
+ module Configurable
5
+ # A hash placeholder for extra config (e.g. git revision for the git implementation)
6
+ attr_accessor :config
7
+
8
+ # saves the @config in a YAML config file
9
+ def save_config
10
+ File.open(config_path, 'w') do |f|
11
+ f.puts YAML.dump(config)
12
+ end
13
+ end
14
+
15
+ # restore the configuration from the config file
16
+ def load_config!(path=config_path)
17
+ if File.file?(path)
18
+ @config = YAML.load_file(path)
19
+ end
20
+ @config ||= {}
21
+ end
22
+
23
+ def config_path
24
+ "config.yaml"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+
2
+ module Laborantin
3
+ module Metaprog
4
+ module Datable
5
+ attr_accessor :date
6
+
7
+ # Format the date of as a string (rounded at 1second).
8
+ def date_str
9
+ date.strftime("%Y-%h-%d_%H-%M-%S")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module Laborantin
3
+ module Metaprog
4
+ module Describable
5
+ # A description used for printing summary and debug purposes.
6
+ attr_accessor :description
7
+
8
+ alias :describe :description=
9
+ end
10
+ end
11
+ end
@@ -21,9 +21,15 @@ Copyright (c) 2009, Lucas Di Cioccio
21
21
 
22
22
  =end
23
23
 
24
- require 'time'
25
- require 'logger'
26
- require 'fileutils'
24
+ autoload :Time, 'time'
25
+ autoload :Logger, 'logger'
26
+ autoload :FileUtils, 'fileutils'
27
+
28
+ require 'laborantin/core/datable'
29
+ require 'laborantin/core/describable'
30
+ require 'laborantin/core/hookable'
31
+ require 'laborantin/core/configurable'
32
+ require 'laborantin/core/multi_name'
27
33
 
28
34
  module Laborantin
29
35
 
@@ -37,26 +43,38 @@ module Laborantin
37
43
  # If you want to do that, you must know that Environment @@all class variable
38
44
  # holds a reference to every child class from Environment.
39
45
  class Environment
46
+ include Metaprog::Datable
47
+ include Metaprog::Configurable
48
+ extend Metaprog::Describable
49
+ extend Metaprog::Hookable
50
+ extend Metaprog::MultiName
51
+
40
52
  @@all = []
41
53
 
42
54
  # Populates loaded (i.e. put in @@all class variable when self.inherited
43
55
  # is called) environment classes from existing results that are stored in
44
56
  # the dir parameter.
45
57
  def self.scan_resdir(dir)
46
- ret = []
58
+ list = []
47
59
  Dir.entries(dir).each do |f|
48
- envklass = Laborantin::Environment.all.find{|e| e.name.duck_case == f}
60
+ envklass = Laborantin::Environment.all.find{|e| e.fs_name == f}
49
61
  if envklass
50
62
  Dir.entries(envklass.envdir).each do |e|
51
63
  if e =~ /\d+-\w+-\d+_\d+-\d+-\d+/
52
- env = envklass.new #XXX don't prepare! it hence don't overwrite logs
53
- env.rundir = File.join(envklass.envdir, e)
54
- ret << env
64
+ env = envklass.new_loading_from_dir(File.join(envklass.envdir, e))
65
+ list << env
55
66
  end
56
67
  end
57
68
  end
58
69
  end
59
- ret
70
+ list
71
+ end
72
+
73
+ def self.new_loading_from_dir(path)
74
+ obj = self.new
75
+ obj.load_config!
76
+ obj.rundir = path
77
+ obj
60
78
  end
61
79
 
62
80
  class << self
@@ -66,15 +84,6 @@ module Laborantin
66
84
  # CURRENTLY NOT HERITED
67
85
  attr_accessor :verifications
68
86
 
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
73
-
74
- # A hash to store setup/teardown hooks.
75
- # CURRENTLY NOT HERITED
76
- attr_accessor :hooks
77
-
78
87
  # Prepares attributes' default values whenever a subclass is created.
79
88
  def inherited(klass)
80
89
  klass.verifications = []
@@ -83,40 +92,20 @@ module Laborantin
83
92
  @@all << klass
84
93
  end
85
94
 
86
- # Registers new verifiers.
95
+ # Registers new verifiers methods that will be verified at beginning.
87
96
  def verify(*args)
88
97
  self.verifications = [*args].flatten
89
98
  end
90
99
 
91
- # Sets the description.
92
- def describe(str)
93
- self.description = str
94
- end
95
-
96
- # Registers setup hooks, called before any scenario is instantiated.
97
- def setup(*args)
98
- self.hooks[:setup] = [*args].flatten
99
- end
100
-
101
- # Registers teardown hooks, called after every scenarii has been
102
- # performed and analyzed.
103
- def teardown(*args)
104
- self.hooks[:teardown] = [*args].flatten
105
- end
106
-
107
- def to_s
108
- "#{self.name}:\n\t#{self.description}"
109
- end
110
-
111
100
  # Returns all the known subklasses of Environment.
112
101
  def all
113
102
  @@all
114
103
  end
115
104
 
116
105
  # The path where the results for instance of a subklass of Environment
117
- # are stored (needs the Laborantin.resultdir).
106
+ # are stored (needs the Runner's resultdir).
118
107
  def envdir
119
- File.join(Laborantin.resultdir, self.name.duck_case)
108
+ File.join(Runner.instance.resultdir, self.fs_name)
120
109
  end
121
110
  end
122
111
 
@@ -124,28 +113,76 @@ module Laborantin
124
113
  # are stored. Can be overridden (e.g. Environment.scan_resdir does that).
125
114
  attr_accessor :rundir
126
115
 
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
116
  # An array of loggers objects.
133
117
  attr_accessor :loggers
134
118
 
135
- def initialize
119
+ # The (optional) commmand instanciating this environment
120
+ attr_accessor :command
121
+
122
+ # Initializes a new instance:
123
+ # the date is Time.now
124
+ # the rundir is the Environment.envdir followed by the date_str
125
+ # the loggers contains an empty Array
126
+ # Does NOT create any directory, so the accessors can be overwritten if needed.
127
+ def initialize(command=nil)
128
+ @command = command
136
129
  @date = Time.now
137
130
  @rundir = File.join(self.class.envdir, date_str)
138
131
  @loggers = []
132
+ @config = {}
139
133
  end
140
134
 
135
+ def runner
136
+ command.runner if command
137
+ end
138
+
139
+ # sends all the methods registered in Environment.verify
140
+ # returns the method symbol of the failed verification if any
141
141
  def valid?
142
142
  self.class.verifications.find{|v| not send(v)}.nil?
143
143
  end
144
144
 
145
+ # complete path to the environment.log file
145
146
  def logfile_path
146
147
  File.join(rundir, 'environment.log')
147
148
  end
148
149
 
150
+ # complete path to the config.yaml file
151
+ def config_path
152
+ File.join(rundir, 'config.yaml')
153
+ end
154
+
155
+ def scenarii_dirs
156
+ config[:scenarii_dirs]
157
+ end
158
+
159
+ def record_scenario_dir(dir, save=false)
160
+ config[:scenarii_dirs] ||= []
161
+ config[:scenarii_dirs] << dir
162
+ save_config if save
163
+ end
164
+
165
+ # gets the state of the environment
166
+ def state
167
+ config[:state]
168
+ end
169
+
170
+ # changes the state of the environment
171
+ def state=(val, save=true)
172
+ config[:state] = val
173
+ save_config if save
174
+ end
175
+
176
+ # returns if the environment succeeded
177
+ def successful?
178
+ config[:state] == :success
179
+ end
180
+
181
+ # returns if the environment exited with error
182
+ def failed?
183
+ config[:state] == :error
184
+ end
185
+
149
186
  # In the following order:
150
187
  # * Creates the envdir if needed.
151
188
  # * Adds some loggers.
@@ -159,6 +196,8 @@ module Laborantin
159
196
  @loggers << Logger.new(STDOUT)
160
197
  log(self.class.description, :info) unless self.class.description.empty?
161
198
  log "Directories prepared"
199
+ log "Writing config file"
200
+ save_config
162
201
  call_hooks :setup
163
202
  end
164
203
 
@@ -178,15 +217,14 @@ module Laborantin
178
217
  Laborantin::Scenario.scan_env(self)
179
218
  end
180
219
 
181
- def date_str
182
- date.strftime("%Y-%h-%d_%H-%M-%S")
183
- end
184
-
185
220
  private
186
221
 
187
222
  def call_hooks(name)
188
223
  log "Calling #{name} hooks"
189
- self.class.hooks[name].each{|sym| send sym}
224
+ self.class.hooks[name].each do |sym|
225
+ log "(#{sym})"
226
+ send sym
227
+ end
190
228
  end
191
229
 
192
230
  end # class
@@ -0,0 +1,20 @@
1
+
2
+ module Laborantin
3
+ module Metaprog
4
+ module Hookable
5
+ # A hash to store setup/teardown hooks.
6
+ attr_accessor :hooks
7
+
8
+ # Registers setup hooks.
9
+ def setup(*args)
10
+ hooks[:setup] = [*args].flatten
11
+ end
12
+
13
+ # Register teardown hooks.
14
+ def teardown(*args)
15
+ hooks[:teardown] = [*args].flatten
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -35,4 +35,3 @@ class String
35
35
  self.gsub(/([A-Z])/){|s| "_#{$1.downcase}"}.sub(/^_/,'')
36
36
  end
37
37
  end
38
-
@@ -0,0 +1,25 @@
1
+
2
+ module Laborantin
3
+ module Metaprog
4
+ module MultiName
5
+ AVAILABLE_NAMES = [:cli, :fs]
6
+
7
+ def set_name(sym, val)
8
+ raise ArgumentError, "invalid name sym: #{sym}, expected in #{AVAILABLE_NAMES.inspect}" unless AVAILABLE_NAMES.include?(sym)
9
+ send "#{sym}_name=", val
10
+ end
11
+
12
+ # a way to name on the command line
13
+ attr_writer :cli_name
14
+ def cli_name
15
+ @cli_name || name.duck_case
16
+ end
17
+
18
+ # a way to name on the filesystem
19
+ attr_writer :fs_name
20
+ def fs_name
21
+ @fs_name || name.duck_case
22
+ end
23
+ end
24
+ end
25
+ end
@@ -21,19 +21,20 @@ Copyright (c) 2009, Lucas Di Cioccio
21
21
 
22
22
  =end
23
23
 
24
+ require 'laborantin/core/describable'
25
+
24
26
  module Laborantin
25
27
 
26
28
  # A ParameterRange instance is more or less a wrapper over an Array of allowed values.
27
29
  class ParameterRange
28
30
 
31
+ include Metaprog::Describable
32
+
29
33
  # The name of the parameter (should be unique in a Scenario's parameters)
30
34
  # Usually a symbol.
31
35
  attr_accessor :name
32
36
 
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
-
37
+ # initialize a new instance with the desired name
37
38
  def initialize(name)
38
39
  @name = name
39
40
  @values = []
@@ -54,13 +55,5 @@ module Laborantin
54
55
  end
55
56
  end
56
57
 
57
- # Sets the description.
58
- def describe(str)
59
- @description = str
60
- end
61
-
62
- def to_s
63
- "#{values.inspect}\n\t\t#{@description}"
64
- end
65
58
  end
66
59
  end
@@ -40,8 +40,12 @@ module Laborantin
40
40
  end
41
41
  end
42
42
 
43
- def to_s
44
- keys.inject(''){|s,k| s + "\t- #{k}: #{self[k]}.\n"}
43
+ def each_config_with_index
44
+ idx = 0
45
+ each_config do |cfg|
46
+ yield cfg, idx
47
+ idx += 1
48
+ end
45
49
  end
46
50
  end
47
51
  end
@@ -21,11 +21,18 @@ Copyright (c) 2009, Lucas Di Cioccio
21
21
 
22
22
  =end
23
23
 
24
- require File.join(File.dirname(__FILE__), 'parameter')
25
- require File.join(File.dirname(__FILE__), 'parameter_hash')
24
+ require 'laborantin/core/parameter'
25
+ require 'laborantin/core/parameter_hash'
26
26
 
27
- require 'fileutils'
28
- require 'yaml'
27
+ autoload :FileUtils, 'fileutils'
28
+ autoload :YAML, 'yaml'
29
+
30
+ require 'laborantin/core/datable'
31
+ require 'laborantin/core/describable'
32
+ require 'laborantin/core/hookable'
33
+ require 'laborantin/core/configurable'
34
+ require 'laborantin/core/multi_name'
35
+ require 'laborantin/core/table'
29
36
 
30
37
  module Laborantin
31
38
 
@@ -41,42 +48,45 @@ module Laborantin
41
48
  # Like the Environment, all the subklasses will be stored in a @@all
42
49
  # class variable for convenience purpose.
43
50
  class Scenario
51
+ include Metaprog::Datable
52
+ include Metaprog::Configurable
53
+ extend Metaprog::Describable
54
+ extend Metaprog::Hookable
55
+ extend Metaprog::MultiName
56
+
44
57
  @@all = []
45
58
 
46
59
  # 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.
60
+ # It will set their configuration (i.e. run date and parameters hash)
61
+ # according to the stored config.yaml in YAML format. Returns an array of
62
+ # such built scenarii.
50
63
  def self.scan_env(env)
51
- scs = []
64
+ list = []
52
65
  Dir.entries(env.rundir).each do |s|
53
- scklass = Laborantin::Scenario.all.find{|t| t.name.duck_case == s}
66
+ scklass = Laborantin::Scenario.all.find{|t| t.fs_name == s}
54
67
  if scklass
55
68
  Dir.entries(scklass.scenardir(env)).each do |r|
56
69
  if r =~ /\d+-\w+-\d+_\d+-\d+-\d+/
57
- scenar = scklass.new(env)
58
- scs << scenar
59
- scenar.rundir = File.join(scklass.scenardir(env), r)
60
- tst, params = YAML.load_file(File.join(scenar.rundir, 'config.yaml'))
61
- scenar.params = params
70
+ scenar = scklass.new_loading_from_dir(env, File.join(scklass.scenardir(env), r))
71
+ list << scenar
62
72
  end
63
73
  end
64
74
  end
65
75
  end
66
- scs
76
+ list
67
77
  end
68
78
 
69
- class << self
70
-
71
- # A description used for printing summary and debug purposes. Will be
72
- # used to create .tex report in the future.
73
- # CURRENTLY NOT HERITED
74
- attr_accessor :description
75
-
76
- # A hash to store setup/teardown hooks.
77
- # CURRENTLY NOT HERITED
78
- attr_accessor :hooks
79
+ def self.new_loading_from_dir(env, path)
80
+ obj = self.new(env)
81
+ yml_path = File.join(path, 'config.yaml')
82
+ tst, params = obj.load_config!(yml_path)
83
+ obj.params = params
84
+ obj.date = tst
85
+ obj.rundir = path
86
+ obj
87
+ end
79
88
 
89
+ class << self
80
90
  # The set of parameters that will vary for this Scenario.
81
91
  attr_accessor :parameters
82
92
 
@@ -95,21 +105,6 @@ module Laborantin
95
105
  @@all << klass
96
106
  end
97
107
 
98
- # Sets the description.
99
- def describe(str)
100
- self.description = str
101
- end
102
-
103
- # Registers setup hooks.
104
- def setup(*args)
105
- self.hooks[:setup] = [*args].flatten
106
- end
107
-
108
- # Register teardown hooks.
109
- def teardown(*args)
110
- self.hooks[:teardown] = [*args].flatten
111
- end
112
-
113
108
  # Defines a new ParameterRange instance for this Scenario.
114
109
  # A block should be passed that will be evaluated in this
115
110
  # ParameterRange instance's context.
@@ -133,10 +128,6 @@ module Laborantin
133
128
  self.products = [*args].flatten
134
129
  end
135
130
 
136
- def to_s
137
- "#{self.name}:\n\t#{self.description}\n#{self.parameters}"
138
- end
139
-
140
131
  # Returns all the known subklasses of Scenario.
141
132
  def all
142
133
  @@all
@@ -147,7 +138,7 @@ module Laborantin
147
138
  # will use '.' as rootdir for the Scenario results.
148
139
  def scenardir(env=nil)
149
140
  envdir = env.rundir || '.'
150
- File.join(envdir, self.name.duck_case)
141
+ File.join(envdir, self.fs_name)
151
142
  end
152
143
  end # class <<
153
144
 
@@ -157,19 +148,20 @@ module Laborantin
157
148
  # The environment in which we run this scenario.
158
149
  attr_accessor :environment
159
150
 
160
- # A date that holds the creation of the instance, it is not meaningful
161
- # when an env was created by a call to Environment.scan_resdir.
162
- # TODO better
163
- attr_accessor :date
164
-
165
151
  # An attribute that holds the directory where the config and the results
166
152
  # are stored. Can be overridden (e.g. Scenario.scan_env does that).
167
153
  attr_accessor :rundir
168
154
 
155
+ # Initializes a new instance contains in the env Environment, and
156
+ # for the parameter set params.
157
+ # Sets the date to Time.now for unicity (with 1sec granularity)
158
+ # Sets the rundir accessor in the directory.
159
+ # Does NOT create any directory, so the accessors can be overwritten if needed.
169
160
  def initialize(env, params={})
170
161
  @environment = env
171
162
  @params = params
172
163
  @date = Time.now
164
+ @config = {}
173
165
  @rundir = File.join(self.class.scenardir(environment), date_str)
174
166
  end
175
167
 
@@ -185,8 +177,10 @@ module Laborantin
185
177
  log self.params.inspect, :info
186
178
  log "Preparing directory #{rundir}"
187
179
  FileUtils.mkdir_p(rundir) #TODO: ensure unicity
180
+ environment.record_scenario_dir(rundir, true)
188
181
  log "Storing configuration in YAML format"
189
- File.open( config_path, 'w') {|f| f.puts YAML.dump( [date, params] ) }
182
+ @config = [date, params]
183
+ save_config
190
184
  end
191
185
 
192
186
  # In the following order:
@@ -216,53 +210,82 @@ module Laborantin
216
210
  # Appends each yielded line from this method.
217
211
  def analyze!
218
212
  self.class.products.each do |name|
219
- log "Producing #{name}"
213
+ log "(#{name})"
220
214
  product_file(name.to_s, 'w') do |f|
221
- send name do |l|
215
+ send(name) do |l|
222
216
  f.puts l
223
217
  end
224
218
  end
225
- log "Product #{name} done"
226
219
  end
227
220
  end
228
221
 
229
- def date_str
230
- date.strftime("%Y-%h-%d_%H-%M-%S")
231
- end
232
-
233
- private
234
-
235
- def call_hooks(name)
236
- log "Calling #{name} hooks"
237
- self.class.hooks[name].each{|sym| send sym}
238
- end
239
-
240
- def log(*args)
241
- environment.log *args
242
- end
243
-
222
+ # Returns the absolute path to a product file (see File.join) If brutname
223
+ # is true, then resultname is appended to the rundir of the scenario. If
224
+ # brutname is false, then before being appended to the rundir of the
225
+ # scenario, the name is surronded by result.<resultname>.txt
226
+ #
227
+ # The idea behind this is to avoid name collisions between simple users of
228
+ # Laborantin, and people developping extensions or modules.
244
229
  def product_path(resultname, brutname=false)
245
230
  resultname = "result.#{resultname}.txt" unless brutname
246
231
  File.join(rundir, resultname)
247
232
  end
248
233
 
234
+ # Yields an open file for a given product, will make sure it is closed.
235
+ # mode is the mode in which the file is opened (you should leave it to 'r')
236
+ # see the doc for product_path to understand the role of brutname
249
237
  def product_file(resultname, mode='r', brutname=false)
250
238
  File.open(product_path(resultname, brutname), mode) do |f|
251
239
  yield f
252
240
  end
253
241
  end
254
242
 
243
+ # The path to the config.yaml file that holds the scenario parameters.
255
244
  def config_path
256
245
  product_path('config.yaml', true)
257
246
  end
258
247
 
248
+ # The path to the "raw result", i.e. the one built when you yield in the
249
+ # run method.
259
250
  def raw_result_path
260
251
  product_path('result.raw', true)
261
252
  end
262
253
 
254
+ # Yield the opened raw result file, will close it afterwards.
255
+ # mode is the mode in which the file is opened (default to 'r')
256
+ # never open the file in another mode, unless you know what you're doing,
257
+ # because this file most likely contains the value of your work, i.e., your
258
+ # data.
263
259
  def raw_result_file(mode='r')
264
- product_file('result.raw', mode, true){|f| yield f}
260
+ product_file('result.raw', mode, true) do |f|
261
+ yield f
262
+ end
263
+ end
264
+
265
+ def table(name, struct)
266
+ Table.new(name, struct, self.product_path(name))
265
267
  end
266
268
 
269
+ private
270
+
271
+ def call_hooks(name)
272
+ log "Calling #{name} hooks"
273
+ self.class.hooks[name].each do |sym|
274
+ log "(#{sym})"
275
+ send sym
276
+ end
277
+ end
278
+
279
+ def log(*args)
280
+ environment.log *args
281
+ end
282
+
283
+ def command
284
+ environment.command
285
+ end
286
+
287
+ def runner
288
+ environment.runner
289
+ end
267
290
  end # class
268
291
  end