zillabyte-cli 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module Zillabyte
2
2
  module CLI
3
- VERSION = "0.0.11"
3
+ VERSION = "0.0.12"
4
4
  end
5
5
  end
@@ -0,0 +1,5 @@
1
+ module Zillabyte
2
+ module CLI
3
+ VERSION = "0.0.11"
4
+ end
5
+ end
@@ -4,19 +4,35 @@ class Zillabyte::API::Data < Zillabyte::API::Base
4
4
 
5
5
  # GET /datasets/1
6
6
  def get(id, options = {})
7
-
8
7
  res = @api.request(
9
8
  :expects => 200,
10
9
  :method => :get,
11
10
  :path => "/relations/#{id}",
12
11
  :body => options.to_json
13
12
  )
14
-
15
13
  res.body
16
-
17
14
  end
18
15
 
16
+ def pull(id, options = {})
17
+ res = @api.request(
18
+ :expects => 200,
19
+ :method => :post,
20
+ :path => "/relations/#{id}/pull",
21
+ :body => options.to_json
22
+ )
23
+ res.body
24
+ end
19
25
 
26
+ def pull_to_s3(id, options)
27
+ res = @api.request(
28
+ :expects => 200,
29
+ :method => :post,
30
+ :path => "/relations/#{id}/pull_to_s3",
31
+ :body => options.to_json
32
+ )
33
+ res.body
34
+ end
35
+
20
36
  # POST /datasets
21
37
  def create(name, options = {})
22
38
 
@@ -63,9 +63,46 @@ class Zillabyte::API::Flows < Zillabyte::API::Base
63
63
 
64
64
  end
65
65
 
66
+ def delete(id, options ={})
67
+ res = @api.request(
68
+ :expects => 200,
69
+ :method => :delete,
70
+ :path => "/flows/#{id}",
71
+ :body => options.to_json
72
+ )
73
+ res.body
74
+ end
75
+
76
+ def list_cycles(id, options={})
77
+ res = @api.request(
78
+ :expects => 200,
79
+ :method => :get,
80
+ :path => "/flows/#{id}/cycles",
81
+ :body => options.to_json
82
+ )
83
+ res.body
84
+
85
+ end
66
86
 
87
+ def create_cycle(id, options ={})
88
+ res = @api.request(
89
+ :expects => 200,
90
+ :method => :post,
91
+ :path => "/flows/#{id}/cycles",
92
+ :body => options.to_json
93
+ )
94
+ res.body
95
+ end
67
96
 
68
-
97
+ def run_forever(id, options ={})
98
+ res = @api.request(
99
+ :expects => 200,
100
+ :method => :post,
101
+ :path => "/flows/#{id}/cycles/run_forever",
102
+ :body => options.to_json
103
+ )
104
+ res.body
105
+ end
69
106
 
70
107
 
71
108
 
@@ -5,6 +5,7 @@ require "pty"
5
5
  require 'indentation'
6
6
  require 'open3'
7
7
  require 'securerandom'
8
+ require 'colorize'
8
9
 
9
10
  # manage custom flows
10
11
  #
@@ -24,7 +25,7 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
24
25
  #
25
26
  def list
26
27
 
27
- headings = ["id", "name", "state"]
28
+ headings = ["id", "name", "state", "cycles"]
28
29
  rows = api.flow.list.map do |row|
29
30
  if headings.size == 0
30
31
  headings = row.keys
@@ -103,6 +104,33 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
103
104
  alias_command "pull", "flows:pull"
104
105
 
105
106
 
107
+ # flows:delete ID
108
+ #
109
+ # deletes a flow. if the flow is running, this command will kill it.
110
+ #
111
+ # -f, --force # don't ask for confirmation
112
+ def delete
113
+ id = options[:id] || shift_argument
114
+ forced = options[:force]
115
+ if not forced
116
+ while true
117
+
118
+ display "This operation cannot be undone. Are you sure you want to delete this flow? (yes/no):", false
119
+ confirm = ask
120
+ break if confirm == "yes" || confirm == "no"
121
+ display "Please enter 'yes' to delete the flow or 'no' to exit"
122
+ end
123
+ end
124
+
125
+ confirmed = forced || confirm == "yes"
126
+
127
+ if confirmed
128
+ response = api.flows.delete(id)
129
+ display response["body"]
130
+
131
+ end
132
+
133
+ end
106
134
 
107
135
 
108
136
  # flows:prep [DIR]
