zillabyte-cli 0.0.16 → 0.0.17
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 +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
|
|