zillabyte-cli 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/lib/#zillabyte-cli.rb# +5 -0
- data/lib/zillabyte/api/{flows.rb → apps.rb} +35 -23
- data/lib/zillabyte/api/data.rb +12 -1
- data/lib/zillabyte/api/logs.rb +4 -4
- data/lib/zillabyte/api/queries.rb +15 -6
- data/lib/zillabyte/api/zillalogs.rb +1 -1
- data/lib/zillabyte/api.rb +40 -38
- data/lib/zillabyte/auth.rb +19 -11
- data/lib/zillabyte/cli/#logs.rb# +12 -0
- data/lib/zillabyte/cli/{flows.rb → apps.rb} +407 -177
- data/lib/zillabyte/cli/auth.rb +1 -1
- data/lib/zillabyte/cli/base.rb +5 -4
- data/lib/zillabyte/cli/config.rb +3 -2
- data/lib/zillabyte/cli/counters.rb +1 -1
- data/lib/zillabyte/cli/help.rb +1 -1
- data/lib/zillabyte/cli/helpers/data_schema_builder.rb +1 -1
- data/lib/zillabyte/cli/helpers/table_output_builder.rb +25 -0
- data/lib/zillabyte/cli/log_formatter.rb +4 -3
- data/lib/zillabyte/cli/query.rb +107 -26
- data/lib/zillabyte/cli/relations.rb +226 -78
- data/lib/zillabyte/cli/sources.rb +1 -1
- data/lib/zillabyte/cli/templates/js/simple_function.js +5 -0
- data/lib/zillabyte/cli/templates/python/#simple_function.py# +27 -0
- data/lib/zillabyte/cli/templates/python/simple_function.py +3 -0
- data/lib/zillabyte/cli/templates/ruby/{simple_function.rb → simple_app.rb} +6 -6
- data/lib/zillabyte/cli/templates/ruby/zillabyte.conf.yaml +1 -1
- data/lib/zillabyte/cli/version.rb +1 -1
- data/lib/zillabyte/cli/zillalogs.rb +1 -1
- data/lib/zillabyte/command.rb +10 -2
- data/lib/zillabyte/common/{progress.rb → session.rb} +1 -1
- data/lib/zillabyte/helpers.rb +9 -4
- data/lib/zillabyte-cli/version.rb +1 -1
- data/zillabyte-cli.gemspec +2 -0
- metadata +25 -7
@@ -1,34 +1,42 @@
|
|
1
1
|
require "zillabyte/cli/base"
|
2
|
+
require "zillabyte/cli/helpers/table_output_builder"
|
2
3
|
require "csv"
|
3
4
|
require "open-uri"
|
4
5
|
require "aws-sdk"
|
6
|
+
require "json"
|
7
|
+
|
5
8
|
|
6
9
|
# manage custom relations
|
7
10
|
#
|
8
11
|
class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
9
12
|
|
13
|
+
MAX_POLL_SECONDS = 60 * 5
|
14
|
+
POLL_SLEEP = 0.5
|
15
|
+
|
10
16
|
# relations
|
11
17
|
#
|
12
18
|
# lists your custom relations
|
19
|
+
# --type TYPE # specify an output type i.e. json
|
13
20
|
#
|
14
21
|
def index
|
15
22
|
self.list
|
16
23
|
end
|
17
24
|
|
18
25
|
|
19
|
-
|
20
26
|
# relations
|
21
|
-
#
|
27
|
+
# v
|
22
28
|
# lists your custom relations
|
23
29
|
#
|
30
|
+
# --type TYPE # The output format type
|
24
31
|
def list
|
25
|
-
|
32
|
+
type = options[:type] || nil
|
33
|
+
|
26
34
|
response = api.request(
|
27
35
|
:expects => 200,
|
28
36
|
:method => :get,
|
29
37
|
:path => "/relations"
|
30
38
|
)
|
31
|
-
|
39
|
+
|
32
40
|
headings = []
|
33
41
|
rows = response.body.map do |row|
|
34
42
|
headings = row.keys if headings.size == 0
|
@@ -37,10 +45,11 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
37
45
|
vals = row.values_at *headings
|
38
46
|
vals
|
39
47
|
end
|
40
|
-
|
41
|
-
display "relations
|
42
|
-
display
|
43
|
-
|
48
|
+
|
49
|
+
display "relations\n" if type.nil? && rows.size > 0
|
50
|
+
display TableOutputBuilder.build_table(headings, rows, type)
|
51
|
+
display "Total number of relations: "+rows.length.to_s if type.nil?
|
52
|
+
|
44
53
|
end
|
45
54
|
alias_command "rl:list", "relations:list"
|
46
55
|
|
@@ -49,13 +58,20 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
49
58
|
#
|
50
59
|
# deletes a relation
|
51
60
|
#
|
52
|
-
# -f, --force
|
61
|
+
# -f, --force # Delete without asking for confirmation
|
62
|
+
# --type TYPE # The output format type
|
53
63
|
def delete
|
54
64
|
id = options[:id] || shift_argument
|
55
65
|
forced = options[:force]
|
56
|
-
|
66
|
+
type = options[:type] || nil
|
67
|
+
|
68
|
+
if !forced
|
69
|
+
|
70
|
+
if !type.nil?
|
71
|
+
error("specify -f, --force to confirm deletion", type)
|
72
|
+
end
|
73
|
+
|
57
74
|
while true
|
58
|
-
|
59
75
|
display "This operation cannot be undone. Are you sure you want to delete this relation? (yes/no):", false
|
60
76
|
confirm = ask
|
61
77
|
break if confirm == "yes" || confirm == "no"
|
@@ -66,11 +82,17 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
66
82
|
confirmed = forced || confirm == "yes"
|
67
83
|
|
68
84
|
if confirmed
|
69
|
-
|
70
|
-
|
85
|
+
res = api.data.delete(id)
|
86
|
+
if res['error']
|
87
|
+
error(res['error'], type)
|
88
|
+
else
|
89
|
+
if type == "json"
|
90
|
+
display {}.to_json
|
91
|
+
else
|
92
|
+
display res["body"]
|
93
|
+
end
|
94
|
+
end
|
71
95
|
end
|
72
|
-
|
73
|
-
|
74
96
|
end
|
75
97
|
alias_command "rl:delete", "relations:delete"
|
76
98
|
|
@@ -82,36 +104,47 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
82
104
|
# --schema SCHEMA # Column names and types in the format "field_1:type_1,field_2:type_2,..."
|
83
105
|
# --public SCOPE # Make the relation public
|
84
106
|
# --file FILE # A data file
|
85
|
-
# --
|
107
|
+
# --filetype FILETYPE # File format type, defaults to csv
|
86
108
|
# --description DESCRIPTION # Description of relation contents
|
87
109
|
# --aliases ALIASES # Relation name aliases in the format "alias_1,alias_2,..."
|
110
|
+
# --type TYPE # The output format type
|
88
111
|
#
|
89
112
|
def create
|
90
113
|
|
91
114
|
name = options[:name] || shift_argument
|
92
115
|
file = options[:file] || nil
|
93
|
-
|
116
|
+
filetype = options[:filetype] || nil
|
117
|
+
type = options[:type]
|
94
118
|
|
95
|
-
error
|
119
|
+
error("no name given", type) if name.nil?
|
96
120
|
|
97
121
|
schema = options[:schema] if options[:schema]
|
98
122
|
is_public = options[:public] || nil
|
99
123
|
description = options[:description] || nil
|
100
124
|
aliases = options[:aliases] || nil
|
101
|
-
|
102
|
-
|
125
|
+
|
126
|
+
if type.nil?
|
127
|
+
hash = get_relation_properties(schema,is_public,description,aliases)
|
128
|
+
else
|
129
|
+
hash = hash_relation_properties(schema,is_public,description,aliases, type)
|
130
|
+
end
|
131
|
+
|
103
132
|
if file
|
104
|
-
|
105
|
-
rows = sanity_check_file(file,
|
133
|
+
filetype ||= File.extname(file).gsub(".", "")
|
134
|
+
rows = sanity_check_file(file,filetype, {"columns" => hash[:schema]}, type)
|
106
135
|
hash[:rows] = rows
|
107
136
|
end
|
108
137
|
|
109
138
|
res = api.data.create name, hash
|
110
139
|
|
111
140
|
if res['error']
|
112
|
-
|
141
|
+
error("#{res['error_message']}", type)
|
113
142
|
else
|
114
|
-
|
143
|
+
if type == "json"
|
144
|
+
display {}.to_json
|
145
|
+
else
|
146
|
+
display "relation ##{res['id']} #{res['action']}. size: #{res['size'] || 0} rows."
|
147
|
+
end
|
115
148
|
end
|
116
149
|
|
117
150
|
end
|
@@ -121,8 +154,8 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
121
154
|
# relations:append ID FILE
|
122
155
|
#
|
123
156
|
# adds data to an existing relation
|
124
|
-
#
|
125
|
-
# --type TYPE
|
157
|
+
# --filetype FILETYPE # Input File format type, defaults to csv
|
158
|
+
# --type TYPE # Output formatting type i.e. json
|
126
159
|
#
|
127
160
|
def append
|
128
161
|
|
@@ -130,16 +163,22 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
130
163
|
file = options[:file] || shift_argument
|
131
164
|
type = options[:type]
|
132
165
|
|
133
|
-
|
134
|
-
|
135
|
-
|
166
|
+
filetype = options[:filetype]
|
167
|
+
filetype ||= File.extname(file).gsub(".", "")
|
168
|
+
error("no id given", type) if id.nil?
|
169
|
+
error("no file given", type) if file.nil?
|
170
|
+
|
136
171
|
|
137
172
|
relation = self.api.data.get(id)
|
138
173
|
columns = relation["columns"].map{|col| {col["index"] => col["type"]}}
|
139
|
-
rows = sanity_check_file(file,
|
174
|
+
rows = sanity_check_file(file,filetype,{"columns" => columns}, type)
|
140
175
|
|
141
176
|
res = self.api.data.append(id, {:rows => rows})
|
142
|
-
|
177
|
+
if type == "json"
|
178
|
+
display {}.to_json
|
179
|
+
else
|
180
|
+
display "relation ##{id} appended #{res["size"]} rows"
|
181
|
+
end
|
143
182
|
|
144
183
|
end
|
145
184
|
alias_command "append", "relations:append"
|
@@ -149,31 +188,36 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
149
188
|
#
|
150
189
|
# pulls relation data into OUTPUT.gz
|
151
190
|
#
|
152
|
-
# --cycle_id [cycle_id]
|
191
|
+
# --cycle_id [cycle_id] # retrieve data generated for that cycle, only if this relation is associated with an app. (defaults to the last cycle
|
192
|
+
# --type TYPE # the output format type
|
153
193
|
#
|
154
194
|
def pull
|
155
195
|
|
156
196
|
id = options[:id] || shift_argument
|
157
197
|
file = options[:file] || shift_argument
|
158
|
-
|
159
|
-
error
|
198
|
+
type = options[:type]
|
199
|
+
error("no id given", type) if id.nil?
|
200
|
+
error("no file given", type) if file.nil?
|
201
|
+
|
160
202
|
file = "#{file}.gz" unless File.extname(file) == ".gz"
|
161
203
|
|
162
204
|
res = self.api.data.pull(id, options)
|
205
|
+
|
163
206
|
if(res["uri"])
|
164
|
-
display "Waiting for download."
|
207
|
+
display "Waiting for download." if type.nil?
|
165
208
|
File.open(file, "w") do |f|
|
166
209
|
f.write open(res["uri"]).read
|
167
210
|
end
|
168
211
|
elsif(res["s3_credentials"])
|
169
|
-
display "Request sent. Depending on the size of your file, this may take a while."
|
212
|
+
display "Request sent. Depending on the size of your file, this may take a while." if type.nil?
|
170
213
|
s3 = AWS::S3.new(res["s3_credentials"])
|
171
214
|
bucket = s3.buckets[res["s3_bucket"]]
|
172
215
|
obj = bucket.objects.with_prefix("#{res["s3_file_key"]}/")
|
216
|
+
|
173
217
|
while(true)
|
174
218
|
keys = obj.collect(&:key)
|
175
219
|
if keys.length > 0 and keys.include?("#{res["s3_file_key"]}/manifest")
|
176
|
-
display "Starting to write to file..."
|
220
|
+
display "Starting to write to file..." if type.nil?
|
177
221
|
File.open(file, "w") do |f|
|
178
222
|
obj.each do |o|
|
179
223
|
if o.key == "#{res["s3_file_key"]}/manifest"
|
@@ -189,7 +233,12 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
189
233
|
end
|
190
234
|
end
|
191
235
|
end
|
192
|
-
|
236
|
+
|
237
|
+
if type == "json"
|
238
|
+
display {}.to_json
|
239
|
+
else
|
240
|
+
display "pulled data from relation ##{id} to file #{file}"
|
241
|
+
end
|
193
242
|
|
194
243
|
end
|
195
244
|
alias_command "rl:pull", "relations:pull"
|
@@ -198,30 +247,37 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
198
247
|
#
|
199
248
|
# pulls relation data to S3_BUCKET/FILE_KEY/part***.gz
|
200
249
|
#
|
201
|
-
# --cycle_id [cycle_id]
|
250
|
+
# --cycle_id [cycle_id] # retrieve data generated for that cycle, only if this relation is associated with an app. (defaults to the last cycle)
|
251
|
+
# -t, --type TYPE # the output format types
|
202
252
|
#
|
253
|
+
|
203
254
|
def pull_to_s3
|
204
255
|
|
205
256
|
id = options[:id] || shift_argument
|
206
|
-
|
257
|
+
type = options[:type]
|
258
|
+
error("no id given", type) if id.nil?
|
207
259
|
|
208
260
|
user_s3_access_key = options[:s3_access_key] || shift_argument
|
209
261
|
user_s3_secret = options[:s3_secret] || shift_argument
|
210
262
|
user_s3_bucket = options[:s3_bucket] || shift_argument
|
211
263
|
user_s3_file_key = options[:s3_file_key] || shift_argument
|
212
|
-
error
|
213
|
-
error
|
214
|
-
error
|
215
|
-
error
|
264
|
+
error("no s3 access key provided", type) if user_s3_access_key.nil?
|
265
|
+
error("no s3 access secret provided", type) if user_s3_secret.nil?
|
266
|
+
error("no s3 access bucket provided", type) if user_s3_bucket.nil?
|
267
|
+
error("no s3 file path provided", type) if user_s3_file_key.nil?
|
216
268
|
|
217
269
|
s3_params = {:s3_access_key => user_s3_access_key, :s3_secret => user_s3_secret,
|
218
270
|
:s3_bucket => user_s3_bucket, :s3_file_key => user_s3_file_key}
|
219
271
|
s3_params[:cycle_id] = options[:cycle_id] if options[:cycle_id]
|
220
272
|
|
221
273
|
res = self.api.data.pull_to_s3(id, s3_params)
|
222
|
-
display "downloading relation data to s3://#{res["s3_bucket"]}/#{res["s3_file_key"]}/"
|
223
|
-
display "if the relation is large, this may take a while, please check your s3 account after a few minutes"
|
224
274
|
|
275
|
+
if type == "json"
|
276
|
+
display {}.to_json
|
277
|
+
else
|
278
|
+
display "downloading relation data to s3://#{res["s3_bucket"]}/#{res["s3_file_key"]}/"
|
279
|
+
display "if the relation is large, this may take a while, please check your s3 account after a few minutes"
|
280
|
+
end
|
225
281
|
end
|
226
282
|
alias_command "relations:pull:s3", "relations:pull_to_s3"
|
227
283
|
alias_command "rl:pull:s3", "relations:pull_to_s3"
|
@@ -231,23 +287,65 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
231
287
|
#
|
232
288
|
# shows a sample of the data in a relation. see 'zillabyte queries' for more elaborate functionality
|
233
289
|
#
|
234
|
-
# --cycle_id [cycle_id] # retrieve data generated for that cycle, only if this relation is associated with
|
290
|
+
# --cycle_id [cycle_id] # retrieve data generated for that cycle, only if this relation is associated with an app. (defaults to the last cycle)
|
235
291
|
# --no_truncation # don't truncate long strings
|
236
|
-
#
|
292
|
+
# --type TYPE # the type of the output
|
237
293
|
def show
|
238
|
-
|
239
294
|
name = options[:name] || shift_argument
|
295
|
+
type = options[:type]
|
240
296
|
error "no id given" if name.nil?
|
241
297
|
|
242
|
-
|
243
|
-
|
298
|
+
# Initial request..
|
299
|
+
res = self.api.data.show(name, :post, options)
|
300
|
+
|
301
|
+
if res['job_id']
|
302
|
+
job_id = res['job_id']
|
303
|
+
options[:job_id] = job_id
|
304
|
+
|
305
|
+
# Poll until the results are ready...
|
306
|
+
start = Time.now.utc
|
307
|
+
|
308
|
+
display "Fetching your data, please wait..." if type.nil?
|
309
|
+
|
310
|
+
while(Time.now.utc < start + MAX_POLL_SECONDS) do
|
311
|
+
|
312
|
+
# Poll
|
313
|
+
res = self.api.data.show(name, :get, options)
|
314
|
+
|
315
|
+
# Status?
|
316
|
+
case res['status']
|
317
|
+
when 'completed'
|
318
|
+
if res['return']
|
319
|
+
res = res['return']
|
320
|
+
else
|
321
|
+
throw "something is wrong: #{res}"
|
322
|
+
end
|
323
|
+
# success! continue below
|
324
|
+
break
|
325
|
+
when 'running'
|
326
|
+
sleep(POLL_SLEEP)
|
327
|
+
# display ".", false
|
328
|
+
else
|
329
|
+
throw "unknown status: #{res}"
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
else
|
334
|
+
if res['error']
|
335
|
+
error(res['error_message'] || res['error'], type)
|
336
|
+
else
|
337
|
+
error("remote server error (r256)", type)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
# We only reach here after polling is complete...
|
244
342
|
if res["rows"].size > 0
|
245
343
|
headings = []
|
246
344
|
concrete_headings = res["rows"].first.keys
|
247
345
|
concrete_headings.delete("id")
|
248
346
|
concrete_headings.each do |ch|
|
249
347
|
has_alias = false
|
250
|
-
res['column_aliases'].each do |al|
|
348
|
+
(res['column_aliases'] || []).each do |al|
|
251
349
|
if(al["concrete_name"] == ch)
|
252
350
|
headings << al["alias"]
|
253
351
|
has_alias = true
|
@@ -256,9 +354,8 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
256
354
|
headings << ch if !has_alias
|
257
355
|
end
|
258
356
|
|
259
|
-
|
357
|
+
rows = []
|
260
358
|
res["rows"].each do |obj|
|
261
|
-
|
262
359
|
new_row = concrete_headings.map do |heading|
|
263
360
|
if options[:no_truncation]
|
264
361
|
obj[heading]
|
@@ -270,16 +367,16 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
270
367
|
end
|
271
368
|
end
|
272
369
|
end
|
273
|
-
|
274
|
-
table.add_row new_row
|
275
|
-
|
370
|
+
rows << new_row
|
276
371
|
end
|
277
|
-
|
278
|
-
|
372
|
+
|
373
|
+
display TableOutputBuilder.build_table(headings, rows, type)
|
279
374
|
else
|
280
|
-
|
281
|
-
|
282
|
-
|
375
|
+
if type == "json"
|
376
|
+
display {}.to_json
|
377
|
+
else
|
378
|
+
display "empty relation"
|
379
|
+
end
|
283
380
|
end
|
284
381
|
|
285
382
|
end
|
@@ -289,8 +386,53 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
289
386
|
private
|
290
387
|
|
291
388
|
|
389
|
+
def hash_relation_properties(schema, is_public, description, aliases, type)
|
390
|
+
if !aliases.nil?
|
391
|
+
aliases = aliases.strip.split(",").map{|x| x.strip}.uniq
|
392
|
+
aliases.each do |a|
|
393
|
+
if(!(a =~ /^[a-zA-Z0-9\_]+$/i))
|
394
|
+
error_message = "(\"#{a}\" contains illegal characters. Only letters, numbers and underscore are allowed in alias names."
|
395
|
+
error_message += "If you would like to give alias names to your relation, please enter them below. (comma separated, only letters, number and underscore allowed)"
|
396
|
+
error(error_message, type)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
else
|
400
|
+
aliases = []
|
401
|
+
end
|
292
402
|
|
293
|
-
|
403
|
+
valid_types=["string", "integer", "float"]
|
404
|
+
meta_names=["id", "confidence", "since", "source"]
|
405
|
+
|
406
|
+
if description.nil?
|
407
|
+
description = ""
|
408
|
+
end
|
409
|
+
|
410
|
+
if schema.nil?
|
411
|
+
error("specify a schema", type)
|
412
|
+
end
|
413
|
+
schema = schema.split(",").map { |x| Hash[*x.split(":")] }
|
414
|
+
schema.each do |s|
|
415
|
+
cname = s.keys[0]
|
416
|
+
if(cname =~ /^v[0-9]+$/i or meta_names.member?(cname.downcase))
|
417
|
+
error("\"v[number]\", \"id\", \"confidence\", \"since\" and \"source\" are special names in Zillabyte. Please name your column name something else.", type)
|
418
|
+
end
|
419
|
+
ctype = s[cname]
|
420
|
+
unless valid_types.member?(ctype)
|
421
|
+
error("invalid type: #{ctype}", type)
|
422
|
+
next
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
if is_public.nil?
|
427
|
+
error("specify public or private using --public")
|
428
|
+
end
|
429
|
+
|
430
|
+
{:schema => schema, :public => is_public, :description => description, :aliases => aliases}
|
431
|
+
end
|
432
|
+
|
433
|
+
|
434
|
+
|
435
|
+
def get_relation_properties(schema, is_public, description, aliases)
|
294
436
|
if aliases.nil?
|
295
437
|
display "If you would like to give alias names to your relation, please enter them below. (comma separated, only letters, number and underscore allowed)"
|
296
438
|
aliases = ask.strip
|
@@ -371,7 +513,7 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
371
513
|
{:schema => schema, :public => is_public, :description => description, :aliases => aliases}
|
372
514
|
end
|
373
515
|
|
374
|
-
def sanity_check_file(file,
|
516
|
+
def sanity_check_file(file, filetype, dataset, type = nil)
|
375
517
|
rows = []
|
376
518
|
|
377
519
|
n_columns = dataset["columns"].size
|
@@ -387,27 +529,33 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
|
|
387
529
|
end
|
388
530
|
end
|
389
531
|
|
390
|
-
case
|
532
|
+
case filetype
|
391
533
|
when "csv"
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
float_cols.each do |i|
|
397
|
-
if(!(row[i] =~ /^[+-]?(\d+(.\d+)?)$/))
|
398
|
-
error "column #{i+1} should be a FLOAT in row::\n #{row}"
|
534
|
+
begin
|
535
|
+
CSV.foreach(file) do |row|
|
536
|
+
if row.size != n_columns
|
537
|
+
error("relation expect #{n_columns} column(s). Found a row with #{row.size}::\n #{row}", type)
|
399
538
|
end
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
error
|
539
|
+
|
540
|
+
float_cols.each do |i|
|
541
|
+
if (!(row[i] =~ /^[+-]?(\d+(.\d+)?)$/))
|
542
|
+
error("column #{i+1} should be an INTEGER in row ::\n #{row}", type)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
int_cols.each do |i|
|
547
|
+
if (!(row[i] =~ /^[+-]?(\d+)$/))
|
548
|
+
error("column #{i+1} should be an INTEGER in row #{row}::\n #{row}", type)
|
549
|
+
end
|
404
550
|
end
|
551
|
+
rows << row
|
405
552
|
end
|
406
|
-
|
553
|
+
rescue Exception => e
|
554
|
+
error("unable to parse csv", type)
|
407
555
|
end
|
408
556
|
|
409
557
|
else
|
410
|
-
error "unsupported type: #{
|
558
|
+
error "unsupported type: #{filetype}"
|
411
559
|
|
412
560
|
end
|
413
561
|
rows
|
@@ -15,6 +15,11 @@ function exec(controller, tuple) {
|
|
15
15
|
}
|
16
16
|
|
17
17
|
zillabyte.simple_function({
|
18
|
+
/**
|
19
|
+
* This specifies the function's name and is mandatory.
|
20
|
+
*/
|
21
|
+
name: "simple_function",
|
22
|
+
|
18
23
|
/**
|
19
24
|
* This directive instructs zillabyte to give your function every
|
20
25
|
* web page in our known universe. Your function will have access
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import zillabyte
|
2
|
+
|
3
|
+
def prep(controller):
|
4
|
+
return
|
5
|
+
|
6
|
+
# This is the heart of your algorithm. It's processed on every
|
7
|
+
# web page. This algorithm is run in parallel on possibly hundreds
|
8
|
+
# of machines.
|
9
|
+
def execute(controller, tup):
|
10
|
+
if("hello world" in tup.values["html"]):
|
11
|
+
controller.emit("has_hello_world",{"url":tup.values["url"]})
|
12
|
+
return
|
13
|
+
|
14
|
+
zillabyte.simple_function(\
|
15
|
+
# This directive instructs zillabyte to give your function every
|
16
|
+
# web page in our known universe. Your function will have access
|
17
|
+
# to two fields: URL and HTML
|
18
|
+
matches = "select * from web_pa", \
|
19
|
+
|
20
|
+
# This directive tells Zillabyte what kind of data your function
|
21
|
+
# produces. In this case, we're saying we will emit a tuple that
|
22
|
+
# is one-column wide and contains the field 'URL'
|
23
|
+
emits = [["has_hello_world", [{"url":"string"}]]], \
|
24
|
+
|
25
|
+
prepare = prep, \
|
26
|
+
execute = execute\
|
27
|
+
)
|
@@ -12,6 +12,9 @@ def execute(controller, tup):
|
|
12
12
|
return
|
13
13
|
|
14
14
|
zillabyte.simple_function(\
|
15
|
+
# This specifies the function's name and is mandatory.
|
16
|
+
name = "simple_function", \
|
17
|
+
|
15
18
|
# This directive instructs zillabyte to give your function every
|
16
19
|
# web page in our known universe. Your function will have access
|
17
20
|
# to two fields: URL and HTML
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'zillabyte'
|
2
2
|
|
3
|
-
Zillabyte.
|
3
|
+
Zillabyte.simple_app do
|
4
4
|
|
5
|
-
# This specifies the
|
6
|
-
name "
|
5
|
+
# This specifies the app's name and is mandatory.
|
6
|
+
name "simple_app"
|
7
7
|
|
8
|
-
# This directive instructs zillabyte to give your
|
9
|
-
# web page in our known universe. Your
|
8
|
+
# This directive instructs zillabyte to give your app every
|
9
|
+
# web page in our known universe. Your app will have access
|
10
10
|
# to two fields: URL and HTML
|
11
11
|
matches "select * from web_pages"
|
12
12
|
|
13
|
-
# This directive tells Zillabyte what kind of data your
|
13
|
+
# This directive tells Zillabyte what kind of data your app
|
14
14
|
# produces. In this case, we're saying we will emit a tuple that
|
15
15
|
# is one-column wide and contains the field 'URL'
|
16
16
|
emits [
|
@@ -1,2 +1,2 @@
|
|
1
1
|
language: ruby
|
2
|
-
script:
|
2
|
+
script: simple_app.rb
|
data/lib/zillabyte/command.rb
CHANGED
@@ -208,9 +208,17 @@ module Zillabyte
|
|
208
208
|
object, method = prepare_run(cmd, arguments.dup)
|
209
209
|
object.send(method)
|
210
210
|
rescue Excon::Errors::SocketError => e
|
211
|
-
|
211
|
+
if ENV['BUBBLE_EXCEPTIONS']
|
212
|
+
raise e
|
213
|
+
else
|
214
|
+
error "remote server error: #{e.message}"
|
215
|
+
end
|
212
216
|
rescue Errno::ECONNREFUSED => e
|
213
|
-
|
217
|
+
if ENV['BUBBLE_EXCEPTIONS']
|
218
|
+
raise e
|
219
|
+
else
|
220
|
+
error "remote server error: #{e.message}"
|
221
|
+
end
|
214
222
|
end
|
215
223
|
end
|
216
224
|
|