@@ -171,6 +199,72 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
171
199
 
172
200
 
173
201
 
202
+ # flow:show FLOW_ID [OPERATION]
203
+ #
204
+ # streams logs from the distributed workers
205
+ #
206
+ # -t, --tail # continuously watches for new results
207
+ # -v, --verbose LEVEL # sets the verbosity (error, info, debug) (default: info)
208
+ #
209
+ def logs
210
+
211
+ flow_id = options[:flow] || shift_argument
212
+ operation_id = options[:operation] || shift_argument || '_ALL_'
213
+ tail = options[:tail]
214
+ priority = options[:verbose] || 'debug'
215
+ carry_settings = {
216
+ :priority => priority
217
+ }
218
+ api_options = {}
219
+
220
+ error "no id given" if flow_id.nil?
221
+
222
+ begin
223
+
224
+ res = self.api.logs.get(flow_id, operation_id, api_options)
225
+
226
+ carry_settings = show_log_output(res, carry_settings)
227
+ api_options[:since] = carry_settings[:since]
228
+
229
+ if (tail)
230
+ sleep(5) # HACK
231
+ end
232
+ end while(tail)
233
+
234
+ end
235
+ alias_command "logs", "flows:logs"
236
+
237
+
238
+ # flow:cycles ID [OPTIONS]
239
+ #
240
+ # operations on the flow's cycles (batches).
241
+ # with no options, the command lists the flows cycles
242
+ # -n, --next # request the flow to move to the next cycle
243
+ # -f, --forever # don't wait on cycles any more
244
+ def cycles
245
+ flow_id = options[:id] || shift_argument
246
+ trigger_next = options[:next] || false
247
+ trigger_forever = options[:forever] || false
248
+ error "id required" if flow_id.nil?
249
+
250
+ if trigger_next
251
+ # Trigger the next flow
252
+ response = api.flows.create_cycle(flow_id)
253
+ elsif trigger_forever
254
+ response = api.flows.run_forever(flow_id)
255
+ else
256
+ # List the flows
257
+ response = api.flows.list_cycles(flow_id)
258
+
259
+ end
260
+
261
+
262
+ display response
263
+ end
264
+
265
+
266
+
267
+
174
268
  # flows:test [TEST_DATASET_ID]
175
269
  #
176
270
  # tests a local flow with sample data
@@ -307,15 +401,55 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
307
401
  end
308
402
 
309
403
  end
404
+
405
+ # An Aggregate?
406
+ elsif type == "aggregate"
407
+ if node['consumes']
408
+ input_stream = node['consumes']
409
+ else
410
+ input_stream = stream_messages.keys.first
411
+ end
412
+ messages = stream_messages[input_stream] || []
413
+ stream_messages[input_stream] = []
310
414
 
311
- end
312
-
313
-
314
-
415
+ group_by = node['group_by']
416
+ group_tuples = {}
417
+ messages.each do |msg|
418
+ msg = JSON.parse(msg)
419
+ tuple = msg["tuple"].to_json
420
+ meta = msg["meta"].to_json
421
+ column_aliases = msg["column_aliases"] || {}
422
+ aliases = Hash[column_aliases.map{|h| [h["alias"],h["concrete_name"]]}]
423
+ gt = {}
424
+ group_by.each do |field|
425
+ field_name = aliases[field] || field
426
+ gt[field] = msg["tuple"][field_name]
427
+ end
428
+
429
+ msg_no_brackets = "\"tuple\": #{tuple}, \"meta\": #{meta}, \"column_aliases\": #{column_aliases.to_json}"
430
+ if group_tuples[gt]
431
+ group_tuples[gt] << msg_no_brackets
432
+ else
433
+ group_tuples[gt] = [msg_no_brackets]
434
+ end
435
+ end
436
+
437
+ group_tuples.each do |group_tuple, tuples|
438
+ stream_messages[input_stream] << "{\"command\": \"begin_group\", \"tuple\": #{group_tuple.to_json}, \"meta\":{}}\n"
439
+ tuples.each do |t|
440
+ stream_messages[input_stream] << "{\"command\": \"aggregate\", #{t}}\n"
441
+ end
442
+ stream_messages[input_stream] << "{\"command\": \"end_group\"}\n"
443
+ end
444
+
315
445
  # A Sink?
316
- if type == "sink"
446
+ elsif type == "sink"
317
447
 
