casegen 1.3.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.
- data/bin/casegen +11 -0
- data/src/agents/sets.rb +336 -0
- data/src/agents/sets/enum/by.rb +244 -0
- data/src/agents/sets/enum/cluster.rb +164 -0
- data/src/agents/sets/enum/inject.rb +50 -0
- data/src/agents/sets/enum/install.rb +73 -0
- data/src/agents/sets/enum/nest.rb +117 -0
- data/src/agents/sets/enum/op.rb +283 -0
- data/src/agents/sets/enum/pipe.rb +160 -0
- data/src/agents/sets/enum/tree.rb +442 -0
- data/src/calc.sample.txt +13 -0
- data/src/cart.sample.txt +33 -0
- data/src/casegen.rb +191 -0
- data/src/ruby_array.sample.txt +20 -0
- metadata +66 -0
data/bin/casegen
ADDED
data/src/agents/sets.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../casegen"
|
2
|
+
$LOAD_PATH << "#{File.expand_path(File.join(File.dirname(__FILE__), 'sets'))}"
|
3
|
+
require 'enum/op'
|
4
|
+
|
5
|
+
class String
|
6
|
+
def to_u
|
7
|
+
self.gsub(/ /, '_')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module CLabs::CaseGen
|
12
|
+
class Set
|
13
|
+
attr_reader :name, :data
|
14
|
+
|
15
|
+
def initialize(name, data_array)
|
16
|
+
@name = name
|
17
|
+
@data = data_array
|
18
|
+
strip_data
|
19
|
+
end
|
20
|
+
|
21
|
+
def strip_data
|
22
|
+
@data.collect! do |datum| datum.strip end
|
23
|
+
end
|
24
|
+
|
25
|
+
def values
|
26
|
+
@data
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Sets < Agent
|
31
|
+
attr_accessor :sets, :combinations, :set_titles
|
32
|
+
|
33
|
+
def Sets.agent_id
|
34
|
+
"casegen:sets"
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(data, reference_agents=nil)
|
38
|
+
@data = data
|
39
|
+
@sets = []
|
40
|
+
parse_sets
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_sets
|
44
|
+
set_names = @data.scan(/^\s*(\w.*):/)
|
45
|
+
set_data = @data.scan(/:(.*)/)
|
46
|
+
sets = set_names.flatten.zip(set_data.flatten)
|
47
|
+
sets.each do |set_array|
|
48
|
+
@sets << Set.new(set_array[0], set_array[1].split(/, /))
|
49
|
+
end
|
50
|
+
generate_combinations
|
51
|
+
end
|
52
|
+
|
53
|
+
def generate_combinations
|
54
|
+
arrays = []
|
55
|
+
@set_titles = []
|
56
|
+
@sets.each do |set| arrays << set.data; @set_titles << set.name end
|
57
|
+
@combinations = all(*arrays)
|
58
|
+
end
|
59
|
+
|
60
|
+
def titles
|
61
|
+
@set_titles
|
62
|
+
end
|
63
|
+
|
64
|
+
def all(*args)
|
65
|
+
result = []
|
66
|
+
EnumerableOperator::Product.new(*args).each { |tuple|
|
67
|
+
result << tuple
|
68
|
+
}
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_names
|
73
|
+
names = []
|
74
|
+
@sets.each do |set| names << set.name end
|
75
|
+
names
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_by_name(setname)
|
79
|
+
@sets.detect do |set| set.name =~ /#{Regexp.escape(setname)}/ end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
class Criteria
|
85
|
+
attr_reader :set_names, :set_values, :equalities
|
86
|
+
|
87
|
+
def initialize(data)
|
88
|
+
@data = data
|
89
|
+
@equalities = {}
|
90
|
+
parse
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse
|
94
|
+
@data.split(/AND/).each do |bit|
|
95
|
+
set_name, set_value = bit.split(/==|=/)
|
96
|
+
set_name.strip!; set_value.strip!
|
97
|
+
if @equalities.keys.include?(set_name)
|
98
|
+
raise ParserException.new("Rule cannot have the same set <#{set_name}> equal to different values <#{@equalities[set_name]}, #{set_value}>")
|
99
|
+
end
|
100
|
+
@equalities[set_name] = set_value
|
101
|
+
end
|
102
|
+
@set_names = @equalities.keys
|
103
|
+
@set_values = @equalities.values
|
104
|
+
end
|
105
|
+
|
106
|
+
# hash keys should be valid set names and hash values should be valid
|
107
|
+
# set values in the named set
|
108
|
+
def match(hash)
|
109
|
+
# must match all equalities
|
110
|
+
@equalities.each_pair do |eq_name, eq_value|
|
111
|
+
actual_value = hash[eq_name]
|
112
|
+
return false if actual_value.nil?
|
113
|
+
return false if actual_value != eq_value
|
114
|
+
end
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_s
|
119
|
+
@data
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class Rule
|
124
|
+
attr_reader :criteria, :description, :data
|
125
|
+
|
126
|
+
def initialize(rule_data)
|
127
|
+
@data = rule_data
|
128
|
+
parse_rule
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_rule
|
132
|
+
data = @data.sub(self.class.regexp, '')
|
133
|
+
criteria_data, *@description = data.split(/\n/)
|
134
|
+
criteria_data.strip!
|
135
|
+
@criteria = Criteria.new(criteria_data)
|
136
|
+
@description = (@description.join("\n") + "\n").outdent.strip
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class ExcludeRule < Rule
|
141
|
+
def type_description
|
142
|
+
"exclude"
|
143
|
+
end
|
144
|
+
|
145
|
+
def ExcludeRule.regexp
|
146
|
+
/^exclude/i
|
147
|
+
end
|
148
|
+
|
149
|
+
def ExcludeRule.create(rule_data)
|
150
|
+
return ExcludeRule.new(rule_data) if rule_data =~ regexp
|
151
|
+
return nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class Rules < Agent
|
156
|
+
def Rules.agent_id
|
157
|
+
"casegen:rules"
|
158
|
+
end
|
159
|
+
|
160
|
+
def initialize(data, reference_agents=[])
|
161
|
+
@data = data
|
162
|
+
@agents = reference_agents
|
163
|
+
@rules = []
|
164
|
+
@rule_classes = []
|
165
|
+
ObjectSpace.each_object(Class) do |obj|
|
166
|
+
@rule_classes << obj if obj.ancestors.include?(Rule) && obj != Rule
|
167
|
+
end
|
168
|
+
parse_data
|
169
|
+
end
|
170
|
+
|
171
|
+
def parse_data
|
172
|
+
raw_rules = @data.split(/(?=^\S)/)
|
173
|
+
|
174
|
+
raw_rules.each do |rule|
|
175
|
+
@rule_classes.each do |rule_class|
|
176
|
+
@rules << rule_class.create(rule.strip)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
@rules.compact!
|
180
|
+
@rules.flatten!
|
181
|
+
validate_rules
|
182
|
+
end
|
183
|
+
|
184
|
+
def validate_rules
|
185
|
+
@agents.each do |agent|
|
186
|
+
if agent.class == Sets
|
187
|
+
@rules.each do |rule|
|
188
|
+
rule.criteria.equalities.each_pair do |set_name, set_value|
|
189
|
+
set = agent.set_by_name(set_name)
|
190
|
+
if set.nil?
|
191
|
+
raise ParserException.new("Invalid set name <#{set_name}> " +
|
192
|
+
"in rule <#{rule.criteria}>. Valid set names are <#{agent.set_names.join(', ')}>.")
|
193
|
+
end
|
194
|
+
if !set.values.include?(set_value)
|
195
|
+
raise ParserException.new("Invalid set value <#{set_value}> " +
|
196
|
+
"in rule <#{rule.criteria}>. Valid set values for <#{set.name}> " +
|
197
|
+
"are <#{set.values.join(', ')}>.")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def length
|
206
|
+
@rules.length
|
207
|
+
end
|
208
|
+
|
209
|
+
def [](index)
|
210
|
+
return @rules[index]
|
211
|
+
end
|
212
|
+
|
213
|
+
def each(&block)
|
214
|
+
@rules.each(&block)
|
215
|
+
end
|
216
|
+
|
217
|
+
def combinations
|
218
|
+
return @combinations if !@combinations.nil?
|
219
|
+
if @agents[0].class == Sets
|
220
|
+
agent = @agents[0]
|
221
|
+
@combinations = []
|
222
|
+
agent.combinations.each do |combo|
|
223
|
+
delete = false
|
224
|
+
combo_hash = {}
|
225
|
+
i = 0
|
226
|
+
# combo is an array of values, in the same order of the set_titles.
|
227
|
+
# combo_hash will have set names matched with set values
|
228
|
+
agent.set_titles.each do |title|
|
229
|
+
combo_hash[title] = combo[i]
|
230
|
+
i += 1
|
231
|
+
end
|
232
|
+
@rules.each do |rule|
|
233
|
+
delete |= rule.criteria.match(combo_hash)
|
234
|
+
end
|
235
|
+
@combinations << combo if !delete
|
236
|
+
end
|
237
|
+
return @combinations
|
238
|
+
end
|
239
|
+
return []
|
240
|
+
end
|
241
|
+
|
242
|
+
def titles
|
243
|
+
@agents[0].titles
|
244
|
+
end
|
245
|
+
|
246
|
+
def to_s
|
247
|
+
puts @agents[0].combinations.inspect if !@agents[0].nil?
|
248
|
+
puts
|
249
|
+
puts @rules.inspect
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class ConsoleOutput < Agent
|
254
|
+
def ConsoleOutput.agent_id
|
255
|
+
"casegen:console"
|
256
|
+
end
|
257
|
+
|
258
|
+
def initialize(data, reference_agents)
|
259
|
+
@data = data
|
260
|
+
@agents = reference_agents
|
261
|
+
table = formatted_table([@agents[0].titles] + @agents[0].combinations)
|
262
|
+
table.each do |ary|
|
263
|
+
puts ary.join(' | ')
|
264
|
+
end
|
265
|
+
puts
|
266
|
+
@agents[0].each do |rule|
|
267
|
+
puts rule.data
|
268
|
+
puts
|
269
|
+
end if @agents[0].is_a?(Rules)
|
270
|
+
end
|
271
|
+
|
272
|
+
def formatted_table(combinations)
|
273
|
+
col_widths = []
|
274
|
+
formatted_tuples = []
|
275
|
+
combinations.each do |tuple|
|
276
|
+
col = 0
|
277
|
+
tuple.each do |item|
|
278
|
+
col_widths[col] = item.to_s.length if col_widths[col].to_i < item.to_s.length
|
279
|
+
col += 1
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
combinations.each do |tuple|
|
284
|
+
col = 0
|
285
|
+
formatted_tuples << tuple.collect { |item|
|
286
|
+
formatted = item.to_s.ljust(col_widths[col]) if !col_widths[col].nil?
|
287
|
+
col += 1
|
288
|
+
formatted
|
289
|
+
}
|
290
|
+
end
|
291
|
+
formatted_tuples
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
class RubyArrayOutput < Agent
|
296
|
+
def self.agent_id
|
297
|
+
"casegen:ruby_array"
|
298
|
+
end
|
299
|
+
|
300
|
+
def initialize(data, reference_agents, io=STDOUT)
|
301
|
+
@io = io
|
302
|
+
@struct_name = "Case"
|
303
|
+
@struct_name = data if !data.empty?
|
304
|
+
@agents = reference_agents
|
305
|
+
@agents.each do |agent| execute(agent) end
|
306
|
+
end
|
307
|
+
|
308
|
+
def execute(agent)
|
309
|
+
struct_header = "#{@struct_name} = Struct.new("
|
310
|
+
struct = ''
|
311
|
+
agent.titles.each do |title|
|
312
|
+
struct << ', ' if !struct.empty?
|
313
|
+
struct << ":#{title.to_u.downcase}"
|
314
|
+
end
|
315
|
+
struct << ')'
|
316
|
+
|
317
|
+
guts_header = 'cases = ['
|
318
|
+
guts = ''
|
319
|
+
agent.combinations.each do |combo|
|
320
|
+
guts << ",\n#{' ' * guts_header.length}" if !guts.empty?
|
321
|
+
guts << "#{@struct_name}.new#{combo.inspect.gsub(/\[/, '(').gsub(/\]/, ')')}"
|
322
|
+
end
|
323
|
+
@io.print(struct_header)
|
324
|
+
@io.print(struct)
|
325
|
+
@io.print("\n\n")
|
326
|
+
@io.print(guts_header)
|
327
|
+
@io.print(guts)
|
328
|
+
@io.print("]\n")
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
if __FILE__ == $0
|
334
|
+
sets = CLabs::CaseGen::Sets.new("a: 1, 2\nb: 3, 4")
|
335
|
+
puts sets.combinations
|
336
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Enumerable
|
4
|
+
|
5
|
+
class LinkedListDelegator
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :first, :next_name, :next_map, :args
|
9
|
+
|
10
|
+
def initialize first, next_spec = nil, *args, &next_proc
|
11
|
+
@first = first
|
12
|
+
@args = args
|
13
|
+
|
14
|
+
case next_spec
|
15
|
+
when Symbol
|
16
|
+
@next_name = next_spec
|
17
|
+
when String
|
18
|
+
@next_name = next_spec.intern
|
19
|
+
when nil
|
20
|
+
@next_map = next_proc
|
21
|
+
else
|
22
|
+
unless next_spec.respond_to? :[]
|
23
|
+
raise ArgumentError,
|
24
|
+
"next_spec must be a method name or respond to []."
|
25
|
+
end
|
26
|
+
@next_map = next_spec
|
27
|
+
end
|
28
|
+
|
29
|
+
unless @next_name or @next_map
|
30
|
+
raise ArgumentError,
|
31
|
+
"no next-getter specified."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def each
|
36
|
+
cur = @first
|
37
|
+
if @next_name
|
38
|
+
next_name = @next_name
|
39
|
+
message = next_name, *@args
|
40
|
+
while cur
|
41
|
+
yield cur
|
42
|
+
cur = cur.send *message
|
43
|
+
end
|
44
|
+
elsif @next_map
|
45
|
+
next_map = @next_map
|
46
|
+
args = @args
|
47
|
+
while cur
|
48
|
+
yield cur
|
49
|
+
cur = next_map[cur, *args]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class Object
|
61
|
+
def by next_spec = nil, *args, &next_proc
|
62
|
+
Enumerable::LinkedListDelegator.new self, next_spec, *args, &next_proc
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
=begin
|
68
|
+
|
69
|
+
==class Object
|
70
|
+
===instance method
|
71
|
+
---Object#by next_spec = nil, *args, &next_proc
|
72
|
+
|
73
|
+
Allows use of (({Enumerable})) methods, such as (({each})), (({collect})),
|
74
|
+
(({select})), etc., to iterate over arbitrary objects. The caller supplies a
|
75
|
+
way of calculating the successor of each object, such as an accessor method for
|
76
|
+
the next element of a linked list.
|
77
|
+
|
78
|
+
Object#by returns an (({Enumerable})) object whose (({each})) method
|
79
|
+
iterates over the sequence beginning with (({self})) and continuing as
|
80
|
+
specified by the arguments. Only the current element of the sequence is
|
81
|
+
kept in memory. No attempt is made to avoid cycles.
|
82
|
+
|
83
|
+
If (({next_spec})) is a string or symbol, (({next_proc})) is ignored and
|
84
|
+
(({next_spec})) is treated as a method name. This method name is sent, along
|
85
|
+
with arguments (({args})), to each element of the sequence to generate the next
|
86
|
+
element. The sequence terminates at the first element for which the method
|
87
|
+
returns (({nil})) or (({false})).
|
88
|
+
|
89
|
+
If (({next_spec})) is anything else, except (({nil})), (({next_proc})) is
|
90
|
+
ignored and (({next_spec})) is required to be an object that responds to
|
91
|
+
(({[]})), such as a proc or a hash. The (({[]})) method of (({next_spec}))
|
92
|
+
is called with each element of the sequence in turn as an argument, along
|
93
|
+
with (({args})), to generate the next element. The sequence terminates at
|
94
|
+
the first element for which (({[]})) returns (({nil})) or (({false})).
|
95
|
+
|
96
|
+
If (({next_spec})) is not given, or is (({nil})), a block is required. In this
|
97
|
+
case, the block is converted to a proc and iteration proceeds as in the
|
98
|
+
preceding paragraph.
|
99
|
+
|
100
|
+
The return value is not an array, but an (({Enumerable})) object that refers
|
101
|
+
to the original objects. In this sense, (({Object#by})) is a ((*delegator*)).
|
102
|
+
Typically, (({by})) is used with the (({for .. in ..})) construct, or
|
103
|
+
(equivalently) with (({each})), or with (({collect})), (({select})), and so on.
|
104
|
+
In these cases, the dependence on the original sequence does not matter. To get
|
105
|
+
the array of entries produced by (({by})) as an independent data structure, use
|
106
|
+
(({Enumerable#entries})) or (({Enumerable#to_a})).
|
107
|
+
|
108
|
+
===examples
|
109
|
+
|
110
|
+
require 'enum/by'
|
111
|
+
|
112
|
+
class A; end
|
113
|
+
class B < A; end
|
114
|
+
class C < B; end
|
115
|
+
|
116
|
+
for cl in C.by :superclass
|
117
|
+
print cl, " "
|
118
|
+
end
|
119
|
+
|
120
|
+
# prints: C B A Object
|
121
|
+
|
122
|
+
steps = proc { |x, incr, limit| y = x + incr; y <= limit ? y : nil }
|
123
|
+
p 0.by(steps, 10, 50).to_a
|
124
|
+
|
125
|
+
# prints: [0, 10, 20, 30, 40, 50]
|
126
|
+
|
127
|
+
See the end of the source file for more examples.
|
128
|
+
|
129
|
+
==version
|
130
|
+
|
131
|
+
Enumerable tools 1.6
|
132
|
+
|
133
|
+
The current version of this software can be found at
|
134
|
+
((<"http://redshift.sourceforge.net/enum
|
135
|
+
"|URL:http://redshift.sourceforge.net/enum>)).
|
136
|
+
|
137
|
+
==license
|
138
|
+
This software is distributed under the Ruby license.
|
139
|
+
See ((<"http://www.ruby-lang.org"|URL:http://www.ruby-lang.org>)).
|
140
|
+
|
141
|
+
==author
|
142
|
+
Joel VanderWerf,
|
143
|
+
((<vjoel@users.sourceforge.net|URL:mailto:vjoel@users.sourceforge.net>))
|
144
|
+
|
145
|
+
==acknowledgement
|
146
|
+
Thanks to David Alan Black for his helpful comments on the Ruby mailing
|
147
|
+
list
|
148
|
+
((<"http://blade.nagaokaut.ac.jp/ruby/ruby-talk
|
149
|
+
"|URL:http://blade.nagaokaut.ac.jp/ruby/ruby-talk>)).
|
150
|
+
|
151
|
+
=end
|
152
|
+
|
153
|
+
|
154
|
+
if __FILE__ == $0
|
155
|
+
|
156
|
+
class Foo
|
157
|
+
attr_reader :value, :next_foo
|
158
|
+
|
159
|
+
def initialize value, next_foo = nil
|
160
|
+
@value = value
|
161
|
+
@next_foo = next_foo
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
list = Foo.new(0, Foo.new(1, Foo.new(2, Foo.new(3))))
|
166
|
+
|
167
|
+
puts "Iterating over a linked list by method next_foo:"
|
168
|
+
for foo in list.by :next_foo
|
169
|
+
print "#{foo.value} "
|
170
|
+
end
|
171
|
+
print "\n\n"
|
172
|
+
|
173
|
+
puts "By proc { |foo| foo.next_foo.next_foo }:"
|
174
|
+
for foo in list.by { |foo| foo.next_foo.next_foo }
|
175
|
+
print "#{foo.value} "
|
176
|
+
end
|
177
|
+
print "\n\n"
|
178
|
+
|
179
|
+
puts "Down a tree, taking a random branch at each node:"
|
180
|
+
puts "First, with a proc:"
|
181
|
+
tree = [[0, [1, 2]], 3, [4, 5, [6, 7, 8]], 9]
|
182
|
+
at_random = proc { |x| x.kind_of?(Array) && x.at(rand(x.size)) }
|
183
|
+
for node in tree.by at_random
|
184
|
+
p node
|
185
|
+
end
|
186
|
+
puts "Second, with methods:"
|
187
|
+
class Object
|
188
|
+
def at_random
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
class Array
|
193
|
+
def at_random
|
194
|
+
at(rand(size))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
for node in tree.by :at_random
|
198
|
+
p node
|
199
|
+
end
|
200
|
+
puts
|
201
|
+
|
202
|
+
puts "With numbers (but watch out for cycles!):"
|
203
|
+
p 0.by { |x| x<10 ? x+1 : nil }.to_a
|
204
|
+
puts
|
205
|
+
|
206
|
+
puts "With numbers using a proc and an argument:"
|
207
|
+
steps = proc { |x, incr, limit| y = x + incr; y <= limit ? y : nil }
|
208
|
+
p 0.by(steps, 10, 50).to_a
|
209
|
+
puts
|
210
|
+
|
211
|
+
puts "Up the superclass relation:"
|
212
|
+
class A; end
|
213
|
+
class B < A; end
|
214
|
+
class C < B; end
|
215
|
+
for cl in C.by :superclass
|
216
|
+
print cl, " "
|
217
|
+
end
|
218
|
+
puts "\n\n"
|
219
|
+
|
220
|
+
puts "Popping down a stack:"
|
221
|
+
p [0,1,2,3,4].by { |y| y.pop; y != [] && y.dup }.entries
|
222
|
+
puts
|
223
|
+
|
224
|
+
puts "#by behaves correctly with self==nil"
|
225
|
+
p nil.by(:something).entries
|
226
|
+
puts
|
227
|
+
|
228
|
+
puts "By hash, or other class responding to []:"
|
229
|
+
h = { :a => :b, :b => :c, :c => :d }
|
230
|
+
p :a.by {|x| h[x]}.entries
|
231
|
+
puts "The same effect, but simpler:"
|
232
|
+
p :a.by(h).entries
|
233
|
+
puts
|
234
|
+
|
235
|
+
puts "Around and around we go..."
|
236
|
+
n = 0;
|
237
|
+
for x in 0.by([1,2,3,4,0])
|
238
|
+
n += 1
|
239
|
+
print x, " "
|
240
|
+
break if n > 12
|
241
|
+
end
|
242
|
+
puts "..."
|
243
|
+
|
244
|
+
end
|