pione 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/Rakefile +13 -0
- data/bin/pione +5 -0
- data/bin/pione-compiler +5 -0
- data/example/ScoreAggregation/ScoreAggregation.pione +2 -0
- data/example/ScoreAggregation/bin/apply-template.rb +1 -1
- data/example/ScoreAggregation/bin/mean-summary.rb +1 -1
- data/example/ScoreAggregation/bin/total-mean.rb +1 -1
- data/example/ScoreAggregation/bin/total-statistics.rb +3 -3
- data/example/ScoreAggregation/pione-package.json +21 -0
- data/example/ScoreAggregation/scenario/case1/Scenario.pione +1 -0
- data/example/ScoreAggregation/scenario/case1/pione-scenario.json +36 -0
- data/lib/pione.rb +15 -2
- data/lib/pione/agent/basic-agent.rb +0 -1
- data/lib/pione/agent/job-terminator.rb +8 -3
- data/lib/pione/agent/task-worker.rb +9 -2
- data/lib/pione/command.rb +2 -0
- data/lib/pione/command/basic-command.rb +4 -0
- data/lib/pione/command/option.rb +4 -1
- data/lib/pione/command/pione-clean.rb +104 -17
- data/lib/pione/command/pione-client.rb +16 -3
- data/lib/pione/command/pione-command.rb +45 -0
- data/lib/pione/command/pione-compiler.rb +39 -0
- data/lib/pione/command/pione-log.rb +1 -0
- data/lib/pione/command/pione-task-worker.rb +6 -2
- data/lib/pione/command/pione-val.rb +1 -0
- data/lib/pione/front.rb +1 -0
- data/lib/pione/front/basic-front.rb +1 -1
- data/lib/pione/front/front-exception.rb +5 -1
- data/lib/pione/global.rb +3 -118
- data/lib/pione/global/broker-variable.rb +21 -8
- data/lib/pione/global/client-variable.rb +16 -8
- data/lib/pione/{system → global}/config.rb +2 -2
- data/lib/pione/global/global-exception.rb +40 -0
- data/lib/pione/global/input-generator-variable.rb +4 -1
- data/lib/pione/global/item.rb +168 -0
- data/lib/pione/global/log-variable.rb +64 -13
- data/lib/pione/global/network-variable.rb +4 -1
- data/lib/pione/global/package-variable.rb +25 -10
- data/lib/pione/global/path-variable.rb +62 -26
- data/lib/pione/global/relay-variable.rb +101 -36
- data/lib/pione/global/system-variable.rb +26 -9
- data/lib/pione/global/task-worker-variable.rb +19 -7
- data/lib/pione/global/tuple-space-notifier-variable.rb +60 -20
- data/lib/pione/lang.rb +0 -2
- data/lib/pione/lang/data-expr.rb +8 -0
- data/lib/pione/package/package-exception.rb +1 -1
- data/lib/pione/rule-engine/action-handler.rb +14 -12
- data/lib/pione/rule-engine/engine-exception.rb +11 -0
- data/lib/pione/rule-engine/flow-handler.rb +20 -11
- data/lib/pione/system.rb +1 -1
- data/lib/pione/system/file-cache.rb +1 -1
- data/lib/pione/system/init.rb +13 -15
- data/lib/pione/system/status.rb +30 -0
- data/lib/pione/system/system-exception.rb +0 -24
- data/lib/pione/test-helper.rb +8 -1
- data/lib/pione/test-helper/command-helper.rb +42 -15
- data/lib/pione/util.rb +1 -0
- data/lib/pione/util/embeded-expr-expander.rb +5 -11
- data/lib/pione/util/pnml-compiler.rb +97 -0
- data/lib/pione/util/profiler.rb +107 -0
- data/lib/pione/version.rb +1 -1
- data/man/pione-clean.1 +76 -0
- data/pione.gemspec +5 -2
- data/test/agent/spec_job-terminator.rb +41 -3
- data/test/command/data/pione-client/ActionError.pione +5 -0
- data/test/command/data/pione-compiler/Sequence.pnml +137 -0
- data/test/command/spec_pione-clean.rb +250 -5
- data/test/command/spec_pione-client.rb +57 -46
- data/test/command/spec_pione-command.rb +18 -0
- data/test/command/spec_pione-compiler.rb +13 -0
- data/test/global/spec_item.rb +77 -0
- data/test/lang/data/data-expr.yml +5 -0
- data/test/rule-engine/{spec_action-handler.pione → data/action-handler/BasicAction.pione} +0 -0
- data/test/rule-engine/data/action-handler/UsePackageScript.pione +17 -0
- data/test/rule-engine/spec_action-handler.rb +143 -80
- data/test/system/spec_status.rb +13 -0
- data/test/{tuple → tuple-space}/spec_basic-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/spec_data-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/spec_finished-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/spec_message-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/spec_task-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/spec_touch-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/spec_working-tuple.rb +0 -0
- data/test/{tuple → tuple-space}/tuple-behavior.rb +0 -0
- data/test/util/data/pnml/Sequence.pnml +137 -0
- data/test/util/spec_pnml-compiler.rb +21 -0
- data/test/util/spec_profiler.rb +53 -0
- metadata +75 -32
- data/example/ScoreAggregation/package.yml +0 -5
- data/example/ScoreAggregation/scenario/case1/scenario.yml +0 -29
- data/lib/pione/lang/interpolator-parser.rb +0 -44
- data/lib/pione/lang/interpolator-transformer.rb +0 -13
- data/test/lang/spec_interpolator-parser.rb +0 -5
- data/test/lang/spec_interpolator-transformer.rb +0 -15
@@ -16,6 +16,17 @@ module Pione
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class ActionError < StandardError
|
20
|
+
def initialize(digest, report)
|
21
|
+
@digest = digest
|
22
|
+
@report = report
|
23
|
+
end
|
24
|
+
|
25
|
+
def message
|
26
|
+
"Action rule %s has errored:\n%s" % [@digest, @report]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
19
30
|
class InvalidOutputError < RuleExecutionError
|
20
31
|
def initialize(handler, outputs)
|
21
32
|
super(handler)
|
@@ -171,6 +171,7 @@ module Pione
|
|
171
171
|
def initialize(handler)
|
172
172
|
super(handler)
|
173
173
|
@data_finder = DataFinder.new(tuple_space_server, domain_id)
|
174
|
+
@finished = [] # finished tuple cache
|
174
175
|
end
|
175
176
|
|
176
177
|
# Apply input data to rules.
|
@@ -178,9 +179,12 @@ module Pione
|
|
178
179
|
# start message
|
179
180
|
user_message_begin("Rule Application: %s" % digest, 1)
|
180
181
|
|
181
|
-
#
|
182
|
-
|
183
|
-
|
182
|
+
# with profile
|
183
|
+
Util::Profiler.profile(Util::RuleApplicationProfileReport.new(digest)) do
|
184
|
+
# rule application loop
|
185
|
+
while tasks = find_tasks(rules) do
|
186
|
+
distribute_tasks(tasks)
|
187
|
+
end
|
184
188
|
end
|
185
189
|
|
186
190
|
# end message
|
@@ -337,9 +341,9 @@ module Pione
|
|
337
341
|
# setup output variables
|
338
342
|
var_o = Lang::Variable.new("O")
|
339
343
|
task.env.variable_set(Lang::Variable.new("OUTPUT"), var_o)
|
340
|
-
|
341
|
-
task.env.variable_set(var_o,
|
342
|
-
param_set = param_set.set(table: param_set.table.merge({"O" =>
|
344
|
+
kseq = find_output_variables(task, Lang::KeyedSequence.new)
|
345
|
+
task.env.variable_set(var_o, kseq)
|
346
|
+
param_set = param_set.set(table: param_set.table.merge({"O" => kseq}))
|
343
347
|
|
344
348
|
return task.set(order: order, env: env, param_set: param_set)
|
345
349
|
else
|
@@ -347,17 +351,17 @@ module Pione
|
|
347
351
|
end
|
348
352
|
end
|
349
353
|
|
350
|
-
def find_output_variables(task,
|
351
|
-
|
354
|
+
def find_output_variables(task, kseq)
|
355
|
+
_kseq = kseq
|
352
356
|
task.rule_condition.outputs.each_with_index do |condition, i|
|
353
357
|
begin
|
354
358
|
data = condition.eval(task.env)
|
355
|
-
|
359
|
+
_kseq = _kseq.put(Lang::IntegerSequence.of(i+1), data)
|
356
360
|
rescue Lang::UnboundError
|
357
361
|
next
|
358
362
|
end
|
359
363
|
end
|
360
|
-
return
|
364
|
+
return _kseq
|
361
365
|
end
|
362
366
|
|
363
367
|
# Distribute tasks.
|
@@ -410,7 +414,12 @@ module Pione
|
|
410
414
|
def need_to_publish_task?(task, tuple)
|
411
415
|
# reuse task finished result if order is weak update
|
412
416
|
if task.order == :weak
|
413
|
-
|
417
|
+
template = TupleSpace::FinishedTuple.new(domain: task.domain_id, status: :succeeded)
|
418
|
+
if @finished.include?(template)
|
419
|
+
return false
|
420
|
+
end
|
421
|
+
if finished = read!(template)
|
422
|
+
@finished << finished
|
414
423
|
return false
|
415
424
|
end
|
416
425
|
end
|
data/lib/pione/system.rb
CHANGED
@@ -5,10 +5,10 @@ end
|
|
5
5
|
|
6
6
|
require 'pione/system/object'
|
7
7
|
require 'pione/system/common'
|
8
|
-
require 'pione/system/config'
|
9
8
|
require 'pione/system/init'
|
10
9
|
require 'pione/system/file-cache'
|
11
10
|
require 'pione/system/domain-info'
|
11
|
+
require 'pione/system/status'
|
12
12
|
|
13
13
|
# export some classes to the name space of Pione
|
14
14
|
Pione.module_exec {const_set(:PioneObject, Pione::System::PioneObject)}
|
@@ -135,7 +135,7 @@ module Pione
|
|
135
135
|
def get(location)
|
136
136
|
# cache if record doesn't exist
|
137
137
|
unless @table.has_key?(location)
|
138
|
-
cache_location = Location[
|
138
|
+
cache_location = Location[Global.file_cache_path_generator.create]
|
139
139
|
location.turn(cache_location)
|
140
140
|
@table[location] = cache_location
|
141
141
|
end
|
data/lib/pione/system/init.rb
CHANGED
@@ -5,27 +5,25 @@ module Pione
|
|
5
5
|
# turn on "abort on exception" mode
|
6
6
|
Thread.abort_on_exception = true
|
7
7
|
|
8
|
-
# load configration file
|
9
|
-
|
8
|
+
# load configration file for global system
|
9
|
+
Global::Config.load(Global.config_path)
|
10
10
|
|
11
11
|
# make temporary directories
|
12
|
-
unless Global.temporary_directory_root.exist?
|
13
|
-
Global.temporary_directory_root.mkdir(0777)
|
14
|
-
end
|
15
12
|
unless Global.temporary_directory.exist?
|
16
|
-
Global.temporary_directory.mkdir(
|
17
|
-
end
|
18
|
-
unless Global.working_directory_root.exist?
|
19
|
-
Global.working_directory_root.mkdir(0777)
|
13
|
+
Global.temporary_directory.mkdir(0777)
|
20
14
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
15
|
+
|
16
|
+
# setup default temporary path generator
|
17
|
+
Temppath.update_basedir(Global.my_temporary_directory + "others_%s" % Util::UUID.generate)
|
18
|
+
|
19
|
+
# make file cache directory
|
20
|
+
unless Global.file_cache_directory.exist?
|
21
|
+
Global.file_cache_directory.mkdir(0777)
|
26
22
|
end
|
23
|
+
|
24
|
+
# make my file cache directory
|
27
25
|
unless Global.file_cache_directory.exist?
|
28
|
-
Global.file_cache_directory.mkdir(
|
26
|
+
Global.file_cache_directory.mkdir(0777)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Pione
|
2
|
+
module System
|
3
|
+
class Status
|
4
|
+
class << self
|
5
|
+
def success
|
6
|
+
new(:success)
|
7
|
+
end
|
8
|
+
|
9
|
+
def error(exception)
|
10
|
+
new(:error, exception)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :exception
|
15
|
+
|
16
|
+
def initialize(status, exception=nil)
|
17
|
+
@status = status
|
18
|
+
@exception = exception
|
19
|
+
end
|
20
|
+
|
21
|
+
def success?
|
22
|
+
@status == :success
|
23
|
+
end
|
24
|
+
|
25
|
+
def error?
|
26
|
+
@status == :error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,29 +1,5 @@
|
|
1
1
|
module Pione
|
2
2
|
module System
|
3
3
|
class SystemException < StandardError; end
|
4
|
-
|
5
|
-
# This exception class is raised when configuration file is in invalid format.
|
6
|
-
class InvalidConfigFile < SystemException
|
7
|
-
# config file path
|
8
|
-
attr_reader :path
|
9
|
-
|
10
|
-
def initialize(path)
|
11
|
-
@path = path
|
12
|
-
end
|
13
|
-
|
14
|
-
def message
|
15
|
-
"invalid format configuration file: %s" % @path
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class UnconfigurableVariableError < SystemException
|
20
|
-
def initialize(name)
|
21
|
-
@name = name
|
22
|
-
end
|
23
|
-
|
24
|
-
def message
|
25
|
-
"global variable \"%s\" is unconfigurable." % @name
|
26
|
-
end
|
27
|
-
end
|
28
4
|
end
|
29
5
|
end
|
data/lib/pione/test-helper.rb
CHANGED
@@ -15,7 +15,14 @@ module Pione
|
|
15
15
|
@scope_id = (@scope_id || 0) + 1
|
16
16
|
mod = Module.new
|
17
17
|
const_set("MODULE%s" % @scope_id, mod)
|
18
|
-
mod.
|
18
|
+
mod.send(:define_method, :this) do
|
19
|
+
mod
|
20
|
+
end
|
21
|
+
mod.module_eval(&b)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.scope_of(mod)
|
25
|
+
eval(mod.name.split("::").reverse.drop(1).reverse.join("::"))
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -1,16 +1,21 @@
|
|
1
1
|
module Pione
|
2
2
|
module TestHelper
|
3
|
+
# CommandResult is a result of command execution. This has result status,
|
4
|
+
# stdout, stdin, and etc, so you can analyze and check it.
|
3
5
|
class CommandResult < StructX
|
4
6
|
member :exception
|
5
7
|
member :stdout
|
6
8
|
member :stderr
|
7
9
|
|
10
|
+
# Return true if the command succeeded.
|
8
11
|
def success?
|
9
12
|
exception.kind_of?(SystemExit) and exception.success?
|
10
13
|
end
|
11
14
|
|
15
|
+
# Print the command result report.
|
12
16
|
def report
|
13
17
|
unless success?
|
18
|
+
puts "[FAIL]"
|
14
19
|
puts "ERROR: %s" % exception.message
|
15
20
|
exception.backtrace.each do |line|
|
16
21
|
puts "TRACE: %s" % line
|
@@ -23,40 +28,62 @@ module Pione
|
|
23
28
|
puts "STDERR:"
|
24
29
|
puts stderr.string[0..100]
|
25
30
|
end
|
31
|
+
else
|
32
|
+
puts "[SUCCESS]"
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
37
|
+
# This module helps tests of command execution.
|
30
38
|
module Command
|
31
39
|
class << self
|
32
|
-
|
40
|
+
# Run the action with expectation command execution succeeds.
|
41
|
+
def succeed(*options, &b)
|
42
|
+
res = execute(options, &b)
|
43
|
+
res.should.success
|
44
|
+
return res
|
45
|
+
end
|
46
|
+
|
47
|
+
# Run the action with expectation command execution fails.
|
48
|
+
def fail(*options, &b)
|
49
|
+
res = execute(options, &b)
|
50
|
+
res.should.not.success
|
51
|
+
return res
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Run the command execution action.
|
57
|
+
def execute(options, &b)
|
33
58
|
# initialize exit status
|
34
59
|
Global.exit_status = true
|
35
60
|
|
36
61
|
# make result
|
37
62
|
res = CommandResult.new(stdout: StringIO.new("", "w"), stderr: StringIO.new("", "w"))
|
38
|
-
|
39
|
-
|
63
|
+
|
64
|
+
# setup stdout and stderr
|
65
|
+
$stdout = res.stdout unless options.include?(:show) or options.include?(:show_stdout)
|
66
|
+
$stderr = res.stderr unless options.include?(:show) or options.include?(:show_stderr)
|
67
|
+
|
68
|
+
# run the action
|
40
69
|
begin
|
41
70
|
b.call
|
42
71
|
rescue Object => e
|
43
|
-
|
72
|
+
unless options.include?(:show_exception)
|
73
|
+
res.exception = e
|
74
|
+
else
|
75
|
+
raise
|
76
|
+
end
|
44
77
|
end
|
78
|
+
|
79
|
+
# revert stdout and stderr
|
45
80
|
$stdout = STDOUT
|
46
81
|
$stderr = STDERR
|
47
|
-
return res
|
48
|
-
end
|
49
82
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
res.should.success
|
54
|
-
return res
|
55
|
-
end
|
83
|
+
if options.include?(:report)
|
84
|
+
res.report
|
85
|
+
end
|
56
86
|
|
57
|
-
def fail(&b)
|
58
|
-
res = execute(&b)
|
59
|
-
res.should.not.success
|
60
87
|
return res
|
61
88
|
end
|
62
89
|
end
|
data/lib/pione/util.rb
CHANGED
@@ -23,4 +23,5 @@ require 'pione/util/positionable' # source position handler
|
|
23
23
|
require 'pione/util/embeded-expr-expander' # expand text embeded PIONE expression
|
24
24
|
require 'pione/util/free-thread-generator' # generate threads free from thread group
|
25
25
|
require 'pione/util/parslet-extension' # parslet extension
|
26
|
+
require 'pione/util/pnml-compiler' # toy compiler from PNML to PIONE
|
26
27
|
|
@@ -6,17 +6,11 @@ module Pione
|
|
6
6
|
return nil if str.nil?
|
7
7
|
|
8
8
|
# parse and transform
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
if elt.is_a?(String)
|
15
|
-
elt
|
16
|
-
else
|
17
|
-
elt.eval(env).call_pione_method(env, "textize", []).value
|
18
|
-
end
|
19
|
-
end.join("")
|
9
|
+
str.gsub(/\{(\$.+?)\}|\<\?\s*(.+?)\s*\?>/) do
|
10
|
+
tree = Lang::DocumentParser.new.expr.parse($1 || $2)
|
11
|
+
expr = Lang::DocumentTransformer.new.apply(tree, {package_name: nil, filename: nil})
|
12
|
+
expr.eval(env).call_pione_method(env, "textize", []).value
|
13
|
+
end
|
20
14
|
end
|
21
15
|
end
|
22
16
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Pione
|
2
|
+
module Util
|
3
|
+
class PNMLCompiler
|
4
|
+
def initialize(src)
|
5
|
+
@src = src
|
6
|
+
end
|
7
|
+
|
8
|
+
# Compile PNML file into PIONE document as a string.
|
9
|
+
def compile
|
10
|
+
doc = REXML::Document.new(@src.read)
|
11
|
+
rule_table = make_rule_table(doc)
|
12
|
+
io_table = make_io_table(doc)
|
13
|
+
input_table = make_input_table(doc, rule_table, io_table)
|
14
|
+
output_table = make_output_table(doc, rule_table, io_table)
|
15
|
+
rules = make_flow_rules(rule_table, input_table, output_table)
|
16
|
+
return textize_flow_rules(rules)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Make rule mapping table by collecting transitions and associating with its id and name.
|
22
|
+
def make_rule_table(doc)
|
23
|
+
rule_table = Hash.new
|
24
|
+
REXML::XPath.each(doc, "/pnml/net/transition") do |elt|
|
25
|
+
id = elt.attribute("id").value
|
26
|
+
name = REXML::XPath.first(elt, "name/text").texts.map{|text| text.value}.join("")
|
27
|
+
rule_table[id] = name
|
28
|
+
end
|
29
|
+
return rule_table
|
30
|
+
end
|
31
|
+
|
32
|
+
# Make input and output mapping table by collecting places and associating its id and name.
|
33
|
+
def make_io_table(doc)
|
34
|
+
io_table = Hash.new {|h, k| h[k] = []}
|
35
|
+
REXML::XPath.each(doc, "/pnml/net/place") do |elt|
|
36
|
+
id = elt.attribute("id").value
|
37
|
+
name = REXML::XPath.first(elt, "name/text").texts.map{|text| text.value}.join("")
|
38
|
+
io_table[id] << name
|
39
|
+
end
|
40
|
+
return io_table
|
41
|
+
end
|
42
|
+
|
43
|
+
# Make rule's input table by collecting arcs and associating its source and target.
|
44
|
+
def make_input_table(doc, rule_table, io_table)
|
45
|
+
input_table = Hash.new {|h, k| h[k] = []}
|
46
|
+
REXML::XPath.each(doc, "/pnml/net/arc") do |elt|
|
47
|
+
source = elt.attribute("source").value
|
48
|
+
target = elt.attribute("target").value
|
49
|
+
name = REXML::XPath.first(elt, "inscription/text").texts.map{|text| text.value}.join("")
|
50
|
+
input_table[rule_table[target]] << io_table[source]
|
51
|
+
end
|
52
|
+
return input_table
|
53
|
+
end
|
54
|
+
|
55
|
+
# Make rule's output table by collecting arcs and associating its source and target.
|
56
|
+
def make_output_table(doc, rule_table, io_table)
|
57
|
+
output_table = Hash.new {|h, k| h[k] = []}
|
58
|
+
REXML::XPath.each(doc, "/pnml/net/arc") do |elt|
|
59
|
+
source = elt.attribute("source").value
|
60
|
+
target = elt.attribute("target").value
|
61
|
+
name = REXML::XPath.first(elt, "inscription/text").texts.map{|text| text.value}.join("")
|
62
|
+
output_table[rule_table[source]] << io_table[target]
|
63
|
+
end
|
64
|
+
return output_table
|
65
|
+
end
|
66
|
+
|
67
|
+
# Make flow rules from rule table, input table, and output table.
|
68
|
+
def make_flow_rules(rule_table, input_table, output_table)
|
69
|
+
rules = Array.new
|
70
|
+
rule_table.each do |key, rule_name|
|
71
|
+
inputs = input_table[rule_name]
|
72
|
+
outputs = output_table[rule_name]
|
73
|
+
rules << [rule_name, inputs, outputs]
|
74
|
+
end
|
75
|
+
return rules.sort {|a, b| a.first <=> b.first}
|
76
|
+
end
|
77
|
+
|
78
|
+
FLOW_RULE_TEMPLATE = <<-RULE
|
79
|
+
Rule %s
|
80
|
+
%s
|
81
|
+
%s
|
82
|
+
Action
|
83
|
+
cp {$I[1]} {$O[1]}
|
84
|
+
End
|
85
|
+
RULE
|
86
|
+
|
87
|
+
# Textize flow rules.
|
88
|
+
def textize_flow_rules(rules)
|
89
|
+
rules.map do |rule|
|
90
|
+
inputs = rule[1].map {|name| "input '%s'" % name}.join("\n ")
|
91
|
+
outputs = rule[2].map {|name| "output '%s'" % name}.join("\n ")
|
92
|
+
Indentation.cut(FLOW_RULE_TEMPLATE) % [rule[0], inputs, outputs]
|
93
|
+
end.join("\n\n")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|