flor 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +9 -0
  3. data/CREDITS.md +21 -0
  4. data/LICENSE.txt +4 -1
  5. data/Makefile +4 -0
  6. data/README.md +8 -0
  7. data/flor.gemspec +10 -10
  8. data/lib/flor.rb +2 -2
  9. data/lib/flor/changes.rb +3 -3
  10. data/lib/flor/colours.rb +14 -8
  11. data/lib/flor/conf.rb +63 -58
  12. data/lib/flor/core.rb +4 -4
  13. data/lib/flor/core/executor.rb +65 -29
  14. data/lib/flor/core/node.rb +37 -20
  15. data/lib/flor/core/procedure.rb +182 -40
  16. data/lib/flor/core/texecutor.rb +125 -52
  17. data/lib/flor/djan.rb +111 -82
  18. data/lib/flor/dollar.rb +31 -30
  19. data/lib/flor/flor.rb +314 -237
  20. data/lib/flor/id.rb +7 -2
  21. data/lib/flor/log.rb +250 -245
  22. data/lib/flor/parser.rb +72 -38
  23. data/lib/flor/pcore/_arr.rb +10 -10
  24. data/lib/flor/pcore/_att.rb +49 -14
  25. data/lib/flor/pcore/_coll.rb +18 -0
  26. data/lib/flor/pcore/_obj.rb +23 -7
  27. data/lib/flor/pcore/_pat_.rb +1 -1
  28. data/lib/flor/pcore/_pat_guard.rb +8 -0
  29. data/lib/flor/pcore/_pat_obj.rb +3 -3
  30. data/lib/flor/pcore/_pat_or.rb +4 -0
  31. data/lib/flor/pcore/_pat_regex.rb +24 -0
  32. data/lib/flor/pcore/_skip.rb +4 -0
  33. data/lib/flor/pcore/_val.rb +0 -1
  34. data/lib/flor/pcore/all.rb +111 -0
  35. data/lib/flor/pcore/any.rb +83 -0
  36. data/lib/flor/pcore/arith.rb +35 -6
  37. data/lib/flor/pcore/break.rb +39 -1
  38. data/lib/flor/pcore/case.rb +82 -4
  39. data/lib/flor/pcore/cmp.rb +7 -7
  40. data/lib/flor/pcore/collect.rb +50 -0
  41. data/lib/flor/pcore/cond.rb +17 -3
  42. data/lib/flor/pcore/cursor.rb +8 -2
  43. data/lib/flor/pcore/detect.rb +45 -0
  44. data/lib/flor/pcore/each.rb +52 -0
  45. data/lib/flor/pcore/empty.rb +60 -0
  46. data/lib/flor/pcore/filter.rb +94 -0
  47. data/lib/flor/pcore/find.rb +67 -0
  48. data/lib/flor/pcore/for_each.rb +65 -0
  49. data/lib/flor/pcore/includes.rb +32 -0
  50. data/lib/flor/pcore/inject.rb +55 -0
  51. data/lib/flor/pcore/iterator.rb +151 -0
  52. data/lib/flor/pcore/keys.rb +60 -0
  53. data/lib/flor/pcore/length.rb +34 -7
  54. data/lib/flor/pcore/logo.rb +18 -0
  55. data/lib/flor/pcore/loop.rb +4 -0
  56. data/lib/flor/pcore/map.rb +77 -46
  57. data/lib/flor/pcore/match.rb +8 -2
  58. data/lib/flor/pcore/matchr.rb +4 -5
  59. data/lib/flor/pcore/move.rb +3 -3
  60. data/lib/flor/pcore/noeval.rb +13 -0
  61. data/lib/flor/pcore/not.rb +16 -0
  62. data/lib/flor/pcore/on.rb +172 -0
  63. data/lib/flor/pcore/on_cancel.rb +54 -0
  64. data/lib/flor/pcore/on_error.rb +68 -0
  65. data/lib/flor/pcore/rand.rb +2 -2
  66. data/lib/flor/pcore/range.rb +2 -1
  67. data/lib/flor/pcore/reduce.rb +124 -0
  68. data/lib/flor/pcore/reverse.rb +46 -0
  69. data/lib/flor/pcore/select.rb +72 -0
  70. data/lib/flor/pcore/set.rb +8 -0
  71. data/lib/flor/pcore/stall.rb +10 -0
  72. data/lib/flor/pcore/to_array.rb +61 -0
  73. data/lib/flor/pcore/until.rb +34 -0
  74. data/lib/flor/punit/cancel.rb +30 -5
  75. data/lib/flor/punit/ccollect.rb +11 -0
  76. data/lib/flor/punit/cmap.rb +10 -5
  77. data/lib/flor/punit/concurrence.rb +42 -51
  78. data/lib/flor/punit/cron.rb +33 -0
  79. data/lib/flor/punit/do_trap.rb +42 -0
  80. data/lib/flor/punit/every.rb +48 -13
  81. data/lib/flor/punit/graft.rb +3 -3
  82. data/lib/flor/punit/on_timeout.rb +38 -0
  83. data/lib/flor/punit/schedule.rb +69 -6
  84. data/lib/flor/punit/signal.rb +54 -0
  85. data/lib/flor/punit/sleep.rb +1 -1
  86. data/lib/flor/punit/task.rb +4 -1
  87. data/lib/flor/punit/trap.rb +188 -13
  88. data/lib/flor/tools/shell.rb +408 -62
  89. data/lib/flor/tools/shell_out.rb +31 -0
  90. data/lib/flor/unit.rb +1 -1
  91. data/lib/flor/unit/caller.rb +177 -0
  92. data/lib/flor/unit/executor.rb +1 -0
  93. data/lib/flor/unit/ganger.rb +15 -21
  94. data/lib/flor/unit/hook.rb +1 -1
  95. data/lib/flor/unit/hooker.rb +22 -10
  96. data/lib/flor/unit/loader.rb +22 -22
  97. data/lib/flor/unit/logger.rb +63 -36
  98. data/lib/flor/unit/models.rb +6 -1
  99. data/lib/flor/unit/models/execution.rb +12 -1
  100. data/lib/flor/unit/models/message.rb +7 -0
  101. data/lib/flor/unit/models/trap.rb +31 -17
  102. data/lib/flor/unit/scheduler.rb +18 -10
  103. data/lib/flor/unit/storage.rb +83 -23
  104. data/lib/flor/unit/waiter.rb +1 -2
  105. metadata +96 -52
  106. data/lib/flor/deep.rb +0 -144
  107. data/lib/flor/punit/on.rb +0 -57
  108. data/lib/flor/unit/runner.rb +0 -84
  109. data/match.md +0 -22
