zillabyte-cli 0.0.24 → 0.1.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.
- checksums.yaml +6 -14
- data/lib/#zillabyte-cli.rb# +5 -0
- data/lib/zillabyte/api/apps.rb +16 -132
- data/lib/zillabyte/api/components.rb +115 -0
- data/lib/zillabyte/api/flows.rb +121 -0
- data/lib/zillabyte/api/keys.rb +70 -0
- data/lib/zillabyte/api.rb +15 -2
- data/lib/zillabyte/auth.rb +43 -16
- data/lib/zillabyte/cli/#logs.rb# +12 -0
- data/lib/zillabyte/cli/#repl.rb# +43 -0
- data/lib/zillabyte/cli/apps.rb +52 -893
- data/lib/zillabyte/cli/auth.rb +3 -8
- data/lib/zillabyte/cli/base.rb +28 -7
- data/lib/zillabyte/cli/components.rb +245 -0
- data/lib/zillabyte/cli/flows.rb +549 -0
- data/lib/zillabyte/cli/git.rb +38 -0
- data/lib/zillabyte/cli/help.rb +11 -4
- data/lib/zillabyte/cli/keys.rb +177 -0
- data/lib/zillabyte/cli/query.rb +0 -1
- data/lib/zillabyte/cli/relations.rb +2 -1
- data/lib/zillabyte/cli/templates/{js → apps/js}/simple_function.js +0 -0
- data/lib/zillabyte/cli/templates/{js → apps/js}/zillabyte.conf.yaml +0 -0
- data/lib/zillabyte/cli/templates/apps/python/app.py +17 -0
- data/lib/zillabyte/cli/templates/{python → apps/python}/requirements.txt +0 -0
- data/lib/zillabyte/cli/templates/{python → apps/python}/zillabyte.conf.yaml +1 -1
- data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/Gemfile +0 -0
- data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/app.rb +1 -1
- data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/zillabyte.conf.yaml +0 -0
- data/lib/zillabyte/cli/templates/python/{simple_function.py → #simple_function.py#} +3 -6
- data/lib/zillabyte/common/session.rb +3 -1
- data/lib/zillabyte/helpers.rb +64 -1
- data/lib/zillabyte/runner/app_runner.rb +226 -0
- data/lib/zillabyte/runner/component_operation.rb +529 -0
- data/lib/zillabyte/runner/component_runner.rb +244 -0
- data/lib/zillabyte/runner/multilang_operation.rb +1133 -0
- data/lib/zillabyte/runner/operation.rb +11 -0
- data/lib/zillabyte/runner.rb +6 -0
- data/lib/zillabyte-cli/version.rb +1 -1
- data/zillabyte-cli.gemspec +1 -0
- metadata +83 -52
@@ -0,0 +1,529 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'mkfifo'
|
3
|
+
require "zillabyte/runner"
|
4
|
+
require "zillabyte/runner/multilang_operation"
|
5
|
+
require "zillabyte/runner/component_runner"
|
6
|
+
require 'zillabyte/api/components'
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
# Emulate Component Operations
|
11
|
+
class Zillabyte::Runner::ComponentOperation
|
12
|
+
|
13
|
+
NEXT_MESSAGE = "{\"command\": \"next\"}\n"
|
14
|
+
END_CYCLE_MESSAGE = "{\"command\": \"end_cycle\"}\n"
|
15
|
+
ENDMARKER = "\nend\n"
|
16
|
+
|
17
|
+
# Run the operation
|
18
|
+
def self.run(node, dir, consumee, consumer_pipes, tester, meta, options = {})
|
19
|
+
|
20
|
+
|
21
|
+
@__node = node
|
22
|
+
@__name = node["name"]
|
23
|
+
@__type = node["type"]
|
24
|
+
@__dir = dir
|
25
|
+
@__consumee = consumee
|
26
|
+
@__consumer_pipes = consumer_pipes
|
27
|
+
@__tester = tester
|
28
|
+
|
29
|
+
@__meta = meta
|
30
|
+
@__options = options
|
31
|
+
@__output_type = options[:output_type]
|
32
|
+
|
33
|
+
# Each consumer of a stream gets its own queue and message passing
|
34
|
+
@__emit_queues = {}
|
35
|
+
@__consumer_pipes.each_pair do |stream, consumers|
|
36
|
+
consumers.each_key do |consumer|
|
37
|
+
@__emit_queues[stream] ||= {}
|
38
|
+
@__emit_queues[stream][consumer] = {:write_queue => [], :ready => true}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
case @__type
|
44
|
+
when "source"
|
45
|
+
self.run_input()
|
46
|
+
when "sink"
|
47
|
+
self.run_output()
|
48
|
+
when "component"
|
49
|
+
self.run_rpc_component()
|
50
|
+
else
|
51
|
+
Zillabyte::Runner::MultilangOperation.run(node, dir, consumee, consumer_pipes, tester, meta, options)
|
52
|
+
end
|
53
|
+
rescue => e
|
54
|
+
cdisplay e.message
|
55
|
+
cdisplay e.backtrace
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Run a component input
|
62
|
+
def self.run_input()
|
63
|
+
input = @__options[:input]
|
64
|
+
|
65
|
+
# Read input from file
|
66
|
+
if input
|
67
|
+
cdisplay "reading from file...."
|
68
|
+
csv_rows = CSV.read("#{input}")
|
69
|
+
|
70
|
+
else
|
71
|
+
|
72
|
+
stdin = @__consumee[:rd_child]
|
73
|
+
loop do
|
74
|
+
|
75
|
+
msg = stdin.gets
|
76
|
+
|
77
|
+
# Build tuple
|
78
|
+
begin
|
79
|
+
tuple = JSON.parse(msg)
|
80
|
+
rescue JSON::ParserError
|
81
|
+
cdisplay "Error: invalid JSON"
|
82
|
+
next
|
83
|
+
end
|
84
|
+
|
85
|
+
# Check input for correct fields
|
86
|
+
has_fields = true
|
87
|
+
fields = @__node['fields'].flat_map {|h| h.keys}
|
88
|
+
tuple.each_key do |field|
|
89
|
+
if !fields.include? field
|
90
|
+
cdisplay "Error: invalid schema for node"
|
91
|
+
next
|
92
|
+
end
|
93
|
+
end
|
94
|
+
tuple_json = build_tuple_json(tuple)
|
95
|
+
display_json = Hash[JSON.parse(tuple_json)["tuple"].map {|k,v| [truncate_message(k), truncate_message(v)]}].to_json
|
96
|
+
send_to_consumers(tuple_json)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
# Send to and manage an RPC component
|
105
|
+
def self.run_rpc_component()
|
106
|
+
|
107
|
+
# Index streams and consumers by their pipes for lookup
|
108
|
+
consumer_hash = {}
|
109
|
+
@__emit_queues.each_pair do |stream, consumers|
|
110
|
+
consumers.each_key do |consumer|
|
111
|
+
read_stream = @__consumer_pipes[stream][consumer][:rd_parent]
|
112
|
+
consumer_hash[read_stream] = {:stream => stream, :consumer => consumer}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Keep track of how many consumers to handle before exiting
|
117
|
+
consumers_running = consumer_hash.keys.length
|
118
|
+
|
119
|
+
# Begin cycle
|
120
|
+
end_cycle_received = false
|
121
|
+
|
122
|
+
|
123
|
+
# TODO multiple inputs
|
124
|
+
# The input component(singular at the moment)
|
125
|
+
read_streams = consumer_hash.keys.concat [@__consumee[:rd_child]]
|
126
|
+
|
127
|
+
# Receive and handle messages
|
128
|
+
loop do
|
129
|
+
|
130
|
+
# Read from a stream
|
131
|
+
rs = select_read_streams(read_streams)
|
132
|
+
rs.each do |r|
|
133
|
+
# Read a message
|
134
|
+
obj = read_message(r)
|
135
|
+
|
136
|
+
# Handle tuple through RPC
|
137
|
+
if obj['tuple']
|
138
|
+
|
139
|
+
display_json = Hash[obj['tuple'].map{|k, v| [truncate_message(k), truncate_message(v)]}].to_json
|
140
|
+
|
141
|
+
|
142
|
+
# Start communication with API
|
143
|
+
api = "API #{@__tester.session.api}"
|
144
|
+
caller = Zillabyte::API::Components.new(api)
|
145
|
+
component_id = @__node["id"]
|
146
|
+
output_format = @__node["output_format"]
|
147
|
+
|
148
|
+
# Send RPC call
|
149
|
+
tuple = obj['tuple']
|
150
|
+
cdisplay "sending RPC call..."
|
151
|
+
call_options = {
|
152
|
+
:rpc_inputs => [tuple],
|
153
|
+
:output_format => output_format
|
154
|
+
}
|
155
|
+
|
156
|
+
res = caller.rpc(component_id, call_options)
|
157
|
+
cdisplay "received API response : #{res}"
|
158
|
+
|
159
|
+
#TODO handle errors
|
160
|
+
if res['error']
|
161
|
+
cdisplay("error: #{res['error']}")
|
162
|
+
next
|
163
|
+
end
|
164
|
+
run_ids = res['run_ids']
|
165
|
+
status = res['status']
|
166
|
+
|
167
|
+
|
168
|
+
# Send result call
|
169
|
+
loop do
|
170
|
+
|
171
|
+
# If a call for results is not ready, save the execution ID for the next cycle
|
172
|
+
ids_in_progress = []
|
173
|
+
res = caller.get_rpc_results(component_id, {:execute_ids => run_ids})
|
174
|
+
if res['error']
|
175
|
+
cdisplay("error: #{res['error']}")
|
176
|
+
next
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
# check results
|
181
|
+
res['results'].each do |run_id, hash|
|
182
|
+
|
183
|
+
# We have results
|
184
|
+
if hash['status'] == "complete"
|
185
|
+
|
186
|
+
# Handle Tuple in here
|
187
|
+
data_streams = hash['data']
|
188
|
+
|
189
|
+
# if stream_hash.nil?
|
190
|
+
# cdisplay("error : no results!")
|
191
|
+
# next
|
192
|
+
# else
|
193
|
+
# stream_hash.each_pair
|
194
|
+
# end
|
195
|
+
|
196
|
+
# Handle each resulting tuple
|
197
|
+
data_streams.each_pair do |stream, data_tuples|
|
198
|
+
data_tuples.each do |data_tuple|
|
199
|
+
tuple_json = build_tuple_json(data_tuple)
|
200
|
+
|
201
|
+
#send or enqueue the tuple to all consumers of the stream
|
202
|
+
@__emit_queues.each_pair do |stream, consumers|
|
203
|
+
consumers.each_pair do |consumer, emitter|
|
204
|
+
if emitter[:ready]
|
205
|
+
emit_consumer_tuple(stream, consumer, tuple_json)
|
206
|
+
else
|
207
|
+
@__emit_queues[stream][consumer][:write_queue] << tuple_json
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
else
|
216
|
+
cdisplay "RPC #{run_id} has not completed... "
|
217
|
+
ids_in_progress << run_id
|
218
|
+
end
|
219
|
+
end
|
220
|
+
run_ids = ids_in_progress
|
221
|
+
|
222
|
+
# If no more IDs to run, we are done
|
223
|
+
if run_ids.empty?
|
224
|
+
break
|
225
|
+
end
|
226
|
+
|
227
|
+
# Dont spam the API
|
228
|
+
sleep(2)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Ask for next tuple
|
232
|
+
write_message(@__consumee[:wr_child], NEXT_MESSAGE)
|
233
|
+
|
234
|
+
|
235
|
+
# End cycle
|
236
|
+
elsif obj['command']
|
237
|
+
case obj["command"]
|
238
|
+
|
239
|
+
# Consumer is ready for a message
|
240
|
+
when "next"
|
241
|
+
stream = consumer_hash[r][:stream]
|
242
|
+
consumer = consumer_hash[r][:consumer]
|
243
|
+
|
244
|
+
@__emit_queues[stream][consumer][:ready] = true
|
245
|
+
tuple_json = get_consumer_tuple(stream, consumer)
|
246
|
+
|
247
|
+
# End cycle for consumer if it has processed all tuples
|
248
|
+
if tuple_json.nil? && end_cycle_received
|
249
|
+
write_stream = get_write_stream(stream, consumer)
|
250
|
+
write_message(write_stream, END_CYCLE_MESSAGE)
|
251
|
+
consumers_running -= 1
|
252
|
+
if consumers_running == 0
|
253
|
+
break
|
254
|
+
end
|
255
|
+
|
256
|
+
# TODO break if last consumer
|
257
|
+
elsif !tuple_json.nil?
|
258
|
+
# Emit tuple to consumer
|
259
|
+
emit_consumer_tuple(stream, consumer, tuple_json)
|
260
|
+
emitted = true
|
261
|
+
end
|
262
|
+
|
263
|
+
when "end_cycle"
|
264
|
+
end_cycle_received = true
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
# Exit after ending consumer cycles
|
271
|
+
if consumers_running == 0
|
272
|
+
break
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
# Run a component output
|
279
|
+
def self.run_output()
|
280
|
+
|
281
|
+
|
282
|
+
output = @__options[:output]
|
283
|
+
header_written = false
|
284
|
+
read_stream = @__consumee[:rd_child]
|
285
|
+
|
286
|
+
loop do
|
287
|
+
# Read a tuple
|
288
|
+
obj = read_message(read_stream)
|
289
|
+
|
290
|
+
|
291
|
+
# Add row
|
292
|
+
if obj['tuple']
|
293
|
+
|
294
|
+
if output
|
295
|
+
filename = "#{output}.csv"
|
296
|
+
t = obj['tuple']
|
297
|
+
m = obj['meta'] || {}
|
298
|
+
|
299
|
+
# Write header
|
300
|
+
if header_written == false
|
301
|
+
keys = [t.keys, m.keys].flatten
|
302
|
+
csv_str = keys
|
303
|
+
f = File.open(filename, "w")
|
304
|
+
f.write(csv_str)
|
305
|
+
f.close()
|
306
|
+
header_written = true
|
307
|
+
end
|
308
|
+
|
309
|
+
# Write CSV row
|
310
|
+
vals = [t.values, m.values].flatten
|
311
|
+
csv_str = vals.to_csv
|
312
|
+
|
313
|
+
f = File.open(filename, "w")
|
314
|
+
f.write(csv_str)
|
315
|
+
f.close()
|
316
|
+
else
|
317
|
+
|
318
|
+
display_json = Hash[obj['tuple'].map{|k, v| [truncate_message(k), truncate_message(v)]}].to_json
|
319
|
+
cdisplay "received #{display_json}"
|
320
|
+
end
|
321
|
+
|
322
|
+
# Ready for next message
|
323
|
+
write_message(@__consumee[:wr_child], NEXT_MESSAGE)
|
324
|
+
|
325
|
+
# End cycle
|
326
|
+
elsif obj['command'] && obj['command'] == "end_cycle"
|
327
|
+
break
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
private
|
335
|
+
|
336
|
+
BUFSIZE = 8192
|
337
|
+
|
338
|
+
# Each reading pipe has a read buffer and message queue
|
339
|
+
@__read_buffers = {}
|
340
|
+
@__read_buffered_messages = {}
|
341
|
+
|
342
|
+
|
343
|
+
# Return availible reading streams
|
344
|
+
def self.select_read_streams(read_streams)
|
345
|
+
|
346
|
+
rs = []
|
347
|
+
read_streams.each do |read_stream|
|
348
|
+
@__read_buffered_messages[read_stream] ||= []
|
349
|
+
if !@__read_buffered_messages[read_stream].empty?
|
350
|
+
rs << read_stream
|
351
|
+
end
|
352
|
+
end
|
353
|
+
return rs unless rs.empty?
|
354
|
+
|
355
|
+
rs, ws, es = IO.select(read_streams, [], [])
|
356
|
+
return rs
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
# Read a JSON message
|
361
|
+
def self.read_message(read_stream)
|
362
|
+
|
363
|
+
@__read_buffers[read_stream] ||= ""
|
364
|
+
@__read_buffered_messages[read_stream] ||= []
|
365
|
+
if !@__read_buffered_messages[read_stream].empty?
|
366
|
+
obj = @__read_buffered_messages[read_stream].shift
|
367
|
+
return obj
|
368
|
+
end
|
369
|
+
|
370
|
+
# read message from stream
|
371
|
+
loop do
|
372
|
+
|
373
|
+
while !@__read_buffers[read_stream].include? ENDMARKER
|
374
|
+
segment = read_stream.sysread(BUFSIZE)
|
375
|
+
@__read_buffers[read_stream] << segment
|
376
|
+
end
|
377
|
+
|
378
|
+
read_buffer = @__read_buffers[read_stream]
|
379
|
+
if read_buffer.include? ENDMARKER
|
380
|
+
objs = read_buffer.split(ENDMARKER)
|
381
|
+
ends = read_buffer.scan(ENDMARKER)
|
382
|
+
if objs.count == ends.count # We have a full number of messages
|
383
|
+
objs.each do |obj|
|
384
|
+
begin
|
385
|
+
@__read_buffered_messages[read_stream] << JSON.parse(obj)
|
386
|
+
rescue JSON::ParserError
|
387
|
+
cdisplay "READMESSAGE: invalid JSON #{obj}"
|
388
|
+
end
|
389
|
+
end
|
390
|
+
@__read_buffers[read_stream] = ""
|
391
|
+
return @__read_buffered_messages[read_stream].shift
|
392
|
+
else
|
393
|
+
|
394
|
+
(0..ends.count-1).each do |i|
|
395
|
+
obj = objs[i]
|
396
|
+
begin
|
397
|
+
@__read_buffered_messages[read_stream] << JSON.parse(obj)
|
398
|
+
rescue JSON::ParserError
|
399
|
+
cdisplay "READMESSAGE: invalid JSON #{obj}"
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
@__read_buffers[read_stream] = objs[ends.count..-1].join(ENDMARKER)
|
404
|
+
return @__read_buffered_messages[read_stream].shift
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
|
411
|
+
# Write JSON message
|
412
|
+
def self.write_message(write_stream, msg)
|
413
|
+
write_msg = msg.strip + ENDMARKER
|
414
|
+
write_stream.write write_msg
|
415
|
+
write_stream.flush
|
416
|
+
end
|
417
|
+
|
418
|
+
# Format a message for display
|
419
|
+
def self.truncate_message(msg)
|
420
|
+
return msg if(!msg.instance_of?(String))
|
421
|
+
t_length = 50 # truncates entries to this length
|
422
|
+
m_length = msg.length
|
423
|
+
msg_out = m_length > t_length ? msg[0..t_length-3]+"..." : msg
|
424
|
+
msg_out
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
# Handshake connection to multilang
|
429
|
+
def self.handshake(write_stream, read_stream)
|
430
|
+
begin
|
431
|
+
write_message write_stream, HANDSHAKE_MESSAGE
|
432
|
+
msg = read_message(read_stream)
|
433
|
+
rescue Exception => e
|
434
|
+
cdisplay(e)
|
435
|
+
cdisplay("Error handshaking node")
|
436
|
+
raise e
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
# Send object to every consumer of the operation, regardless of stream
|
441
|
+
def self.send_to_consumers(json_obj)
|
442
|
+
@__consumer_pipes.each_pair do |stream, consumers|
|
443
|
+
consumers.each_pair do |consumer, pipe|
|
444
|
+
write_message(pipe[:wr_parent], json_obj)
|
445
|
+
cdisplay "emitted #{json_obj} to #{consumer}"
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
|
451
|
+
# Get the write pipe of the stream consumer
|
452
|
+
def self.get_write_stream(stream, consumer)
|
453
|
+
@__consumer_pipes[stream][consumer][:wr_parent]
|
454
|
+
end
|
455
|
+
|
456
|
+
|
457
|
+
# Get tuple for sending to consumer of stream
|
458
|
+
def self.get_consumer_tuple(stream, consumer)
|
459
|
+
@__emit_queues[stream][consumer][:write_queue].shift
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
# Emit tuple_json to the consumer of a stream
|
464
|
+
def self.emit_consumer_tuple(stream, consumer, tuple_json)
|
465
|
+
begin
|
466
|
+
display_json = Hash[JSON.parse(tuple_json)["tuple"].map {|k,v| [truncate_message(k), truncate_message(v)]}].to_json
|
467
|
+
rescue JSON::ParserError
|
468
|
+
cdisplay "Error: invalid JSON"
|
469
|
+
end
|
470
|
+
write_stream = get_write_stream(stream, consumer)
|
471
|
+
write_message(write_stream, tuple_json)
|
472
|
+
@__emit_queues[stream][consumer][:ready] = false
|
473
|
+
cdisplay "emitted tuple #{display_json} to #{consumer} "
|
474
|
+
end
|
475
|
+
|
476
|
+
|
477
|
+
# Build a tuple and format into JSON
|
478
|
+
def self.build_tuple_json(tuple, meta = nil, column_aliases = nil)
|
479
|
+
meta ||= {}
|
480
|
+
column_aliases ||= {}
|
481
|
+
values = {}
|
482
|
+
tuple.each do |k, v|
|
483
|
+
if(k == "id")
|
484
|
+
nextx
|
485
|
+
elsif(k == "confidence" or k == "since" or k == "source")
|
486
|
+
meta[k] = v
|
487
|
+
else
|
488
|
+
values[k] = v
|
489
|
+
end
|
490
|
+
end
|
491
|
+
tuple_json = {"tuple" => values, "meta" => meta, "column_aliases" => column_aliases}.to_json
|
492
|
+
return tuple_json
|
493
|
+
end
|
494
|
+
|
495
|
+
|
496
|
+
# Construct a multilang command
|
497
|
+
def self.command(arg, ignore_stderr=false)
|
498
|
+
cdisplay("could not extract meta information. missing zillabyte.conf.yml?") if @__meta.nil?
|
499
|
+
|
500
|
+
full_script = File.join(@__dir, @__meta["script"])
|
501
|
+
stderr_opt = "2> /dev/null" if ignore_stderr
|
502
|
+
|
503
|
+
case @__meta["language"]
|
504
|
+
when "ruby"
|
505
|
+
# Execute in the bundler context
|
506
|
+
cmd = "cd \"#{@__dir}\"; unset BUNDLE_GEMFILE; ZILLABYTE_HARNESS=1 bundle exec ruby \"#{full_script}\" #{arg} #{stderr_opt}"
|
507
|
+
when "python"#{
|
508
|
+
if(File.directory?("#{@__dir}/vEnv"))
|
509
|
+
cmd = "cd \"#{@__dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte #{@__dir}/vEnv/bin/python \"#{full_script}\" #{arg} #{stderr_opt}"
|
510
|
+
else
|
511
|
+
cmd = "cd \"#{@__dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte python \"#{full_script}\" #{arg} #{stderr_opt}"
|
512
|
+
end
|
513
|
+
when "js"
|
514
|
+
cmd = "cd \"#{@__dir}\"; NODE_PATH=~/zb1/multilang/js/src/lib #{Zillabyte::API::NODEJS_BIN} \"#{full_script}\" #{arg} #{stderr_opt}"
|
515
|
+
else
|
516
|
+
cdisplay("no language specified")
|
517
|
+
end
|
518
|
+
return cmd
|
519
|
+
end
|
520
|
+
|
521
|
+
|
522
|
+
# Display a colored, formatted message
|
523
|
+
def self.cdisplay(msg)
|
524
|
+
|
525
|
+
@__tester.cdisplay(@__name, msg)
|
526
|
+
end
|
527
|
+
|
528
|
+
|
529
|
+
end
|