318
- if split_branches || stream_messages.size > 1
448
+ if split_branches || stream_messages.size > 1
449
+ split_branches = true
450
+ if node['consumes'].nil?
451
+ error "The node #{name} must declare which stream it 'consumes'"
452
+ end
319
453
  sink_stream = node["consumes"]
320
454
  messages = stream_messages[sink_stream] || []
321
455
  else
@@ -354,7 +488,7 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
354
488
 
355
489
 
356
490
 
357
- next
491
+ next
358
492
  end
359
493
 
360
494
 
@@ -491,6 +625,11 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
491
625
  end
492
626
  rescue PTY::ChildExited
493
627
  puts "The child process exited!"
628
+ ensure
629
+ # We need to check here to see if we need to turn on multistream mode.
630
+ # Note that we still need the redundant checks above, in case the user
631
+ # didn't honor the emits contract.
632
+ split_branches = true if node["emits"].size() > 1
494
633
  end
495
634
  end
496
635
 
@@ -517,7 +656,7 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
517
656
 
518
657
 
519
658
 
520
- # flows:live_run [DIR]
659
+ # flows:live_run [OPERATION_NAME] [PIPE_NAME] [DIR]
521
660
  #
522
661
  # runs a local flow with live data
523
662
  #
@@ -598,6 +737,66 @@ class Zillabyte::Command::Flows < Zillabyte::Command::Base
598
737
 
599
738
  private
600
739
 
740
+ def show_log_output(lines, settings = {})
741
+
742
+ @colors ||= [:green, :yellow, :magenta, :cyan, :light_black, :light_green, :light_yellow, :light_blue, :light_magenta, :light_cyan]
743
+
744
+ max_category_size = 0
745
+ all_lines = []
746
+
747
+ lines.each_pair do |operation, lines|
748
+ settings[operation] ||= {}
749
+ settings[operation][:color] ||= @colors.shift
750
+ lines.each do |line_h|
751
+
752
+ line = line_h['line']
753
+ priority = line_h['priority']
754
+ date = line_h['date']
755
+
756
+ all_lines << {
757
+ :line => line,
758
+ :category => operation,
759
+ :priority => priority,
760
+ :color => settings[operation][:color],
761
+ :date => date
762
+ }
763
+ max_category_size = [max_category_size, operation.size].max
764
+
765
+ if settings[:since]
766
+ settings[:since] = [date, settings[:since]].max
767
+ else
768
+ settings[:since] = date
769
+ end
770
+
771
+ end
772
+
773
+ end
774
+
775
+ all_lines.sort! do |a,b|
776
+ a[:date] <=> b[:date]
777
+ end
778
+
779
+ requested_priority = settings[:priority]
780
+
781
+ all_lines.each do |h|
782
+ color = h[:color]
783
+
784
+ case h[:priority]
785
+ when 'error'
786
+ color = :red if h[:priority] == 'error'
787
+ when 'info'
788
+ next if ['error'].member?(requested_priority)
789
+ when 'system'
790
+ when 'debug'
791
+ next if ['info', 'error'].member?(requested_priority)
792
+ end
793
+
794
+ display "#{h[:category].rjust(max_category_size)} #{h[:priority].rjust(6)} - #{h[:line]}".colorize(color)
795
+ end
796
+
797
+ settings
798
+ end
799
+
601
800
 
602
801
 
603
802
 
@@ -1,5 +1,7 @@
1
1
  require "zillabyte/cli/base"
2
2
  require "csv"
3
+ require "open-uri"
4
+ require "aws-sdk"
3
5
 
4
6
  # manage custom relations
5
7
  #
@@ -122,23 +124,25 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
122
124
  #
123
125
  # creates a new relation
124
126
  #
125
- # --schema SCHEMA # a JSON array of column types
126
- # --public # Make the relation public
127
- # --file # A data file
128
- # --type # type of data file [csv (default), xlsx]
127
+ # --schema SCHEMA # a JSON object of column types
128
+ # --public SCOPE # Make the relation public
129
+ # --file FILE # A data file
130
+ # --type FILE_TYPE # type of data file [csv (default), xlsx]
131
+ # --description DESCRIPTION # Description of relation contents
129
132
  #
130
133
  def create
131
134
 
132
135
  name = options[:name] || shift_argument
133
- file = options[:file] || shift_argument || nil
136
+ file = options[:file] || nil
134
137
  type = options[:type] || nil
135
138
 
136
139
  error "no name given" if name.nil?
