MrMurano 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9ba16e5f2344bf987358a56dada5f3e3b8c4cc88
4
- data.tar.gz: 6528a533043deae3b1e81493e52fee94772506c1
3
+ metadata.gz: 02d6e091a69dd09c071539a97a2b9d3755ad9202
4
+ data.tar.gz: 46f2c3f6836c39648de3c08beef0eaf7c4ceb54b
5
5
  SHA512:
6
- metadata.gz: c66eccf67ca91de551b15493faa4059c648eb83f60c6b6fc39839a6938ab91a35795e3291c3630df1ac660cb0fcb0ff82b96afcea8353b84ef5a0d1855f1cfbe
7
- data.tar.gz: 29c38a617d405be2c6d732e19369c4f46147f98ffa82bc86394ffaabc34693d7696fcd38df139506732829948f9f26c3090650adf381b1fb49f57ccb606fbe34
6
+ metadata.gz: fa6b24a0ce013cb732a5563efbe86c54bbeb2cb9264cf69cc0dd1a7a43528f3fb22ef005af188ce7400ac2a7be40a9f716d850b395a24076edd550d8faf47d5c
7
+ data.tar.gz: 2633547b8be35f46ee9239b3092f8d6ebb7242fea6e8c62c53bdf9df93c461b57c22945a8fbabc556d054d64d85208052b8545d8a59bae85c464532e2b9ab389
data/TODO.taskpaper CHANGED
@@ -2,12 +2,13 @@ Readme:
2
2
  - Add more walk-thrus of common actions. (create new solution, …)
3
3
 
4
4
  Commands:
5
- - Status will show things have changes when they don’t.
5
+ - Status will show {modules,eventhandlers} have changes when they don’t.
6
6
  - First time run needs to be smoothed out. @done(2016-08-09)
7
7
  - Default for most commands should be -same @done(2016-08-02)
8
8
  - Add Diff Command @done(2016-07-27)
9
9
 
10
10
  Account:
11
+ - token is refetched many times. Do this once per run. @done(2016-09-12)
11
12
  - Netrc library (or the netrc format) doesn't allow '#' in passwords. @done(2016-08-10)
12
13
 
13
14
  Endpoints:
@@ -31,9 +32,15 @@ CORS:
31
32
  - Get working with sync up/down.
32
33
  - GET&PUT /cors data @done(2016-09-08)
33
34
 
35
+ Timeseries:
36
+ - Add CSV output option. @done(2016-09-09)
37
+
34
38
  Product:
35
39
  - Need to add way to set the product ID on a device eventhandler. @done(2016-08-01)
36
40
 
41
+ Service Device:
42
+ - When listing and bussiness.id is missing, gracefully fall back to --idonly @done(2016-09-12)
43
+
37
44
  Config:
38
45
  - Think about adding dev,staging,prod system; how would that work?
39
46
  Would it work ok to just use the --configfile option?
@@ -43,6 +50,7 @@ Config:
43
50
  - Maybe add dotenv support.
44
51
 
45
52
  SolutionBase:
53
+ - Errors from the server should be displayed prettier.
46
54
  - All network traffic is serialized. Make some parallel.
47
55
  - JSON parse should use symbols for keys. @done(2016-09-01)
48
56
  - Add the --curl verbose option. @done(2016-08-12)
@@ -35,10 +35,10 @@ module MrMurano
35
35
  end
36
36
  hd = @data[host]
37
37
  if hd.nil? or not hd.kind_of?(Hash) then
38
- hd = {user=>pass}
38
+ @data[host] = {user=>pass}
39
39
  return
40
40
  end
41
- hd[user] = pass
41
+ @data[host][user] = pass
42
42
  return
43
43
  end
44
44
  def get(host, user)
@@ -51,14 +51,7 @@ module MrMurano
51
51
  end
52
52
 
53
53
  class Account
