tap 0.12.4 → 0.17.0

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 (102) hide show
  1. data/History +34 -0
  2. data/README +62 -41
  3. data/bin/tap +36 -40
  4. data/cmd/console.rb +14 -6
  5. data/cmd/manifest.rb +62 -58
  6. data/cmd/run.rb +49 -31
  7. data/doc/API +84 -0
  8. data/doc/Class Reference +83 -115
  9. data/doc/Examples/Command Line +36 -0
  10. data/doc/Examples/Workflow +40 -0
  11. data/lib/tap/app.rb +293 -214
  12. data/lib/tap/app/node.rb +43 -0
  13. data/lib/tap/app/queue.rb +77 -0
  14. data/lib/tap/app/stack.rb +16 -0
  15. data/lib/tap/app/state.rb +22 -0
  16. data/lib/tap/constants.rb +2 -2
  17. data/lib/tap/env.rb +400 -314
  18. data/lib/tap/env/constant.rb +227 -0
  19. data/lib/tap/env/gems.rb +63 -0
  20. data/lib/tap/env/manifest.rb +89 -0
  21. data/lib/tap/env/minimap.rb +292 -0
  22. data/lib/tap/{support → env}/string_ext.rb +2 -2
  23. data/lib/tap/exe.rb +113 -125
  24. data/lib/tap/join.rb +175 -0
  25. data/lib/tap/joins.rb +9 -0
  26. data/lib/tap/joins/switch.rb +44 -0
  27. data/lib/tap/joins/sync.rb +99 -0
  28. data/lib/tap/root.rb +100 -491
  29. data/lib/tap/root/utils.rb +220 -0
  30. data/lib/tap/{support → root}/versions.rb +31 -29
  31. data/lib/tap/schema.rb +248 -0
  32. data/lib/tap/schema/parser.rb +413 -0
  33. data/lib/tap/schema/utils.rb +82 -0
  34. data/lib/tap/support/intern.rb +19 -6
  35. data/lib/tap/support/templater.rb +8 -3
  36. data/lib/tap/task.rb +175 -171
  37. data/lib/tap/tasks/dump.rb +58 -0
  38. data/lib/tap/tasks/load.rb +62 -0
  39. metadata +30 -73
  40. data/cmd/destroy.rb +0 -27
  41. data/cmd/generate.rb +0 -27
  42. data/doc/Command Reference +0 -105
  43. data/doc/Syntax Reference +0 -234
  44. data/doc/Tutorial +0 -348
  45. data/lib/tap/dump.rb +0 -142
  46. data/lib/tap/file_task.rb +0 -384
  47. data/lib/tap/generator/arguments.rb +0 -13
  48. data/lib/tap/generator/base.rb +0 -176
  49. data/lib/tap/generator/destroy.rb +0 -60
  50. data/lib/tap/generator/generate.rb +0 -93
  51. data/lib/tap/generator/generators/command/command_generator.rb +0 -21
  52. data/lib/tap/generator/generators/command/templates/command.erb +0 -32
  53. data/lib/tap/generator/generators/config/config_generator.rb +0 -98
  54. data/lib/tap/generator/generators/generator/generator_generator.rb +0 -37
  55. data/lib/tap/generator/generators/generator/templates/task.erb +0 -27
  56. data/lib/tap/generator/generators/generator/templates/test.erb +0 -26
  57. data/lib/tap/generator/generators/root/root_generator.rb +0 -84
  58. data/lib/tap/generator/generators/root/templates/MIT-LICENSE +0 -22
  59. data/lib/tap/generator/generators/root/templates/README +0 -14
  60. data/lib/tap/generator/generators/root/templates/Rakefile +0 -84
  61. data/lib/tap/generator/generators/root/templates/Rapfile +0 -11
  62. data/lib/tap/generator/generators/root/templates/gemspec +0 -27
  63. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -3
  64. data/lib/tap/generator/generators/task/task_generator.rb +0 -25
  65. data/lib/tap/generator/generators/task/templates/task.erb +0 -14
  66. data/lib/tap/generator/generators/task/templates/test.erb +0 -19
  67. data/lib/tap/generator/manifest.rb +0 -20
  68. data/lib/tap/generator/preview.rb +0 -69
  69. data/lib/tap/load.rb +0 -64
  70. data/lib/tap/spec.rb +0 -41
  71. data/lib/tap/support/aggregator.rb +0 -65
  72. data/lib/tap/support/audit.rb +0 -333
  73. data/lib/tap/support/constant.rb +0 -143
  74. data/lib/tap/support/constant_manifest.rb +0 -126
  75. data/lib/tap/support/dependencies.rb +0 -54
  76. data/lib/tap/support/dependency.rb +0 -44
  77. data/lib/tap/support/executable.rb +0 -198
  78. data/lib/tap/support/executable_queue.rb +0 -125
  79. data/lib/tap/support/gems.rb +0 -43
  80. data/lib/tap/support/join.rb +0 -144
  81. data/lib/tap/support/joins.rb +0 -12
  82. data/lib/tap/support/joins/switch.rb +0 -27
  83. data/lib/tap/support/joins/sync_merge.rb +0 -38
  84. data/lib/tap/support/manifest.rb +0 -171
  85. data/lib/tap/support/minimap.rb +0 -90
  86. data/lib/tap/support/node.rb +0 -176
  87. data/lib/tap/support/parser.rb +0 -450
  88. data/lib/tap/support/schema.rb +0 -385
  89. data/lib/tap/support/shell_utils.rb +0 -67
  90. data/lib/tap/test.rb +0 -77
  91. data/lib/tap/test/assertions.rb +0 -38
  92. data/lib/tap/test/env_vars.rb +0 -29
  93. data/lib/tap/test/extensions.rb +0 -73
  94. data/lib/tap/test/file_test.rb +0 -362
  95. data/lib/tap/test/file_test_class.rb +0 -15
  96. data/lib/tap/test/regexp_escape.rb +0 -87
  97. data/lib/tap/test/script_test.rb +0 -46
  98. data/lib/tap/test/script_tester.rb +0 -115
  99. data/lib/tap/test/subset_test.rb +0 -260
  100. data/lib/tap/test/subset_test_class.rb +0 -99
  101. data/lib/tap/test/tap_test.rb +0 -109
  102. data/lib/tap/test/utils.rb +0 -231