@@ -19,14 +19,12 @@ module Flor
19
19
  @archive = nil
20
20
  end
21
21
 
22
- def notify(executor, o)
22
+ def notify(executor, msg)
23
23
 
24
- return [] if o['consumed']
24
+ return [] if msg['consumed']
25
25
 
26
- Flor.log_message(executor, o) \
27
- if @conf['log_msg']
28
-
29
- @journal << o
26
+ @logger.notify(executor, msg)
27
+ @journal << msg
30
28
 
31
29
  []
32
30
  end
@@ -47,13 +45,38 @@ module Flor
47
45
  def initialize(unit)
48
46
 
49
47
  @unit = unit
48
+
49
+ @out = Flor::Logger::Out.prepare(unit)
50
+ end
51
+
52
+ def notify(executor, message)
53
+
54
+ return if message['point'] == 'end'
55
+ return unless @unit.conf['log_msg']
56
+
57
+ @out.puts(Flor.message_to_one_line_s(executor, message, out: @out))
50
58
  end
51
59
 
52
60
  def log_err(executor, message, opts={})
53
61
 
54
62
  return unless @unit.conf['log_err']
55
63
 
56
- Flor.print_detail_msg(executor, message, flag: true)
64
+ @out.puts(
65
+ Flor.msg_to_detail_s(executor, message, opts.merge(flag: true)))
66
+ end
67
+
68
+ def log_src(source, opts, log_opts={})
69
+
70
+ return unless @unit.conf['log_src']
71
+
72
+ @out.puts(Flor.src_to_s(source, opts, log_opts))
73
+ end
74
+
75
+ def log_tree(tree, nid='0', opts={})
76
+
77
+ return unless @unit.conf['log_tree']
78
+
79
+ @out.puts(Flor.tree_to_s(tree, nid, opts.merge(out: @out)))
57
80
  end
58
81
  end
59
82
 
@@ -83,11 +106,11 @@ module Flor
83
106
  @unit.opts = opts
84
107
  @unit.archive ||= {} if opts[:archive]
85
108
 
86
- Flor.print_src(tree, opts) if conf['log_src']
109
+ @unit.logger.log_src(tree, opts)
87
110
 
88
111
  messages = [ Flor.make_launch_msg(@execution['exid'], tree, opts) ]
89
112
 