137
140
 
138
- schema = JSON.parse(options[:schema]) if options[:schema]
139
- is_public = options[:public] || false
141
+ schema = options[:schema] if options[:schema]
142
+ is_public = options[:public] || nil
143
+ description = options[:description] || nil
140
144
 
141
- hash = set_relation_properties(schema,is_public)
145
+ hash = set_relation_properties(schema,is_public,description)
142
146
  if file
143
147
  type ||= File.extname(file).gsub(".", "")
144
148
  rows = sanity_check_file(file,type, {"columns" => hash[:schema]})
@@ -174,15 +178,96 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
174
178
  type ||= File.extname(file).gsub(".", "")
175
179
 
176
180
  relation = self.api.data.get(id)
177
- rows = sanity_check_file(file,type,relation)
181
+ columns = relation["columns"].map{|col| {col["index"] => col["type"]}}
182
+ rows = sanity_check_file(file,type,{"columns" => columns})
178
183
 
179
184
  res = self.api.data.append(id, {:rows => rows})
180
- display "relation #{id} appended"
185
+ display "relation ##{id} appended #{res["size"]} rows"
181
186
 
182
187
  end
183
188
  alias_command "append", "relations:append"
184
189
  alias_command "rl:append", "relations:append"
185
190
 
191
+ # relations:pull ID OUTPUT
192
+ #
193
+ # pulls relation data into OUTPUT
194
+ # if s3 parameters specified, pulls relation data to S3_BUCKET/OUTPUT/
195
+ #
196
+ # --type TYPE # interpret file as type [csv, xlsx]
197
+ #
198
+ def pull
199
+
200
+ id = options[:id] || shift_argument
201
+ file = options[:file] || shift_argument
202
+ error "no id given" if id.nil?
203
+ error "no output file given" if file.nil?
204
+ type = options[:type]
205
+ type ||= File.extname(file).gsub(".", "")
206
+
207
+ res = self.api.data.pull(id)
208
+ if(res["uri"])
209
+ File.open(file, "w") do |f|
210
+ f.write open(res["uri"]).read
211
+ end
212
+ elsif(res["s3_credentials"])
213
+ display "request sent...depending on the size of your file, this may take a while..."
214
+ s3 = AWS::S3.new(res["s3_credentials"])
215
+ bucket = s3.buckets[res["s3_bucket"]]
216
+ obj = bucket.objects.with_prefix("#{res["s3_file_key"]}/")
217
+ while(true)
218
+ keys = obj.collect(&:key)
219
+ if keys.length > 0 and keys.include?("#{res["s3_file_key"]}/manifest")
220
+ File.open(file, "w") do |f|
221
+ obj.each do |o|
222
+ if o.key == "#{res["s3_file_key"]}/manifest"
223
+ next
224
+ end
225
+ f.write(o.read)
226
+ end
227
+ end
228
+ break
229
+ else
230
+ sleep(3)
231
+ obj = bucket.objects.with_prefix("#{res["s3_file_key"]}/")
232
+ end
233
+ end
234
+ end
235
+ display "pulled data from relation ##{id} to file #{file}"
236
+
237
+ end
238
+ alias_command "rl:pull", "relations:pull"
239
+
240
+ # relations:pull:s3 ID S3_KEY S3_SECRET S3_BUCKET S3_FILE_PATH
241
+ #
242
+ # pulls relation data to S3_BUCKET/FILE_PATH/
243
+ #
244
+ # --type TYPE # interpret file as type [csv, xlsx]
245
+ #
246
+ def pull_to_s3
247
+
248
+ id = options[:id] || shift_argument
249
+ error "no id given" if id.nil?
250
+
251
+ user_s3_access_key = options[:s3_access_key] || shift_argument
252
+ user_s3_secret = options[:s3_secret] || shift_argument
253
+ user_s3_bucket = options[:s3_bucket] || shift_argument
254
+ user_s3_file_path = options[:s3_file_path] || shift_argument
255
+ error "no s3 access key provided" if user_s3_access_key.nil?
256
+ error "no s3 secret provided" if user_s3_secret.nil?
257
+ error "no s3 bucket provided" if user_s3_bucket.nil?
258
+ error "no s3 file path specified" if user_s3_file_path.nil?
259
+
260
+ s3_params = {:s3_access_key => user_s3_access_key, :s3_secret => user_s3_secret,
261
+ :s3_bucket => user_s3_bucket, :s3_object_key => user_s3_file_path}
262
+
263
+ res = self.api.data.pull_to_s3(id, s3_params)
264
+ display "downloading relation data to s3://#{res["s3_bucket"]}/#{res["s3_file_key"]}/"
265
+ display "if the relation is large, this may take a while, please check your s3 account after a few minutes"
266
+
267
+ end
268
+ alias_command "relations:pull:s3", "relations:pull_to_s3"
269
+ alias_command "rl:pull:s3", "relations:pull_to_s3"
270
+
186
271
 