54
- def initialize
55
- @json_opts = {
56
- :allow_nan => true,
57
- :symbolize_names => true,
58
- :create_additions => false
59
- }
60
- end
61
-
54
+ include Http
62
55
 
63
56
  def endPoint(path)
64
57
  URI('https://' + $cfg['net.host'] + '/api:1/' + path.to_s)
@@ -86,84 +79,46 @@ module MrMurano
86
79
  }
87
80
  end
88
81
 
82
+ # Store the token in a class variable so that we only fetch it once per run
83
+ # session of this tool
84
+ @@token = nil
89
85
  def token
90
- if @token.nil? then
91
- r = endPoint('token/')
92
- Net::HTTP.start(r.host, r.port, :use_ssl=>true) do |http|
93
- request = Net::HTTP::Post.new(r)
94
- request.content_type = 'application/json'
95
- #request.basic_auth(username(), password())
96
- request.body = JSON.generate(_loginInfo)
97
-
98
- response = http.request(request)
99
- case response
100
- when Net::HTTPSuccess
101
- token = JSON.parse(response.body, @json_opts)
102
- @token = token[:token]
103
- else
104
- say_error "No token! because: #{response}"
105
- @token = nil
106
- raise response
107
- end
108
- end
109
- end
110
- @token
111
- end
86
+ if @@token.nil? then
87
+ # Cannot have token call token, so cannot use workit.
88
+ uri = endPoint('token/')
89
+ request = Net::HTTP::Post.new(uri)
90
+ curldebug(request)
112
91
 
113
- def businesses
114
- r = endPoint('user/' + $cfg['user.name'] + '/membership/')
115
- Net::HTTP.start(r.host, r.port, :use_ssl=>true) do |http|
116
- request = Net::HTTP::Get.new(r)
117
92
  request.content_type = 'application/json'
118
- request['authorization'] = 'token ' + token
93
+ #request.basic_auth(username(), password())
94
+ request.body = JSON.generate(_loginInfo)
119
95
 
120
96
  response = http.request(request)
121
97
  case response
122
98
  when Net::HTTPSuccess
123
- busy = JSON.parse(response.body, @json_opts)
124
- return busy
99
+ token = JSON.parse(response.body, json_opts)
100
+ @@token = token[:token]
125
101
  else
102
+ say_error "No token! because: #{response}"
103
+ @@token = nil
126
104
  raise response
127
105
  end
128
106
  end
107
+ @@token
108
+ end
109
+
110
+ def businesses
111
+ get('user/' + $cfg['user.name'] + '/membership/')
129
112
  end
130
113
 
131
114
  def products
132
115
  raise "Missing Bussiness ID" if $cfg['business.id'].nil?
133
- r = endPoint('business/' + $cfg['business.id'] + '/product/')
134
- Net::HTTP.start(r.host, r.port, :use_ssl=>true) do |http|
135
- request = Net::HTTP::Get.new(r)
136
- request.content_type = 'application/json'
137
- request['authorization'] = 'token ' + token
138
-
139
- response = http.request(request)
140
- case response
141
- when Net::HTTPSuccess
142
- busy = JSON.parse(response.body, @json_opts)
143
- return busy
144
- else
145
- raise response
146
- end
147
- end
116
+ get('business/' + $cfg['business.id'] + '/product/')
148
117
  end
149
118
 
150
119
  def solutions
151
120
  raise "Missing Bussiness ID" if $cfg['business.id'].nil?
152
- r = endPoint('business/' + $cfg['business.id'] + '/solution/')
153
- Net::HTTP.start(r.host, r.port, :use_ssl=>true) do |http|
154
- request = Net::HTTP::Get.new(r)
155
- request.content_type = 'application/json'
156
- request['authorization'] = 'token ' + token
157
-
158
- response = http.request(request)
159
- case response
160
- when Net::HTTPSuccess
161
- busy = JSON.parse(response.body, @json_opts)
162
- return busy
163
- else
164
- raise response
165
- end
166
- end
121
+ get('business/' + $cfg['business.id'] + '/solution/')
167
122
  end