90
- Flor.print_tree(messages.first['tree']) if conf['log_tree']
113
+ @unit.logger.log_tree(messages.first['tree'], '0', opts)
91
114
 
92
115
  walk(messages, opts)
93
116
  end
@@ -111,13 +134,22 @@ module Flor
111
134
 
112
135
  messages.concat(msgs)
113
136
 
114
- return messages if message_match?(message, opts[:until_after])
115
- return messages if message_match?(messages, opts[:until])
137
+ return messages \
138
+ if message_match?(message, opts[:until_after])
139
+ return messages \
140
+ if message_match?(messages, opts[:until])
141
+ #
142
+ # Walk is suspended if options :until_after or :until
143
+ # are satisfied.
144
+ # Returns the remaining messages.
116
145
 
117
146
  return message \
118
147
  if message['point'] == 'terminated'
119
148
  return message \
120
149
  if message['point'] == 'failed' && message['on_error'] == nil
150
+ #
151
+ # Walk exits when execution terminates or fails (without on_error).
152
+ # Returns the last message
121
153
  end
122
154
  end
123
155
 
@@ -158,68 +190,109 @@ module Flor
158
190
 
159
191
  class ConfExecutor < TransientExecutor
160
192
 
161
- def self.interpret(path)
193
+ class << self
162
194
 