187
272
  # relations:show ID
188
273
  #
@@ -246,11 +331,16 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
246
331
 
247
332
 
248
333
 
249
- def set_relation_properties(schema,is_public)
250
- valid_types=["STRING", "NUMERIC", "FLOAT"]
334
+ def set_relation_properties(schema,is_public,description)
335
+ if description.nil?
336
+ display "Enter a description for your relation (defaults to empty string)."
337
+ description = ask.strip
338
+ end
339
+
340
+ valid_types=["STRING", "INTEGER", "FLOAT"]
251
341
  meta_names=["confidence", "since", "source"]
252
342
  if schema.nil?
253
- schema = []
343
+ schema = ""
254
344
 
255
345
  # Get the column types (required)
256
346
  index = 1
@@ -261,7 +351,7 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
261
351
  display "Column #{index} name (empty when done): ", false
262
352
  cname = ask.strip
263
353
  break if cname == "" || cname.nil?
264
- if((cname[0].downcase == "v" and cname[1...-1].respond_to?("to_i") and cname[1...-1].to_i >= 0) or meta_names.member?(cname.downcase))
354
+ if(cname =~ /^v[0-9]+$/i or meta_names.member?(cname.downcase))
265
355
  warn "\"v[number]\", \"confidence\", \"since\" and \"source\" are special names in Zillabyte. Please name your column name something else."
266
356
  next
267
357
  end
@@ -277,27 +367,64 @@ class Zillabyte::Command::Relations < Zillabyte::Command::Base
277
367
  next
278
368
  end
279
369
  index += 1
280
- schema << {cname => ctype}
370
+ schema += "#{cname}:#{ctype},"
281
371
  already_aliased[cname] = 1
372
+ end
373
+ schema = schema[0...-1]
374
+ end
375
+ schema = schema.split(",").map { |x| Hash[*x.split(":")] }
376
+ display "using schema: #{schema.to_json}"
377
+
378
+ if is_public.nil?
379
+ while true
380
+ display "Would you like your relation to be public? (yes or no)"
381
+ pp = ask.strip.downcase
382
+ if(pp == "yes" or pp == "y")
383
+ is_public = "public"
384
+ break
385
+ elsif(pp == "no" or pp == "n")
386
+ is_public = "private"
387
+ break
388
+ end
282
389
  end
283
-
284
- display "using schema: #{schema.to_json}"
285
-
286
390
  end
287
- {:schema => schema, :public => is_public}
391
+
392
+ {:schema => schema, :public => is_public, :description => description}
288
393
  end
289
394
 
290
395
  def sanity_check_file(file,type,dataset)
291
396
  rows = []
292
-
397
+
398
+ n_columns = dataset["columns"].size
399
+ float_cols = []
400
+ int_cols = []
401
+ (0...n_columns).each do |i|
402
+ value = dataset["columns"][i].values[0].downcase
403
+ # use integer instead of numeric to be consistent with multilang type declarations
404
+ if(value == 'integer')
405
+ int_cols << i
406
+ elsif(value == "float")
407
+ float_cols << i
408
+ end
409
+ end
410
+
293
411
  case type
294
412
  when "csv"
295
413
  CSV.foreach(file) do |row|
296
- if row.size != dataset['columns'].size
297
- puts "relation expect #{dataset['columns'].size} column(s). Found a row with #{row.size}::\n #{row}"
298
- else
299
- rows << row
414
+ if row.size != n_columns
415
+ error "relation expect #{n_columns} column(s). Found a row with #{row.size}::\n #{row}"
416
+ end
417
+ float_cols.each do |i|
418
+ if(!(row[i] =~ /^[+-]?(\d+(.\d+)?)$/))
419
+ error "column #{i+1} should be a FLOAT in row::\n #{row}"
420
+ end
421
+ end
422
+ int_cols.each do |i|
423
+ if(!(row[i] =~ /^[+-]?(\d+)$/))
424
+ error "column #{i+1} should be an INTEGER in row::\n #{row}"
425
+ end
300
426
  end