168
123
 
169
124
  end
@@ -20,7 +20,7 @@ module MrMurano
20
20
 
21
21
  def fetch(id)
22
22
  ret = get('/' + id.to_s)
23
- aheader = ret[:script].lines.first.chomp
23
+ aheader = (ret[:script].lines.first or "").chomp
24
24
  dheader = /^--#ENDPOINT (?i:#{ret[:method]}) #{ret[:path]}$/
25
25
  rheader = %{--#ENDPOINT #{ret[:method]} #{ret[:path]}\n}
26
26
  if block_given? then
@@ -86,7 +86,11 @@ module MrMurano
86
86
  path = Pathname.new(path) unless path.kind_of? Pathname
87
87
  aheader = path.readlines().first
88
88
  md = /--#ENDPOINT (\S+) (.*)/.match(aheader)
89
- raise "Not an Endpoint: #{path.to_s}" if md.nil?
89
+ if md.nil? then
90
+ rp = path.relative_path_from(Pathname.new(Dir.pwd))
91
+ say_warning "Not an Endpoint: #{rp.to_s}"
92
+ return nil
93
+ end
90
94
  {:method=>md[1], :path=>md[2]}
91
95
  end
92
96
 
@@ -125,12 +125,13 @@ module MrMurano
125
125
  #sha1 = Digest::SHA1.file(path.to_s).hexdigest
126
126
  def hexit(str)
127
127
  ret=''
128
+ # TODO: find a faster way to do this.
128
129
  str.each_byte{|b| ret << "%02x" % b}
129
130
  ret
130
131
  end
131
132
  sha1 = Digest::SHA1.new
132
133
  path.open('rb:ASCII-8BIT') do |io|
133
- while chunk = io.read(4096) do
134
+ while chunk = io.read(1048576) do
134
135
  sha1 << hexit(chunk)
135
136
  end
136
137
  end
@@ -25,23 +25,23 @@ module MrMurano
25
25
  return @scid unless @scid.nil?
26
26
  @scid = scid_for_name(@serviceName)
27
27
  end
28
+ end
28
29
 
29
- # Below is Device ServiceConfig Specific. Should it be in its own class?
30
+ class SC_Device < ServiceConfig
31
+ def initialize
32
+ super
33
+ @serviceName = 'device'
34
+ end
30
35
 
31
36
  def assignTriggers(products)
32
- scid = scid_for_name('device')
33
-
34
37
  details = fetch(scid)
35
38
  products = [products] unless products.kind_of? Array
36
- details[:triggers] = {:pid=>products}
39
+ details[:triggers] = {:pid=>products, :vendor=>products}
37
40
 
38
41
  put('/'+scid, details)
39
-
40
42
  end
41
43
 
42
44
  def showTriggers
43
- scid = scid_for_name('device')
44
-
45
45
  details = fetch(scid)
46
46
 
47
47
  return [] if details[:triggers].nil?
@@ -51,46 +51,55 @@ module MrMurano
51
51
  end
52
52
  end
53
53
 
54
- command :assign do |c|
55
- c.syntax = 'mr assign [product]'
56
- c.description = 'Assign a product to a eventhandler'
57
-
58
- c.option '--list', %{List assigned products}
54
+ command 'assign list' do |c|
55
+ c.syntax = 'mr assign list [options]'
56
+ c.description = 'List the products that are assigned'
59
57
  c.option '--idonly', 'Only return the ids'
60
-
61
58
  c.action do |args, options|
62
- sol = MrMurano::ServiceConfig.new
59
+ sol = MrMurano::SC_Device.new
63
60
 
64
- if options.list then
65
- trigs = sol.showTriggers()
66
- if options.idonly then
61
+ trigs = sol.showTriggers()
62
+ if options.idonly or $cfg['business.id'].nil? then
63
+ say trigs.join(' ')
64
+ else
65
+ acc = MrMurano::Account.new
66
+ products = acc.products
67
+ products.select!{|p| trigs.include? p[:modelId] }
68
+ if products.empty? then
67
69
  say trigs.join(' ')
68
70
  else
69
- acc = MrMurano::Account.new
70
- products = acc.products
71
- products.select!{|p| trigs.include? p[:pid] }
72
71
  busy = products.map{|r| [r[:label], r[:type], r[:pid], r[:modelId]]}
73
72
  table = Terminal::Table.new :rows => busy, :headings => ['Label', 'Type', 'PID', 'ModelID']
74
73
  say table
75
74
  end
76
-
77
- else
78
- prname = args.shift
79
- if prname.nil? then
80
- prid = $cfg['product.id']
81
- else
82
- acc = MrMurano::Account.new
83
- products = acc.products
84
- products.select!{|p| p[:label] == prname or p[:pid] == prname }
85
- prid = products.map{|p| p[:pid]}
86
- end
87
- raise "No product ID!" if prid.nil?
88
- say "Assigning #{prid} to solution" if $cfg['tool.verbose']
89
- sol.assignTriggers(prid) unless $cfg['tool.dry']
90
75
  end
76
+ end
77
+ end
78
+ alias_command :assign, 'assign list'
91
79
 
80
+ command 'assign set' do |c|
81
+ c.syntax = 'mr assign set [product]'
82
+ c.description = 'Assign a product to a eventhandler'
92
83
 
84
+ c.action do |args, options|
85
+ sol = MrMurano::SC_Device.new
86
+
87
+ prname = args.shift
88
+ if prname.nil? then
89
+ prid = $cfg['product.id']
90
+ else
91
+ acc = MrMurano::Account.new
92
+ products = acc.products
93
+ products.select!{|p|
94
+ p[:label] == prname or p[:modelId] == prname or p[:pid] == prname
95
+ }
96
+ prid = products.map{|p| p[:modelId]}
97
+ end
98
+ raise "No product ID!" if prid.nil?
99
+ say "Assigning #{prid} to solution" if $cfg['tool.verbose']
100
+ sol.assignTriggers(prid) unless $cfg['tool.dry']
93
101
  end
102
+
94
103
  end
95
104
 
96
105
  # vim: set ai et sw=2 ts=2 :
@@ -153,7 +153,11 @@ module MrMurano
153
153
  path = Pathname.new(path) unless path.kind_of? Pathname
154
154
  aheader = path.readlines().first
155
155
  md = /--#EVENT (\S+) (\S+)/.match(aheader)
156
- raise "Not an Event handler: #{path.to_s}" if md.nil?
156
+ if md.nil? then
157
+ rp = path.relative_path_from(Pathname.new(Dir.pwd))
158
+ say_warning "Not an Event handler: #{rp}"
159
+ return nil
160
+ end
157
161
  {:service=>md[1], :event=>md[2]}
158
162
  end
159
163
 
@@ -9,110 +9,27 @@ module MrMurano
9
9
  class SolutionBase
10
10
  # This might also be a valid ProductBase.
11
11
  def initialize
12
- @token = Account.new.token
13
- raise "Not logged in!" if @token.nil?
14
12
  @sid = $cfg['solution.id']
15
13
  raise "No solution!" if @sid.nil?
16
14
  @uriparts = [:solution, @sid]
17
15
  @itemkey = :id
18
16
  @locationbase = $cfg['location.base']
19
17
  @location = nil
20
- @json_opts = {
21
- :allow_nan => true,
22
- :symbolize_names => true,
23
- :create_additions => false
24
- }
25
18
  end
26
19
 
20
+ include Http
21
+
27
22
  def verbose(msg)
28
23
  if $cfg['tool.verbose'] then
29
24
  say msg
30
25
  end
31
26
  end
32
27
 
33
- def curldebug(request)
34
- if $cfg['tool.curldebug'] then
35
- a = []
36
- a << %{curl -s -H 'Authorization: #{request['authorization']}'}
37
- a << %{-H 'User-Agent: #{request['User-Agent']}'}
38
- a << %{-H 'Content-Type: #{request.content_type}'}
39
- a << %{-X #{request.method}}
40
- a << %{'#{request.uri.to_s}'}
41
- a << %{-d '#{request.body}'} unless request.body.nil?
42
- puts a.join(' ')
43
- end
44
- end
45
-
46
28
  def endPoint(path='')
47
29
  parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
48
30
  s = parts.map{|v| v.to_s}.join('/')
49
31
  URI(s + path.to_s)
50
32
  end
51
- def http
52
- uri = URI('https://' + $cfg['net.host'])
53
- if @http.nil? then
54
- @http = Net::HTTP.new(uri.host, uri.port)
55
- @http.use_ssl = true
56
- @http.start
57
- end
58
- @http
59
- end
60
-
61
- def set_req_defaults(request)
62
- request.content_type = 'application/json'
63
- request['authorization'] = 'token ' + @token
64
- request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
65
- request
66
- end
67
-
68
- def workit(request, &block)
69
- set_req_defaults(request)
70
- curldebug(request)
71
- if block_given? then
72
- yield request, http()
73
- else
74
- response = http().request(request)
75
- case response
76
- when Net::HTTPSuccess
77
- return {} if response.body.nil?
78
- begin
79
- return JSON.parse(response.body, @json_opts)
80
- rescue
81
- return response.body
82
- end
83
- else
84
- say_error "got #{response} from #{request} #{request.uri.to_s}"
85
- say_error ":: #{response.body}"
86
- say_error '==='
87
- raise response
88
- end
89
- end
90
- end
91
-
92
- def get(path='', &block)
93
- uri = endPoint(path)
94
- workit(Net::HTTP::Get.new(uri), &block)
95
- end
96
-
97
- def post(path='', body={}, &block)
98
- uri = endPoint(path)
99
- req = Net::HTTP::Post.new(uri)
100
- req.body = JSON.generate(body)
101
- workit(req, &block)
102
- end
103
-
104
- def put(path='', body={}, &block)
105
- uri = endPoint(path)
106
- req = Net::HTTP::Put.new(uri)
107
- req.body = JSON.generate(body)
108
- workit(req, &block)
109
- end
110
-
111
- def delete(path='', &block)
112
- uri = endPoint(path)
113
- workit(Net::HTTP::Delete.new(uri), &block)
114
- end
115
-
116
33
  # …
117
34
 
118
35
  ##
@@ -215,9 +132,11 @@ module MrMurano
215
132
  # sometimes this is a name, sometimes it is an item.
216
133
  # do I want to keep that? NO.
217
134
  name = toRemoteItem(from, path)
218
- name[:local_path] = path
219
- name
220
- end
135
+ unless name.nil? then
136
+ name[:local_path] = path
137
+ name
138
+ end
139
+ end.flatten.compact
221
140
  end
222
141
 
223
142
  def synckey(item)
@@ -0,0 +1,103 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'json'
4
+
5
+ module MrMurano
6
+ module Http
7
+ def token
8
+ return @token unless @token.nil?
9
+ @token = Account.new.token
10
+ raise "Not logged in!" if @token.nil?
11
+ @token
12
+ end
13
+
14
+ def json_opts
15
+ return @json_opts unless @json_opts.nil?
16
+ @json_opts = {
17
+ :allow_nan => true,
18
+ :symbolize_names => true,
19
+ :create_additions => false
20
+ }
21
+ end
22
+
23
+ def curldebug(request)
24
+ if $cfg['tool.curldebug'] then
25
+ a = []
26
+ a << %{curl -s -H 'Authorization: #{request['authorization']}'}
27
+ a << %{-H 'User-Agent: #{request['User-Agent']}'}
28
+ a << %{-H 'Content-Type: #{request.content_type}'}
29
+ a << %{-X #{request.method}}
30
+ a << %{'#{request.uri.to_s}'}
31
+ a << %{-d '#{request.body}'} unless request.body.nil?
32
+ puts a.join(' ')
33
+ end
34
+ end
35
+
36
+ def http
37
+ uri = URI('https://' + $cfg['net.host'])
38
+ if @http.nil? then
39
+ @http = Net::HTTP.new(uri.host, uri.port)
40
+ @http.use_ssl = true
41
+ @http.start
42
+ end
43
+ @http
44
+ end
45
+
46
+ def set_req_defaults(request)
47
+ request.content_type = 'application/json'
48
+ request['authorization'] = 'token ' + token
49
+ request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
50
+ request
51
+ end
52
+
53
+ def workit(request, &block)
54
+ set_req_defaults(request)
55
+ curldebug(request)
56
+ if block_given? then
57
+ yield request, http()
58
+ else
59
+ response = http().request(request)
60
+ case response
61
+ when Net::HTTPSuccess
62
+ return {} if response.body.nil?
63
+ begin
64
+ return JSON.parse(response.body, json_opts)
65
+ rescue
66
+ return response.body
67
+ end
68
+ else
69
+ say_error "got #{response} from #{request} #{request.uri.to_s}"
70
+ say_error ":: #{response.body}"
71
+ say_error '==='
72
+ raise response
73
+ end
74
+ end
75
+ end
76
+
77
+ def get(path='', &block)
78
+ uri = endPoint(path)
79
+ workit(Net::HTTP::Get.new(uri), &block)
80
+ end
81
+
82
+ def post(path='', body={}, &block)
83
+ uri = endPoint(path)
84
+ req = Net::HTTP::Post.new(uri)
85
+ req.body = JSON.generate(body)
86
+ workit(req, &block)
87
+ end
88
+
89
+ def put(path='', body={}, &block)
90
+ uri = endPoint(path)
91
+ req = Net::HTTP::Put.new(uri)
92
+ req.body = JSON.generate(body)
93
+ workit(req, &block)
94
+ end
95
+
96
+ def delete(path='', &block)
97
+ uri = endPoint(path)
98
+ workit(Net::HTTP::Delete.new(uri), &block)
99
+ end
100
+
101
+ end
102
+ end
103
+ # vim: set ai et sw=2 ts=2 :
@@ -87,6 +87,7 @@ alias_command 'keystore rm', 'keystore delete'
87
87
 
88
88
  command 'keystore command' do |c|
89
89
  c.syntax = %{mr keystore command <key> <command> <args...>}
90
+ c.summary = %{Call some Redis commands in the Keystore}
90
91
  c.description = %{Call some Redis commands in the Keystore.
91
92
 
92
93
  Only a subset of all Redis commands is supported.
data/lib/MrMurano/logs.rb CHANGED
@@ -55,6 +55,9 @@ command :logs do |c|
55
55
  elsif line.kind_of?(Hash) then
56
56
  out=""
57
57
 
58
+ if line.has_key?(:type) then
59
+ out << "#{line[:type]} ".upcase.color(:red).background(:aliceblue)
60
+ end
58
61
  out << "[#{line[:subject]}]".color(:red).background(:aliceblue)
59
62
  out << " "
60
63
  if options.localtime then
@@ -63,24 +66,37 @@ command :logs do |c|
63
66
  curtime = Time.at(line[:timestamp]).to_datetime.iso8601(3)
64
67
  end
65
68
  out << curtime.color(:blue)
66
- out << ":"
67
- out << "\n---------\nrequest:"
68
- if options.pretty then
69
- ret = JSON.pretty_generate(line[:data][:request]).to_s
70
- ret[0] = ret[0].color(:magenta)
71
- ret[-1] = ret[-1].color(:magenta)
72
- out << ret
73
- else
74
- out << line[:data][:request].to_json
75
- end
76
- out << "\n---------\nresponse:"
77
- if options.pretty then
78
- ret = JSON.pretty_generate(line[:data][:response]).to_s
79
- ret[0] = ret[0].color(:magenta)
80
- ret[-1] = ret[-1].color(:magenta)
81
- out << ret
69
+ out << ":\n"
70
+ if line.has_key?(:data) then
71
+ data = line[:data]
72
+
73
+ if data.kind_of?(Hash) and data.has_key?(:request) and data.has_key?(:response) then
74
+ out << "---------\nrequest:"
75
+ if options.pretty then
76
+ ret = JSON.pretty_generate(data[:request]).to_s
77
+ ret[0] = ret[0].color(:magenta)
78
+ ret[-1] = ret[-1].color(:magenta)
79
+ out << ret
80
+ else
81
+ out << data[:request].to_json
82
+ end
83
+
84
+ out << "\n---------\nresponse:"
85
+ if options.pretty then
86
+ ret = JSON.pretty_generate(data[:response]).to_s
87
+ ret[0] = ret[0].color(:magenta)
88
+ ret[-1] = ret[-1].color(:magenta)
89
+ out << ret
90
+ else
91
+ out << data[:response].to_json
92
+ end
93
+
94
+ else
95
+ out << data.to_s
96
+ end
97
+
82
98
  else
83
- out << line[:data][:response].to_json
99
+ out << line.to_s
84
100
  end
85
101
 
86
102
  end
@@ -1,3 +1,4 @@
1
+ require 'csv'
1
2
 
2
3
  module MrMurano
3
4
  class Timeseries < ServiceConfig
@@ -25,14 +26,25 @@ command 'timeseries query' do |c|
25
26
  c.syntax = %{mr timeseries query <query string>}
26
27
  c.description = %{Query the timeseries database}
27
28
  c.option '--[no-]json', %{Display results as raw json}
28
- #c.option '--csv', %{Display results as csv}
29
- # Should get a CSV formatter to make sure cells are properly escapped.
29
+ c.option '--[no-]csv', %{Display results as CSV}
30
+
30
31
  c.action do |args,options|
31
- options.defalts :json=>false
32
+ options.defalts :json=>false, :csv=>false
32
33
  sol = MrMurano::Timeseries.new
33
34
  ret = sol.query args.join(' ')
34
35
  if options.json then
35
36
  puts ret.to_json
37
+
38
+ elsif options.csv then
39
+ (ret[:results] or []).each do |res|
40
+ (res[:series] or []).each do |ser|
41
+ cols = ser[:columns] #.map{|h| "#{ser[:name]}.#{h}"}
42
+ CSV($stdout, :headers=>cols, :write_headers=>true) do |csv|
43
+ ser[:values].each{|v| csv << v}
44
+ end
45
+ end
46
+ end
47
+
36
48
  else
37
49
  (ret[:results] or []).each do |res|
38
50
  (res[:series] or []).each do |ser|
@@ -1,4 +1,4 @@
1
1
  module MrMurano
2
- VERSION = '1.3.1'.freeze
2
+ VERSION = '1.3.2'.freeze
3
3
  end
4
4
 
data/lib/MrMurano.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'MrMurano/version'
2
2
  require 'MrMurano/hash.rb'
3
+ require 'MrMurano/http'
3
4
  require 'MrMurano/configFile'
4
5
  require 'MrMurano/Account'
5
6
  require 'MrMurano/Solution'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: MrMurano
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Conrad Tadpol Tilstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-08 00:00:00.000000000 Z
11
+ date: 2016-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -179,6 +179,7 @@ files:
179
179
  - lib/MrMurano/configFile.rb
180
180
  - lib/MrMurano/cors.rb
181
181
  - lib/MrMurano/hash.rb
182
+ - lib/MrMurano/http.rb
182
183
  - lib/MrMurano/keystore.rb
183
184
  - lib/MrMurano/logs.rb
184
185
  - lib/MrMurano/shelledCommand.rb