@@ -1,176 +0,0 @@
1
- module Tap
2
- module Support
3
-
4
- # Represents a task node in a Schema.
5
- class Node
6
- class << self
7
-
8
- # Returns the natural round of a set of nodes. The natural round is
9
- # the lowest round of any of the nodes, or the node ancestors.
10
- #
11
- # # (3)-o-[A]-o-[C]-o-[D]
12
- # # |
13
- # # (2)-o-[B]-o
14
- #
15
- # join1, join2 = Array.new(2) { [:join, [], []] }
16
- # a = Node.new [], 3, join1
17
- # b = Node.new [], 2, join1
18
- # c = Node.new [], join1, join2
19
- # d = Node.new [], join2
20
- #
21
- # Node.natural_round([d]) # => 2
22
- #
23
- # Tracking back, the natural round of D is 2. Node order does not
24
- # matter and globals are ignored.
25
- #
26
- # # ( )-o-[E]-o
27
- # # |
28
- # # (1)-o-[F]-o
29
- # # |
30
- # # (2)-o-[G]-o-[H]
31
- #
32
- # join = [:join, [], []]
33
- # e = Node.new [], nil, join
34
- # f = Node.new [], 1, join
35
- # g = Node.new [], 2, join
36
- # h = Node.new [], join
37
- #
38
- # Node.natural_round([d, h]) # => 1
39
- #
40
- def natural_round(nodes, visited=[])
41
- round = nil
42
- nodes.each do |node|
43
- next if visited.include?(node)
44
- visited << node
45
-
46
- case input = node.input
47
- when Integer
48
-
49
- # reassign current round if necesssary
50
- unless round && round < input
51
- round = input
52
- end
53
-
54
- when Array
55
- round = natural_round(node.parents, visited)
56
- end
57
-
58
- # optimization; no round is less than 0
59
- return 0 if round == 0
60
- end
61
-
62
- round || 0
63
- end
64
- end
65
-
66
- # An array of arguments used to instantiate the node
67
- attr_accessor :argv
68
-
69
- # The input for the node. Input may be:
70
- #
71
- # - a join array: [join_instance, input_nodes, output_nodes]
72
- # - an Integer indicating the round for self
73
- # - nil signifiying 'global'
74
- #
75
- attr_reader :input
76
-
77
- # The output for the node. Output may be:
78
- #
79
- # - a join array: [join_instance, input_nodes, output_nodes]
80
- # - nil signifying nothing
81
- #
82
- attr_reader :output
83
-
84
- def initialize(argv=[], input=0, output=nil)
85
- @argv = argv
86
- @input = @output = nil
87
-
88
- self.input = input
89
- self.output = output
90
- end
91
-
92
- def input_join
93
- input.kind_of?(Array) ? input : nil
94
- end
95
-
96
- def output_join
97
- output.kind_of?(Array) ? output : nil
98
- end
99
-
100
- # Returns an array of nodes that pass inputs to self via an input join.
101
- # If input is not a join, parents is an empty array.
102
- def parents
103
- input.kind_of?(Array) ? input[1] : []
104
- end
105
-
106
- # Returns an array of nodes that receive the outputs self via an output
107
- # join. If output is not a join, children is an empty array.
108
- def children
109
- output.kind_of?(Array) ? output[2] : []
110
- end
111
-
112
- # Sets the input for self.
113
- def input=(value)
114
- if input.kind_of?(Array)
115
- input[2].delete(self)
116
- end
117
-
118
- if value.kind_of?(Array)
119
- value[2] << self
120
- end
121
-
122
- @input = value
123
- end
124
-
125
- # Sets the output for self.
126
- def output=(value)
127
- if output.kind_of?(Array)
128
- output[1].delete(self)
129
-
130
- # cleanup orphan joins
131
- if output[1].empty?
132
- orphan_round = natural_round
133
- output[2].dup.each {|node| node.input = orphan_round }
134
- end
135
- end
136
-
137
- if value.kind_of?(Array)
138
- value[1] << self
139
- end
140
-
141
- @output = value
142
- end
143
-
144
- # Sets the input to nil.
145
- def globalize
146
- self.input = nil
147
- end
148
-
149
- # True if the input is nil.
150
- def global?
151
- input == nil
152
- end
153
-
154
- # Returns the round for self; a round is indicated by an integer input.
155
- # If input is anything but an integer, round returns nil.
156
- def round
157
- input.kind_of?(Integer) ? input : nil
158
- end
159
-
160
- # Alias for input=
161
- def round=(input)
162
- self.input = input
163
- end
164
-
165
- # Returns the natural round for self.
166
- def natural_round
167
- Node.natural_round([self])
168
- end
169
-
170
- def inspect
171
- "#<#{self.class}:#{object_id} argv=[#{argv.join(' ')}] input=#{input.inspect} output=#{output.inspect}>"
172
- end
173
-
174
- end
175
- end
176
- end
@@ -1,450 +0,0 @@
1
- module Tap
2
- module Support
3
-
4
- # == Syntax
5
- #
6
- # ==== Round Assignment
7
- # Tasks can be defined and set to a round using the following:
8
- #
9
- # break assigns task(s) to round
10
- # -- next 0
11
- # --+ next 1
12
- # --++ next 2
13
- # --+2 next 2
14
- # --+2[1,2,3] 1,2,3 2
15
- #
16
- # Here all task (except c) are parsed into round 0, then the
17
- # final argument reassigns e to round 3.
18
- #
19
- # schema = Parser.new("a -- b --+ c -- d -- e --+3[4]").schema
20
- # a, b, c, d, e = schema.nodes
21
- # schema.rounds # => [[a,b,d],[c], nil, [e]]
22
- #
23
- # ==== Workflow Assignment
24
- # All simple workflow patterns except switch can be specified within
25
- # the parse syntax (switch is the exception because there is no good
26
- # way to define the switch block).
27
- #
28
- # break pattern source(s) target(s)
29
- # --: sequence last next
30
- # --[] fork last next
31
- # --{} merge next last
32
- # --() sync_merge next last
33
- #
34
- # example meaning
35
- # --1:2 1.sequence(2)
36
- # --1:2:3 1.sequence(2,3)
37
- # --:2: last.sequence(2,next)
38
- # --[] last.fork(next)
39
- # --1{2,3,4} 1.merge(2,3,4)
40
- # --(2,3,4) last.sync_merge(2,3,4)
41
- #
42
- # Note how all of the bracketed styles behave similarly; they are
43
- # parsed with essentially the same code, but reverse the source
44
- # and target in the case of merges.
45
- #
46
- # Here a and b are sequenced inline. Task c is assigned to no
47
- # workflow until the final argument which sequenced b and c.
48
- #
49
- # schema = Parser.new("a --: b -- c --1:2i").schema
50
- # a, b, c = schema.nodes
51
- # schema.joins.collect do |join, inputs, outputs|
52
- # [join.options, inputs, outputs]
53
- # end # => [[{},[a],[b]], [{:iterate => true},[b],[c]]]
54
- #
55
- # ==== Globals
56
- # Global instances of task (used, for example, by dependencies) may
57
- # be assigned in the parse syntax as well. The break for a global
58
- # is '--*'.
59
- #
60
- # schema = Parser.new("a -- b --* c").schema
61
- # a, b, c = schema.nodes
62
- # schema.globals # => [c]
63
- #
64
- # ==== Escapes and End Flags
65
- # Breaks can be escaped by enclosing them in '-.' and '.-' delimiters;
66
- # any number of arguments may be enclosed within the escape. After the
67
- # end delimiter, breaks are active once again.
68
- #
69
- # schema = Parser.new("a -- b -- c").schema
70
- # schema.argvs # => [["a"], ["b"], ["c"]]
71
- #
72
- # schema = Parser.new("a -. -- b .- -- c").schema
73
- # schema.argvs # => [["a", "--", "b"], ["c"]]
74
- #
75
- # Parsing continues until the end of argv, or a an end flag '---' is
76
- # reached. The end flag may also be escaped.
77
- #
78
- # schema = Parser.new("a -- b --- c").schema
79
- # schema.argvs # => [["a"], ["b"]]
80
- #
81
- class Parser
82
-
83
- # A set of parsing routines used internally by Tap::Support::Parser,
84
- # modularized for ease of testing, and potential re-use. These methods
85
- # require that <tt>current_index</tt> and <tt>previous_index</tt> be
86
- # implemented in the including class.
87
- module Utils
88
- module_function
89
-
90
- # Defines a break regexp that matches a bracketed-pairs
91
- # break. The left and right brackets are specified as
92
- # inputs. After a match:
93
- #
94
- # $1:: The source string after the break.
95
- # (ex: '[]' => '', '1[]' => '1')
96
- # $2:: The target string.
97
- # (ex: '[]' => '', '1[1,2,3]' => '1,2,3')
98
- # $3:: The modifier string.
99
- # (ex: '[]i' => 'i', '1[1,2,3]is' => 'is')
100
- #
101
- def bracket_regexp(l, r)
102
- /\A(\d*)#{Regexp.escape(l)}([\d,]*)#{Regexp.escape(r)}([A-z]*)\z/
103
- end
104
-
105
- # The escape begin argument
106
- ESCAPE_BEGIN = "-."
107
-
108
- # The escape end argument
109
- ESCAPE_END = ".-"
110
-
111
- # The parser end flag
112
- END_FLAG = "---"
113
-
114
- # Matches any breaking arg (ex: '--', '--+', '--1:2')
115
- # After the match:
116
- #
117
- # $1:: The string after the break
118
- # (ex: '--' => '', '--++' => '++', '--1(2,3)' => '1(2,3)')
119
- #
120
- BREAK = /\A--(\z|[\+\d\:\*\[\{\(].*\z)/
121
-
122
- # Matches an execution-round break. After the match:
123
- #
124
- # $2:: The round string, or nil.
125
- # (ex: '' => nil, '++' => '++', '+1' => '+1')
126
- # $5:: The target string, or nil.
127
- # (ex: '+' => nil, '+[1,2,3]' => '1,2,3')
128
- #
129
- ROUND = /\A((\+(\d*|\+*))(\[([\d,]*)\])?)?\z/
130
-
131
- # Matches a sequence break. After the match:
132
- #
133
- # $1:: The sequence string after the break.
134
- # (ex: ':' => ':', '1:2' => '1:2', '1:' => '1:', ':2' => ':2')
135
- # $3:: The modifier string.
136
- # (ex: ':i' => 'i', '1:2is' => 'is')
137
- #
138
- SEQUENCE = /\A(\d*(:\d*)+)([A-z]*)\z/
139
-
140
- # Matches an instance break. After the match:
141
- #
142
- # $1:: The index string after the break.
143
- # (ex: '*' => '', '*1' => '1')
144
- #
145
- INSTANCE = /\A\*(\d*)\z/
146
-
147
- # A break regexp using "[]"
148
- FORK = bracket_regexp("[", "]")
149
-
150
- # A break regexp using "{}"
151
- MERGE = bracket_regexp("{", "}")
152
-
153
- # A break regexp using "()"
154
- SYNC_MERGE = bracket_regexp("(", ")")
155
-
156
- # Parses an indicies str along commas, and collects the indicies
157
- # as integers. Ex:
158
- #
159
- # parse_indicies('') # => []
160
- # parse_indicies('1') # => [1]
161
- # parse_indicies('1,2,3') # => [1,2,3]
162
- #
163
- def parse_indicies(str, regexp=/,+/)
164
- indicies = []
165
- str.split(regexp).each do |n|
166
- indicies << n.to_i unless n.empty?
167
- end
168
- indicies
169
- end
170
-
171
- # Parses the match of a ROUND regexp into a round index and an array
172
- # of task indicies that should be added to the round. The inputs
173
- # correspond to $2 and $5 for the match.
174
- #
175
- # If $2 is nil, a round index of zero is assumed; if $5 is nil or
176
- # empty, then indicies of [:current_index] are assumed.
177
- #
178
- # parse_round("+", "") # => [1, [:current_index]]
179
- # parse_round("+2", "1,2,3") # => [2, [1,2,3]]
180
- # parse_round(nil, nil) # => [0, [:current_index]]
181
- #
182
- def parse_round(two, five)
183
- index = case two
184
- when nil then 0
185
- when /\d/ then two[1, two.length-1].to_i
186
- else two.length
187
- end
188
- [index, five.to_s.empty? ? [current_index] : parse_indicies(five)]
189
- end
190
-
191
- # Parses the match of a SEQUENCE regexp into an [indicies, options]
192
- # array. The inputs corresponds to $1 and $3 for the match. The
193
- # previous and current index are assumed if $1 starts and/or ends
194
- # with a semi-colon.
195
- #
196
- # parse_sequence("1:2:3", '') # => [[1,2,3], {}]
197
- # parse_sequence(":1:2:", '') # => [[:previous_index,1,2,:current_index], {}]
198
- #
199
- def parse_sequence(one, three)
200
- seq = parse_indicies(one, /:+/)
201
- seq.unshift previous_index if one[0] == ?:
202
- seq << current_index if one[-1] == ?:
203
- [seq, parse_options(three)]
204
- end
205
-
206
- # Parses the match of an INSTANCE regexp into an index.
207
- # The input corresponds to $1 for the match. The current
208
- # index is assumed if $1 is empty.
209
- #
210
- # parse_instance("1") # => 1
211
- # parse_instance("") # => :current_index
212
- #
213
- def parse_instance(one)
214
- one.empty? ? current_index : one.to_i
215
- end
216
-
217
- # Parses the match of an bracket_regexp into a [input_indicies,
218
- # output_indicies, options] array. The inputs corresponds to $1,
219
- # $2, and $3 for a match to a bracket regexp. The previous and
220
- # current index are assumed if $1 and/or $2 is empty.
221
- #
222
- # parse_bracket("1", "2,3", "") # => [[1], [2,3], {}]
223
- # parse_bracket("", "", "") # => [[:previous_index], [:current_index], {}]
224
- # parse_bracket("1", "", "") # => [[1], [:current_index], {}]
225
- # parse_bracket("", "2,3", "") # => [[:previous_index], [2,3], {}]
226
- #
227
- def parse_bracket(one, two, three)
228
- targets = parse_indicies(two)
229
- targets << current_index if targets.empty?
230
- [[one.empty? ? previous_index : one.to_i], targets, parse_options(three)]
231
- end
232
-
233
- # Same as parse_bracket but reverses the input and output indicies.
234
- def parse_reverse_bracket(one, two, three)
235
- inputs, outputs, options = parse_bracket(one, two, three)
236
- [outputs, inputs, options]
237
- end
238
-
239
- # Parses an options string into a hash. The input corresponds
240
- # to $3 in a SEQUENCE or bracket_regexp match. Raises an error
241
- # if the options string contains unknown options.
242
- #
243
- # parse_options("") # => {}
244
- # parse_options("ik") # => {:iterate => true, :stack => true}
245
- #
246
- def parse_options(three)
247
- options = {}
248
- 0.upto(three.length - 1) do |char_index|
249
- char = three[char_index, 1]
250
-
251
- entry = Join.configurations.find do |key, config|
252
- config.attributes[:short] == char
253
- end
254
- key, config = entry
255
-
256
- raise "unknown option in: #{three} (#{char})" unless key
257
- options[key] = true
258
- end
259
- options
260
- end
261
-
262
- # Parses an arg hash into a schema argv. An arg hash is a hash
263
- # using numeric keys to specify the [row][col] in a two-dimensional
264
- # array where a set of values should go. Breaks are added between
265
- # rows (if necessary) and the array is collapsed to yield the
266
- # argv:
267
- #
268
- # argh = {
269
- # 0 => {
270
- # 0 => 'a',
271
- # 1 => ['b', 'c']},
272
- # 1 => 'z'
273
- # }
274
- # parse_argh(argh) # => ['--', 'a', 'b', 'c', '--', 'z']
275
- #
276
- # Non-numeric keys are converted to integers using to_i and
277
- # existing breaks (such as workflow breaks) occuring at the
278
- # start of a row are preseved.
279
- #
280
- # argh = {
281
- # '0' => {
282
- # '0' => 'a',
283
- # '1' => ['b', 'c']},
284
- # '1' => ['--:', 'z']
285
- # }
286
- # parse_argh(argh) # => ['--', 'a', 'b', 'c', '--:', 'z']
287
- #
288
- def parse_argh(argh)
289
- rows = []
290
- argh.each_pair do |row, values|
291
- if values.kind_of?(Hash)
292
- arry = []
293
- values.each_pair {|col, value| arry[col.to_i] = value }
294
- values = arry
295
- end
296
-
297
- rows[row.to_i] = values
298
- end
299
-
300
- argv = []
301
- rows.each do |row|
302
- row = [row].flatten.compact
303
- if row.empty? || row[0] !~ BREAK
304
- argv << '--'
305
- end
306
- argv.concat row
307
- end
308
- argv
309
- end
310
- end
311
-
312
- include Utils
313
-
314
- # The schema into which nodes are parsed
315
- attr_reader :schema
316
-
317
- def initialize(argv=[])
318
- @current_index = 0
319
- @schema = Schema.new
320
- parse(argv)
321
- end
322
-
323
- # Iterates through the argv splitting out task and workflow definitions.
324
- # Task definitions are split out (with configurations) along round and/or
325
- # workflow break lines. Rounds and workflows are dynamically parsed;
326
- # tasks may be reassigned to different rounds or workflows by later
327
- # arguments.
328
- #
329
- # Parse is non-destructive to argv. If a string argv is provided, parse
330
- # splits it into an array using Shellwords; if a hash argv is provided,
331
- # parse converts it to an array using Parser::Utils#parse_argh.
332
- #
333
- def parse(argv)
334
- parse!(argv.kind_of?(String) ? argv : argv.dup)
335
- end
336
-
337
- # Same as parse, but removes parsed args from argv.
338
- def parse!(argv)
339
- # prevent the addition of an empty node to schema
340
- return if argv.empty?
341
-
342
- argv = case argv
343
- when Array then argv
344
- when String then Shellwords.shellwords(argv)
345
- when Hash then parse_argh(argv)
346
- else argv
347
- end
348
- argv.unshift('--')
349
-
350
- escape = false
351
- current_argv = nil
352
- while !argv.empty?
353
- arg = argv.shift
354
-
355
- # if escaping, add escaped arguments
356
- # until an escape-end argument
357
- if escape
358
- if arg == ESCAPE_END
359
- escape = false
360
- else
361
- (current_argv ||= schema[current_index].argv) << arg
362
- end
363
-
364
- next
365
- end
366
-
367
- case arg
368
- when ESCAPE_BEGIN
369
- # begin escaping if indicated
370
- escape = true
371
-
372
- when END_FLAG
373
- # break on an end-flag
374
- break
375
-
376
- when BREAK
377
- # a breaking argument was reached:
378
- # unless the current argv is empty,
379
- # append and start a new definition
380
- if current_argv && !current_argv.empty?
381
- self.current_index += 1
382
- current_argv = nil
383
- end
384
-
385
- # parse the break string for any
386
- # schema modifications
387
- parse_break($1)
388
-
389
- else
390
- # add all other non-breaking args to
391
- # the current argv; this includes
392
- # both inputs and configurations
393
- (current_argv ||= schema[current_index].argv) << arg
394
-
395
- end
396
- end
397
-
398
- schema
399
- end
400
-
401
- def load(argv)
402
- argv.each do |args|
403
- case args
404
- when Array
405
- schema.nodes << Node.new(args, 0)
406
- self.current_index += 1
407
- else
408
- parse_break(args)
409
- end
410
- end
411
-
412
- # cleanup is currently required if terminal joins like
413
- # --0[] are allowed (since it's interpreted as --0[next])
414
- schema.cleanup
415
- end
416
-
417
- protected
418
-
419
- # The index of the node currently being parsed.
420
- attr_accessor :current_index # :nodoc:
421
-
422
- # Returns current_index-1, or raises an error if current_index < 1.
423
- def previous_index # :nodoc:
424
- raise ArgumentError, 'there is no previous index' if current_index < 1
425
- current_index - 1
426
- end
427
-
428
- # determines the type of break and modifies self appropriately
429
- def parse_break(arg) # :nodoc:
430
- case arg
431
- when ROUND
432
- round, indicies = parse_round($2, $5)
433
- indicies.each {|index| schema[index].round = round }
434
-
435
- when SEQUENCE
436
- indicies, options = parse_sequence($1, $3)
437
- while indicies.length > 1
438
- schema.set(Join, [indicies.shift], [indicies[0]], options)
439
- end
440
-
441
- when INSTANCE then schema[parse_instance($1)].globalize
442
- when FORK then schema.set(Join, *parse_bracket($1, $2, $3))
443
- when MERGE then schema.set(Join, *parse_reverse_bracket($1, $2, $3))
444
- when SYNC_MERGE then schema.set(Joins::SyncMerge, *parse_reverse_bracket($1, $2, $3))
445
- else raise ArgumentError, "invalid break argument: #{arg}"
446
- end
447
- end
448
- end
449
- end
450
- end