427
+ rows << row
301
428
  end
302
429
 
303
430
  else
@@ -9,8 +9,8 @@ function prep(controller) {
9
9
  * of machines.
10
10
  */
11
11
  function exec(controller, tuple) {
12
- if(tuple.values["html"].indexOf("hello world") !== -1) {
13
- controller.emit("has_hello_world",{"url":tuple.values["url"]});
12
+ if(tuple["html"].indexOf("hello world") !== -1) {
13
+ controller.emit("has_hello_world",{"url":tuple["url"]});
14
14
  }
15
15
  }
16
16
 
@@ -7,8 +7,8 @@ def prep(controller):
7
7
  # web page. This algorithm is run in parallel on possibly hundreds
8
8
  # of machines.
9
9
  def execute(controller, tup):
10
- if("hello world" in tup.values["html"]):
11
- controller.emit("has_hello_world",{"url":tup.values["url"]})
10
+ if("hello world" in tup["html"]):
11
+ controller.emit("has_hello_world",{"url":tup["url"]})
12
12
  return
13
13
 
14
14
  zillabyte.simple_function(\
@@ -0,0 +1,37 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activesupport (3.2.17)
5
+ i18n (~> 0.6, >= 0.6.4)
6
+ multi_json (~> 1.0)
7
+ ascii_charts (0.9.1)
8
+ chronic (0.10.2)
9
+ colorize (0.6.0)
10
+ excon (0.31.0)
11
+ i18n (0.6.9)
12
+ indentation (0.1.1)
13
+ mime-types (2.1)
14
+ multi_json (1.8.4)
15
+ netrc (0.7.7)
16
+ rest-client (1.6.7)
17
+ mime-types (>= 1.16)
18
+ terminal-table (1.4.5)
19
+ zillabyte (0.0.9)
20
+ zillabyte-cli
21
+ zillabyte-cli (0.0.10)
22
+ activesupport (~> 3.2.11)
23
+ ascii_charts (~> 0.9.1)
24
+ bundler (~> 1.3)
25
+ chronic (~> 0.10)
26
+ colorize (~> 0.6)
27
+ excon (~> 0.31)
28
+ indentation (~> 0.1)
29
+ netrc (~> 0.7.7)
30
+ rest-client (~> 1.6.1)
31
+ terminal-table (~> 1.4)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ zillabyte
@@ -19,15 +19,15 @@ Zillabyte.simple_function do |fn|
19
19
  fn.execute do |controller, tuple|
20
20
 
21
21
  # get the fields
22
- url = tuple['v0']
23
- html = tuple['v1']
22
+ url = tuple['url']
23
+ html = tuple['html']
24
24
 
25
25
  # For the purpose of this test, to show results from the test set,
26
26
  # we'll loosen the search to include either hello or world
27
27
  # instead of
28
28
  # if html.include?('hello world')
29
- if html.include?('hello') or html.include?('world')
30
- controller.emit("URL" => url)
29
+ if html.include?('hello world')
30
+ controller.emit("has_hello_world", "URL" => url)
31
31
  end
32
32
 
33
33
  end
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "chronic", "~> 0.10"
35
35
  spec.add_dependency "ascii_charts", "~> 0.9.1"
36
36
  spec.add_dependency "indentation", "~> 0.1"
37
+ spec.add_dependency "aws-sdk", "~> 1.33.0"
37
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zillabyte-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-02-15 00:00:00.000000000 Z
12
+ date: 2014-02-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -187,6 +187,22 @@ dependencies:
187
187
  - - ~>
188
188
  - !ruby/object:Gem::Version
189
189
  version: '0.1'
190
+ - !ruby/object:Gem::Dependency
191
+ name: aws-sdk
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ~>
196
+ - !ruby/object:Gem::Version
197
+ version: 1.33.0
198
+ type: :runtime
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ~>
204
+ - !ruby/object:Gem::Version
205
+ version: 1.33.0
190
206
  description: The Official Zillabyte CLI Gem
191
207
  email:
192
208
  - gem@zillabyte.com
@@ -217,7 +233,6 @@ files:
217
233
  - lib/zillabyte/cli/help.rb
218
234
  - lib/zillabyte/cli/helpers/data_schema_builder.rb
219
235
  - lib/zillabyte/cli/host.rb
220
- - lib/zillabyte/cli/logs.rb
221
236
  - lib/zillabyte/cli/query.rb
222
237
  - lib/zillabyte/cli/relations.rb
223
238
  - lib/zillabyte/cli/sources.rb
@@ -227,6 +242,7 @@ files:
227
242
  - lib/zillabyte/cli/templates/python/simple_function.py
228
243
  - lib/zillabyte/cli/templates/python/zillabyte.conf.yaml
229
244
  - lib/zillabyte/cli/templates/ruby/Gemfile
245
+ - lib/zillabyte/cli/templates/ruby/Gemfile.lock
230
246
  - lib/zillabyte/cli/templates/ruby/simple_function.rb
231
247
  - lib/zillabyte/cli/templates/ruby/zillabyte.conf.yaml
232
248
  - lib/zillabyte/cli/version.rb
@@ -239,6 +255,7 @@ files:
239
255
  - lib/zillabyte/helpers.rb
240
256
  - lib/zillabyte/queries.rb
241
257
  - lib/zillabyte-cli/version.rb
258
+ - lib/zillabyte-cli/version.rb~
242
259
  - lib/zillabyte-cli.rb
243
260
  - LICENSE
244
261
  - README.md
@@ -1,117 +0,0 @@
1
- require "zillabyte/cli/base"
2
- require "pty"
3
- require 'colorize'
4
-
5
- # manage custom logs
6
- #
7
- class Zillabyte::Command::Logs < Zillabyte::Command::Base
8
-
9
-
10
-
11
-
12
-
13
- # logs:show FLOW_ID [OPERATION]
14
- #
15
- # shows the logs
16
- #
17
- # -t, --tail # continuously watches for new results
18
- # -v, --verbose LEVEL # sets the verbosity (error, info, debug) (default: info)
19
- #
20
- def show
21
-
22
- flow_id = options[:flow] || shift_argument
23
- operation_id = options[:operation] || shift_argument || '_ALL_'
24
- tail = options[:tail]
25
- priority = options[:verbose] || 'debug'
26
- carry_settings = {
27
- :priority => priority
28
- }
29
- api_options = {}
30
-
31
- error "no id given" if flow_id.nil?
32
-
33
- begin
34
-
35
- res = self.api.logs.get(flow_id, operation_id, api_options)
36
-
37
- carry_settings = show_log_output(res, carry_settings)
38
- api_options[:since] = carry_settings[:since]
39
-
40
- if (tail)
41
- sleep(5) # HACK
42
- end
43
- end while(tail)
44
-
45
- end
46
- alias_command "logs", "logs:show"
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
- private
55
-
56
- def show_log_output(lines, settings = {})
57
-
58
- @colors ||= [:green, :yellow, :magenta, :cyan, :light_black, :light_green, :light_yellow, :light_blue, :light_magenta, :light_cyan]
59
-
60
- max_category_size = 0
61
- all_lines = []
62
-
63
- lines.each_pair do |operation, lines|
64
- settings[operation] ||= {}
65
- settings[operation][:color] ||= @colors.shift
66
- lines.each do |line_h|
67
-
68
- line = line_h['line']
69
- priority = line_h['priority']
70
- date = line_h['date']
71
-
72
- all_lines << {
73
- :line => line,
74
- :category => operation,
75
- :priority => priority,
76
- :color => settings[operation][:color],
77
- :date => date
78
- }
79
- max_category_size = [max_category_size, operation.size].max
80
-
81
- if settings[:since]
82
- settings[:since] = [date, settings[:since]].max
83
- else
84
- settings[:since] = date
85
- end
86
-
87
- end
88
-
89
- end
90
-
91
- all_lines.sort! do |a,b|
92
- a[:date] <=> b[:date]
93
- end
94
-
95
- requested_priority = settings[:priority]
96
-
97
- all_lines.each do |h|
98
- color = h[:color]
99
-
100
- case h[:priority]
101
- when 'error'
102
- color = :red if h[:priority] == 'error'
103
- when 'info'
104
- next if ['error'].member?(requested_priority)
105
- when 'system'
106
- when 'debug'
107
- next if ['info', 'error'].member?(requested_priority)
108
- end
109
-
110
- display "#{h[:category].rjust(max_category_size)} #{h[:priority].rjust(6)} - #{h[:line]}".colorize(color)
111
- end
112
-
113
- settings
114
- end
115
-
116
-
117
- end