163
- s =
164
- if path.match(/[\r\n]/)
165
- path.strip
195
+ def load(path)
196
+
197
+ src =
198
+ if path.match(/[\r\n]/)
199
+ path.strip
200
+ else
201
+ ls = File.readlines(path)
202
+ ls.reject! { |l| l.strip[0, 1] == '#' }
203
+ s = ls.join("\n").strip
204
+ end
205
+
206
+ az = "#{src[0, 1]}#{src[-1, 1]}"
207
+
208
+ if az == '{}' || az == '[]'
209
+ src
210
+ elsif src.match(/[^\r\n{]+:/) || src == ''
211
+ "{\n#{src}\n}"
166
212
  else
167
- ls = File.readlines(path)
168
- ls.reject! { |l| l.strip[0, 1] == '#' }
169
- s = ls.join("\n").strip
213
+ "[\n#{src}\n]"
214
+ end
215
+ end
216
+
217
+ def interpret(path, source, context)
218
+
219
+ path ||= '.'
220
+
221
+ fs = context['payload'] || {}
222
+
223
+ vs = Hash.new { |h, k| k }
224
+ #
225
+ vs.merge!(context['vars'] || {})
226
+ vs['root'] = determine_root(path)
227
+ vs['ruby_version'] = RUBY_VERSION
228
+ vs['ruby_platform'] = RUBY_PLATFORM
229
+ #
230
+ class << vs
231
+ def has_key?(k)
232
+ prc = Flor::Procedure[k]
233
+ ( ! prc) || ( ! prc.core?) # ignore non-core procedures
234
+ end
170
235
  end
171
236
 
172
- a, b = s[0, 1], s[-1, 1]
237
+ c = ! (ENV['FLOR_DEBUG'] || '').match(/conf/)
238
+ e = self.new('conf' => c)
239
+ r = e.launch(source, payload: fs, vars: vs)
173
240
 
174
- s =
175
- if (a == '{' && b == '}') || (a == '[' && b == ']')
176
- s
177
- elsif s.match(/[^\r\n{]+:/) || s == ''
178
- "{\n#{s}\n}"
179
- else
180
- "[\n#{s}\n]"
241
+ unless r['point'] == 'terminated'
242
+ ae = ArgumentError.new(
243
+ "error while reading conf: #{r['error']['msg']}")
244
+ ae.set_backtrace(r['error']['trc'])
245
+ fail ae
181
246
  end
182
247
 
183
- vs = Hash.new { |h, k| k }
184
- class << vs
185
- def has_key?(k)
186
- prc = Flor::Procedure[k]
187
- ( ! prc) || ( ! prc.core?) # ignore non-core procedures
248
+ o = Flor.dup(r['payload']['ret'])
249
+
250
+ if o.is_a?(Hash)
251
+ o['_path'] = path
252
+ o['root'] ||= Flor.relativize_path(vs['root'])
253
+ elsif o.is_a?(Array)
254
+ o.each { |e| e['_path'] = path }
188
255
  end
256
+
257
+ o
189
258
  end
190
259
 
191
- vs['root'] = determine_root(path)
260
+ def interpret_path(path, context=nil)
192
261
 
193
- vs['ruby_version'] = RUBY_VERSION
194
- vs['ruby_platform'] = RUBY_PLATFORM
262
+ interpret(path, load(path), context || {})
263
+ end
195
264
 
196
- c = (ENV['FLOR_DEBUG'] || '').match(/conf/) ? false : true
197
- r = (self.new('conf' => c)).launch(s, vars: vs)
265
+ def interpret_source(source, context=nil)
198
266
 
199
- unless r['point'] == 'terminated'
200
- ae = ArgumentError.new("error while reading conf: #{r['error']['msg']}")
201
- ae.set_backtrace(r['error']['trc'])
202
- fail ae
267
+ interpret(nil, source, context || {})
203
268
  end
204
269
 
205
- o = Flor.dup(r['payload']['ret'])
270
+ def interpret_path_or_source(s, context=nil)
206
271
 
207
- if o.is_a?(Hash)
208
- o['_path'] = path unless path.match(/[\r\n]/)
209
- o['root'] ||= Flor.relativize_path(vs['root'])
210
- elsif o.is_a?(Array)
211
- o.each { |e| e['_path'] = path } unless path.match(/[\r\n]/)
272
+ if s.index("\n")
273
+ interpret_source(load(s), context)
274
+ else
275
+ interpret_path(s, context)
276
+ end
212
277
  end
213
278
 
214
- o
215
- end
279
+ # Used by "flosh" the flor shell
280
+ #
281
+ def interpret_line(s)
216
282
 
217
- def self.determine_root(path)
283
+ r = interpret_source(s)
284
+ r.delete('root') if r.is_a?(Hash)
218
285
 
219
- dir = File.absolute_path(File.dirname(path))
220
- ps = dir.split(File::SEPARATOR)
286
+ r
287
+ end
288
+
289
+ def determine_root(path)
221
290
 
222
- ps.last == 'etc' ? File.absolute_path(File.join(dir, '..')) : dir
291
+ dir = File.absolute_path(File.dirname(path))
292
+ ps = dir.split(File::SEPARATOR)
293
+
294
+ ps.last == 'etc' ? File.absolute_path(File.join(dir, '..')) : dir
295
+ end
223
296
  end
224
297
  end
225
298
  end
@@ -8,193 +8,222 @@ module Flor
8
8
 
9
9
  def self.to_d(x, opts={})
10
10
 
11
- opts[:s] = StringIO.new
11
+ out = StringIO.new
12
12
  opts[:c] = Flor.colours(opts)
13
13
 
14
- if opts[:width] == true
15
- opts[:width] = IO.console.winsize[1]
14
+ if [ :console, true ].include?(opts[:width])
15
+ opts[:width] = IO.console.winsize[1] rescue 80
16
+ #elsif opts[:width].is_a?(Integer)
17
+ # let it go
16
18
  elsif mw = (opts[:mw] || opts[:maxwidth] || opts[:max_width])
17
- opts[:width] = [ IO.console.winsize[1], mw ].min
19
+ opts[:width] = [ (IO.console.winsize[1] rescue 80), mw ].min
18
20
  end
21
+ opts[:indent] ||= 0 if opts[:width]
19
22
 
20
- Djan.to_d(x, opts)
23
+ Djan.to_d(x, out, opts)
21
24
 
22
- opts[:s].string
25
+ out.string
23
26
  end
24
27
 
25
28
  module Djan
26
29
  extend self
27
30
 
28
- def to_d(x, opts)
31
+ def to_d(x, out, opts)
29
32
 
30
33
  case x
31
- when nil then nil_to_d(x, opts)
32
- when String then string_to_d(x, opts)
33
- when Hash then object_to_d(x, opts)
34
- when Array then array_to_d(x, opts)
35
- when TrueClass then boolean_to_d(x.to_s, opts)
36
- when FalseClass then boolean_to_d(x.to_s, opts)
37
- else num_to_d(x.to_s, opts)
34
+ when nil then nil_to_d(x, out, opts)
35
+ when String then string_to_d(x, out, opts)
36
+ when Hash then object_to_d(x, out, opts)
37
+ when Array then array_to_d(x, out, opts)
38
+ when TrueClass then boolean_to_d(x.to_s, out, opts)
39
+ when FalseClass then boolean_to_d(x.to_s, out, opts)
40
+ else num_to_d(x.to_s, out, opts)
38
41
  end
39
42
  end
40
43
 
41
44
  def len(x, opts)
42
45
 
43
- opts = opts.merge(
44
- s: StringIO.new, c: Flor.no_colours, indent: nil, width: nil)
46
+ opts = opts.merge(c: Flor.no_colours, indent: nil, width: nil)
47
+ o = StringIO.new
45
48
 
46
- to_d(x, opts)
49
+ to_d(x, o, opts)
47
50
 
48
- opts[:s].string.length
51
+ o.string.length
49
52
  end
50
53
 
51
- def newline(opts)
54
+ def adjust(x, opts)
55
+
56
+ i = opts[:indent]
57
+ w = opts[:width]
58
+
59
+ return opts unless i && w && (i + len(x, opts) < w)
60
+ opts.merge(indent: nil)
61
+ end
62
+
63
+ def newline(out, opts)
52
64
 
53
- opts[:s] << "\n"
65
+ out << "\n"
54
66
  end
55
67
 
56
- def space(opts, force=false)
68
+ def space(out, opts, force=false)
57
69
 
58
- opts[:s] << ' ' if force || ! opts[:compact]
70
+ out << ' ' if force || ! opts[:compact]
59
71
  end
60
72
 
61
- def newline_or_space(opts)
73
+ def newline_or_space(out, opts)
62
74
 
63
- if opts[:indent]
64
- newline(opts)
75
+ if kt = opts[:keytab]
76
+ out << ' ' * kt
77
+ elsif opts[:indent]
78
+ newline(out, opts)
65
79
  elsif ! opts[:compact]
66
- space(opts)
80
+ space(out, opts)
67
81
  end
68
82
  end
69
83
 
70
- def indent_space(opts)
84
+ def indent_space(out, opts)
71
85
 
72
86
  return if opts.delete(:first)
73
87
  i = opts[:indent]
74
- opts[:s] << ' ' * i if i
88
+ out << ' ' * i if i
75
89
  end
76
90
 
77
91
  def indent(opts, os={})
78
92
 
79
- if i = opts[:indent]
80
- opts.merge(indent: i + (os[:inc] || 1), first: os[:first])
93
+ if kt = os[:keytab]
94
+ opts.merge(indent: nil, keytab: kt)
95
+ elsif i = opts[:indent]
96
+ opts.merge(indent: i + (os[:inc] || 1) * 2, first: os[:first])
81
97
  else
82
98
  opts
83
99
  end
84
100
  end
85
101
 
86
- def adjust(x, opts)
87
-
88
- i = opts[:indent]
89
- w = opts[:width]
90
-
91
- return opts unless i && w && i + len(x, opts) < w
92
- opts.merge(indent: nil)
93
- end
94
-
95
- def object_to_d(x, opts)
102
+ def object_to_d(x, out, opts)
96
103
 
97
104
  inner = opts.delete(:inner)
98
105
 
99
- indent_space(opts)
106
+ indent_space(out, opts)
100
107
 
101
- return c_inf('{}', opts) if x.empty?
108
+ return c_inf('{}', out, opts) if x.empty?
102
109
 
103
110
  opts = adjust(x, opts)
104
111
 
105
112
  unless inner
106
- c_inf('{', opts); space(opts)
113
+ c_inf('{', out, opts); space(out, opts)
107
114
  end
108
115
 
116
+ key_max_len =
117
+ if opts[:compact]
118
+ nil
119
+ else
120
+ i = opts[:indent]
121
+ w = opts[:width]
122
+ #
123
+ kml, vml =
124
+ x.inject([ 0, 0 ]) { |(kl, vl), (k, v)|
125
+ [ [ kl, len(k, opts) ].max, [ vl, len(v, opts) ].max ] }
126
+ kml += 1
127
+ #
128
+ if i && w && i + kml + 1 + vml < w
129
+ kml
130
+ else
131
+ nil
132
+ end
133
+ end
134
+
109
135
  x.each_with_index do |(k, v), i|
110
- string_to_d(k, indent(opts, first: i == 0))
111
- c_inf(':', opts)
112
- newline_or_space(opts)
113
- to_d(v, indent(opts, inc: 2))
136
+
137
+ kl = string_to_d(k, out, indent(opts, first: i == 0))
138
+ c_inf(':', out, opts)
139
+
140
+ kt = key_max_len ? key_max_len - kl : nil
141
+ newline_or_space(out, opts.merge(keytab: kt))
142
+
143
+ to_d(v, out, indent(opts, inc: 2, keytab: kt))
144
+
114
145
  if i < x.size - 1
115
- c_inf(',', opts)
116
- newline_or_space(opts)
146
+ c_inf(',', out, opts)
147
+ newline_or_space(out, opts)
117
148
  end
118
149
  end
119
150
 
120
151
  unless inner
121
- space(opts); c_inf('}', opts)
152
+ space(out, opts); c_inf('}', out, opts)
122
153
  end
123
154
  end
124
155
 
125
- def array_to_d(x, opts)
156
+ def array_to_d(x, out, opts)
126
157
 
127
158
  inner = opts.delete(:inner)
128
159
 
129
- indent_space(opts)
160
+ indent_space(out, opts)
130
161
 
131
- return c_inf('[]', opts) if x.empty?
162
+ return c_inf('[]', out, opts) if x.empty?
132
163
 
133
164
  opts = adjust(x, opts)
134
165
 
135
166
  unless inner
136
- c_inf('[', opts); space(opts)
167
+ c_inf('[', out, opts); space(out, opts)
137
168
  end
138
169
 
139
170
  x.each_with_index do |e, i|
140
- to_d(e, indent(opts, first: i == 0))
171
+ to_d(e, out, indent(opts, first: i == 0))
141
172
  if i < x.size - 1
142
- c_inf(',', opts)
143
- newline_or_space(opts)
173
+ c_inf(',', out, opts)
174
+ newline_or_space(out, opts)
144
175
  end
145
176
  end
146
177
 
147
178
  unless inner
148
- space(opts); c_inf(']', opts)
179
+ space(out, opts); c_inf(']', out, opts)
149
180
  end
150
181
  end
151
182
 
152
- def string_to_d(x, opts)
183
+ def string_to_d(x, out, opts)
153
184
 
154
185
  x = x.to_s
155
186
 
156
- indent_space(opts)
187
+ indent_space(out, opts)
157
188
 
158
189
  if (
190
+ opts[:json] ||
159
191
  x.match(/\A[^: \b\f\n\r\t"',()\[\]{}#\\+%\/><^!=-]+\z/) == nil ||
160
192
  x.to_i.to_s == x ||
161
193
  x.to_f.to_s == x
162
- )
163
- c_inf('"', opts)
164
- c_str(x.inspect[1..-2], opts)
165
- c_inf('"', opts)
194
+ ) then
195
+ c_inf('"', out, opts)
196
+ c_str(x.inspect[1..-2], out, opts)
197
+ c_inf('"', out, opts)
198
+ x.inspect[1..-2].length + 2
166
199
  else
167
- c_str(x, opts)
200
+ c_str(x, out, opts)
201
+ x.length
168
202
  end
169
203
  end
170
204
 
171
- def boolean_to_d(x, opts)
205
+ def boolean_to_d(x, out, opts)
172
206
 
173
- indent_space(opts)
174
- if x
175
- c_tru(x, opts)
176
- else
177
- c_fal(x, opts)
178
- end
207
+ indent_space(out, opts); x ? c_tru(x, out, opts) : c_fal(x, out, opts)
179
208
  end
180
209
 
181
- def num_to_d(x, opts)
210
+ def num_to_d(x, out, opts)
182
211
 
183
- indent_space(opts); c_num(x, opts)
212
+ indent_space(out, opts); c_num(x, out, opts)
184
213
  end
185
214
 
186
- def nil_to_d(x, opts)
215
+ def nil_to_d(x, out, opts)
187
216
 
188
- indent_space(opts); c_nil('null', opts)
217
+ indent_space(out, opts); c_nil('null', out, opts)
189
218
  end
190
219
 
191
- def c_inf(s, opts); opts[:s] << opts[:c].dark_gray(s); end
220
+ def c_inf(s, out, opts); out << opts[:c].dark_gray(s); end
192
221
 
193
- def c_nil(s, opts); opts[:s] << opts[:c].dark_gray(s); end
194
- def c_tru(s, opts); opts[:s] << opts[:c].green(s); end
195
- def c_fal(s, opts); opts[:s] << opts[:c].red(s); end
196
- def c_str(s, opts); opts[:s] << opts[:c].brown(s); end
197
- def c_num(s, opts); opts[:s] << opts[:c].light_blue(s); end
222
+ def c_nil(s, out, opts); out << opts[:c].dark_gray(s); end
223
+ def c_tru(s, out, opts); out << opts[:c].green(s); end
224
+ def c_fal(s, out, opts); out << opts[:c].red(s); end
225
+ def c_str(s, out, opts); out << opts[:c].brown(s); end
226
+ def c_num(s, out, opts); out << opts[:c].light_blue(s); end
198
227
  end
199
228
  end
200
229