MrMurano 1.6.3 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -2
  3. data/Gemfile +1 -1
  4. data/MrMurano.gemspec +1 -1
  5. data/README.markdown +4 -0
  6. data/TODO.taskpaper +17 -4
  7. data/lib/MrMurano/Account.rb +5 -1
  8. data/lib/MrMurano/Config.rb +3 -1
  9. data/lib/MrMurano/Product-Resources.rb +239 -0
  10. data/lib/MrMurano/Product.rb +51 -2
  11. data/lib/MrMurano/Solution-Cors.rb +81 -0
  12. data/lib/MrMurano/Solution-Endpoint.rb +8 -4
  13. data/lib/MrMurano/Solution-File.rb +4 -2
  14. data/lib/MrMurano/Solution-ServiceConfig.rb +74 -1
  15. data/lib/MrMurano/Solution-Services.rb +4 -2
  16. data/lib/MrMurano/Solution-Users.rb +6 -3
  17. data/lib/MrMurano/Solution.rb +6 -274
  18. data/lib/MrMurano/SyncUpDown.rb +433 -0
  19. data/lib/MrMurano/commands/completion.rb +152 -0
  20. data/lib/MrMurano/commands/content.rb +15 -14
  21. data/lib/MrMurano/commands/cors.rb +11 -38
  22. data/lib/MrMurano/commands/exportImport.rb +4 -3
  23. data/lib/MrMurano/commands/keystore.rb +15 -16
  24. data/lib/MrMurano/commands/logs.rb +2 -2
  25. data/lib/MrMurano/commands/productCreate.rb +4 -4
  26. data/lib/MrMurano/commands/productDelete.rb +6 -8
  27. data/lib/MrMurano/commands/productSpec.rb +31 -36
  28. data/lib/MrMurano/commands/productWrite.rb +9 -7
  29. data/lib/MrMurano/commands/serialNumberCmds.rb +4 -4
  30. data/lib/MrMurano/commands/solutionCreate.rb +4 -4
  31. data/lib/MrMurano/commands/solutionDelete.rb +5 -5
  32. data/lib/MrMurano/commands/status.rb +9 -42
  33. data/lib/MrMurano/commands/sync.rb +15 -71
  34. data/lib/MrMurano/commands/timeseries.rb +23 -29
  35. data/lib/MrMurano/commands/tsdb.rb +202 -0
  36. data/lib/MrMurano/commands/zshcomplete.erb +112 -0
  37. data/lib/MrMurano/commands.rb +4 -1
  38. data/lib/MrMurano/http.rb +4 -3
  39. data/lib/MrMurano/makePretty.rb +15 -6
  40. data/lib/MrMurano/verbosing.rb +71 -0
  41. data/lib/MrMurano/version.rb +1 -1
  42. data/lib/MrMurano.rb +1 -0
  43. data/spec/ConfigFile_spec.rb +3 -3
  44. data/spec/ProductContent_spec.rb +2 -2
  45. data/spec/ProductResources_spec.rb +152 -0
  46. data/spec/Product_spec.rb +38 -1
  47. data/spec/Solution-Cors_spec.rb +134 -0
  48. data/spec/Solution-ServiceConfig_spec.rb +198 -0
  49. data/spec/SyncRoot_spec.rb +74 -0
  50. data/spec/cmd_config_spec.rb +51 -0
  51. data/spec/fixtures/.mrmuranorc +9 -0
  52. data/spec/{testfiles → fixtures}/configfile +0 -0
  53. data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
  54. data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
  55. data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
  56. data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
  57. data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
  58. data/spec/{lightbulb.yaml → fixtures/product_spec_files/lightbulb.yaml} +0 -0
  59. data/spec/spec_helper.rb +4 -4
  60. metadata +47 -19
@@ -0,0 +1,433 @@
1
+ require 'pathname'
2
+ require 'tempfile'
3
+ require 'shellwords'
4
+ require 'MrMurano/Config'
5
+ require 'MrMurano/hash'
6
+
7
+ module MrMurano
8
+ class SyncRoot
9
+ Syncable = Struct.new(:name, :class, :type, :desc, :bydefault) do
10
+ end
11
+
12
+ def self.add(name, klass, type, desc, bydefault=false)
13
+ @@syncset = [] unless defined?(@@syncset)
14
+ @@syncset << Syncable.new(name.to_s, klass, type, desc, bydefault)
15
+ end
16
+
17
+ def self.reset()
18
+ @@syncset = []
19
+ end
20
+
21
+ def self.each(&block)
22
+ @@syncset.each{|a| yield a.name, a.type, a.class }
23
+ end
24
+
25
+ def self.each_option(&block)
26
+ @@syncset.each{|a| yield "-#{a.type.downcase}", "--[no-]#{a.name}", a.desc}
27
+ end
28
+
29
+ def self.each_filtered(opt, &block)
30
+ self.checkSAME(opt)
31
+ @@syncset.each do |a|
32
+ if opt[a.name.to_sym] or opt[a.type.to_sym] then
33
+ yield a.name, a.type, a.class
34
+ end
35
+ end
36
+ end
37
+
38
+ ## Adjust options based on all or none
39
+ # If none are selected, select the bydefault ones.
40
+ def self.checkSAME(opt)
41
+ if opt[:all] then
42
+ @@syncset.each {|a| opt[a.name.to_sym] = true }
43
+ else
44
+ any = @@syncset.select {|a| opt[a.name.to_sym] or opt[a.type.to_sym]}
45
+ if any.empty? then
46
+ @@syncset.select{|a| a.bydefault }.each{|a| opt[a.name.to_sym] = true}
47
+ end
48
+ end
49
+
50
+ nil
51
+ end
52
+ end
53
+
54
+ module SyncUpDown
55
+ #######################################################################
56
+ # Methods that must be overridden
57
+
58
+ ##
59
+ # Get a list of remote items.
60
+ #
61
+ # Children objects Must override this
62
+ #
63
+ # @return Array: of Hashes of item details
64
+ def list()
65
+ []
66
+ end
67
+
68
+ ## Remove remote item
69
+ #
70
+ # Children objects Must override this
71
+ #
72
+ # @param itemkey String: The identifying key for this item
73
+ def remove(itemkey)
74
+ raise "Forgotten implementation"
75
+ end
76
+
77
+ ## Upload local item to remote
78
+ #
79
+ # Children objects Must override this
80
+ #
81
+ # @param src Pathname: Full path of where to upload from
82
+ # @param item Hash: The item details to upload
83
+ # @param modify Bool: True if item exists already and this is changing it
84
+ def upload(src, item, modify)
85
+ raise "Forgotten implementation"
86
+ end
87
+
88
+ ##
89
+ # True if itemA and itemB are different
90
+ #
91
+ # Children objects must override this
92
+ #
93
+ def docmp(itemA, itemB)
94
+ true
95
+ end
96
+
97
+ #
98
+ #######################################################################
99
+
100
+ #######################################################################
101
+ # Methods that could be overriden
102
+
103
+ ##
104
+ # Compute a remote item hash from the local path
105
+ #
106
+ # Children objects should override this.
107
+ #
108
+ # @param root Pathname: Root path for this resource type from config files
109
+ # @param path Pathname: Path to local item
110
+ # @return Hash: hash of the details for the remote item for this path
111
+ def toRemoteItem(root, path)
112
+ path = Pathname.new(path) unless path.kind_of? Pathname
113
+ root = Pathname.new(root) unless root.kind_of? Pathname
114
+ {:name => path.relative_path_from(root).to_s}
115
+ end
116
+
117
+ ##
118
+ # Compute the local name from remote item details
119
+ #
120
+ # Children objects should override this or #tolocalpath
121
+ #
122
+ # @param item Hash: listing details for the item.
123
+ # @param itemkey Symbol: Key for look up.
124
+ def tolocalname(item, itemkey)
125
+ item[itemkey].to_s
126
+ end
127
+
128
+ ##
129
+ # Compute the local path from the listing details
130
+ #
131
+ # If there is already a matching local item, some of its details are also in
132
+ # the item hash.
133
+ #
134
+ # Children objects should override this or #tolocalname
135
+ #
136
+ # @param into Pathname: Root path for this resource type from config files
137
+ # @param item Hash: listing details for the item.
138
+ # @return Pathname: path to save (or merge) remote item into
139
+ def tolocalpath(into, item)
140
+ return item[:local_path] if item.has_key? :local_path
141
+ itemkey = @itemkey.to_sym
142
+ name = tolocalname(item, itemkey)
143
+ raise "Bad key(#{itemkey}) for #{item}" if name.nil?
144
+ name = Pathname.new(name) unless name.kind_of? Pathname
145
+ name = name.relative_path_from(Pathname.new('/')) if name.absolute?
146
+ into + name
147
+ end
148
+
149
+ ## Get the key used to quickly compare two items
150
+ #
151
+ # Children objects should override this if synckey is not @itemkey
152
+ #
153
+ # @param item Hash: The item to get a key from
154
+ # @returns Object: The object to use a comparison key
155
+ def synckey(item)
156
+ key = @itemkey.to_sym
157
+ item[key]
158
+ end
159
+
160
+ ## Download an item into local
161
+ #
162
+ # Children objects should override this or implement #fetch()
163
+ #
164
+ # @param local Pathname: Full path of where to download to
165
+ # @param item Hash: The item to download
166
+ def download(local, item)
167
+ if item[:bundled] then
168
+ say_warning "Not downloading into bundled item #{synckey(item)}"
169
+ # FIXME don't use say_warning
170
+ return
171
+ end
172
+ local.dirname.mkpath
173
+ id = item[@itemkey.to_sym]
174
+ local.open('wb') do |io|
175
+ fetch(id) do |chunk|
176
+ io.write chunk
177
+ end
178
+ end
179
+ end
180
+
181
+ ## Remove local reference of item
182
+ #
183
+ # Children objects should override this if move than just unlinking the local
184
+ # item.
185
+ #
186
+ # @param dest Pathname: Full path of item to be removed
187
+ # @param item Hash: Full details of item to be removed
188
+ def removelocal(dest, item)
189
+ dest.unlink
190
+ end
191
+
192
+ #
193
+ #######################################################################
194
+
195
+
196
+ ##
197
+ # So, for bundles this needs to look at all the places and build up the mered
198
+ # stack of local items.
199
+ #
200
+ # Which means it needs the from to be split into the base and the sub so we can
201
+ # inject bundle directories.
202
+
203
+ ##
204
+ # Get a list of local items.
205
+ #
206
+ # Children should never need to override this. Instead they should override
207
+ # #localitems
208
+ #
209
+ # This collects items in the project and all bundles.
210
+ # @return Array: of Hashes of items
211
+ def locallist()
212
+ # so. if @locationbase/bundles exists
213
+ # gather and merge: @locationbase/bundles/*/@location
214
+ # then merge @locationbase/@location
215
+ #
216
+
217
+ bundleDir = $cfg['location.bundles'] or 'bundles'
218
+ bundleDir = 'bundles' if bundleDir.nil?
219
+ items = {}
220
+ if (@locationbase + bundleDir).directory? then
221
+ (@locationbase + bundleDir).children.sort.each do |bndl|
222
+ if (bndl + @location).exist? then
223
+ verbose("Loading from bundle #{bndl.basename}")
224
+ bitems = localitems(bndl + @location)
225
+ bitems.map!{|b| b[:bundled] = true; b} # mark items from bundles.
226
+
227
+
228
+ # use synckey for quicker merging.
229
+ bitems.each { |b| items[synckey(b)] = b }
230
+ end
231
+ end
232
+ end
233
+ if (@locationbase + @location).exist? then
234
+ bitems = localitems(@locationbase + @location)
235
+ # use synckey for quicker merging.
236
+ bitems.each { |b| items[synckey(b)] = b }
237
+ end
238
+
239
+ items.values
240
+ end
241
+
242
+ ##
243
+ # Get a list of local items rooted at #from
244
+ #
245
+ # Children rarely need to override this. Only when the locallist is not a set
246
+ # of files in a directory will they need to override it.
247
+ #
248
+ # @param from Pathname: Directory of items to scan
249
+ # @return Array: of Hashes of item details
250
+ def localitems(from)
251
+ from.children.map do |path|
252
+ if path.directory? then
253
+ # TODO: look for definition. ( ?.rockspec? ?mr.modules? ?mr.manifest? )
254
+ # Lacking definition, find all *.lua but not *_test.lua
255
+ # This specifically and intentionally only goes one level deep.
256
+ path.children
257
+ else
258
+ path
259
+ end
260
+ end.flatten.compact.reject do |path|
261
+ path.fnmatch('*_test.lua') or path.basename.fnmatch('.*')
262
+ end.select do |path|
263
+ path.extname == '.lua'
264
+ end.map do |path|
265
+ # sometimes this is a name, sometimes it is an item.
266
+ # do I want to keep that? NO.
267
+ name = toRemoteItem(from, path)
268
+ unless name.nil? then
269
+ name[:local_path] = path
270
+ name
271
+ end
272
+ end.flatten.compact
273
+ end
274
+
275
+ #######################################################################
276
+ # Methods that provide the core status/syncup/syncdown
277
+
278
+ def elevate_hash(hsh)
279
+ if hsh.kind_of?(Hash) then
280
+ hsh = Hash.transform_keys_to_symbols(hsh)
281
+ hsh.define_singleton_method(:method_missing) do |mid,*args|
282
+ if mid.to_s.match(/^(.+)=$/) then
283
+ self[$1.to_sym] = args.first
284
+ else
285
+ self[mid]
286
+ end
287
+ end
288
+ end
289
+ hsh
290
+ end
291
+ private :elevate_hash
292
+
293
+ def syncup(options={})
294
+ options = elevate_hash(options)
295
+ itemkey = @itemkey.to_sym
296
+ options.asdown=false
297
+ dt = status(options)
298
+ toadd = dt[:toadd]
299
+ todel = dt[:todel]
300
+ tomod = dt[:tomod]
301
+
302
+ if options.delete then
303
+ todel.each do |item|
304
+ verbose "Removing item #{item[:synckey]}"
305
+ unless $cfg['tool.dry'] then
306
+ remove(item[itemkey])
307
+ end
308
+ end
309
+ end
310
+ if options.create then
311
+ toadd.each do |item|
312
+ verbose "Adding item #{item[:synckey]}"
313
+ unless $cfg['tool.dry'] then
314
+ upload(item[:local_path], item.reject{|k,v| k==:local_path}, false)
315
+ end
316
+ end
317
+ end
318
+ if options.update then
319
+ tomod.each do |item|
320
+ verbose "Updating item #{item[:synckey]}"
321
+ unless $cfg['tool.dry'] then
322
+ upload(item[:local_path], item.reject{|k,v| k==:local_path}, true)
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ def syncdown(options={})
329
+ options = elevate_hash(options)
330
+ options.asdown = true
331
+ dt = status(options)
332
+ into = @locationbase + @location ###
333
+ toadd = dt[:toadd]
334
+ todel = dt[:todel]
335
+ tomod = dt[:tomod]
336
+
337
+ if options.delete then
338
+ todel.each do |item|
339
+ verbose "Removing item #{item[:synckey]}"
340
+ unless $cfg['tool.dry'] then
341
+ dest = tolocalpath(into, item)
342
+ removelocal(dest, item)
343
+ end
344
+ end
345
+ end
346
+ if options.create then
347
+ toadd.each do |item|
348
+ verbose "Adding item #{item[:synckey]}"
349
+ unless $cfg['tool.dry'] then
350
+ dest = tolocalpath(into, item)
351
+ download(dest, item)
352
+ end
353
+ end
354
+ end
355
+ if options.update then
356
+ tomod.each do |item|
357
+ verbose "Updating item #{item[:synckey]}"
358
+ unless $cfg['tool.dry'] then
359
+ dest = tolocalpath(into, item)
360
+ download(dest, item)
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ ## Call external diff tool on item
367
+ # WARNING: This will download the remote item to do the diff.
368
+ # @param item Hash: The item to get a diff of
369
+ # @return String: The diff output
370
+ def dodiff(item)
371
+ tfp = Tempfile.new([tolocalname(item, @itemkey), '.lua'])
372
+ df = ""
373
+ begin
374
+ download(Pathname.new(tfp.path), item)
375
+
376
+ cmd = $cfg['diff.cmd'].shellsplit
377
+ cmd << tfp.path
378
+ cmd << item[:local_path].to_s
379
+
380
+ IO.popen(cmd) {|io| df = io.read }
381
+ ensure
382
+ tfp.close
383
+ tfp.unlink
384
+ end
385
+ df
386
+ end
387
+
388
+ ## Get status of things here verses there
389
+ def status(options={})
390
+ options = elevate_hash(options)
391
+ there = list()
392
+ here = locallist()
393
+ itemkey = @itemkey.to_sym
394
+
395
+ therebox = {}
396
+ there.each do |item|
397
+ item = Hash.transform_keys_to_symbols(item)
398
+ item[:synckey] = synckey(item)
399
+ therebox[ item[:synckey] ] = item
400
+ end
401
+ herebox = {}
402
+ here.each do |item|
403
+ item = Hash.transform_keys_to_symbols(item)
404
+ item[:synckey] = synckey(item)
405
+ herebox[ item[:synckey] ] = item
406
+ end
407
+ toadd = []
408
+ todel = []
409
+ tomod = []
410
+ unchg = []
411
+ if options.asdown then
412
+ todel = (herebox.keys - therebox.keys).map{|key| herebox[key] }
413
+ toadd = (therebox.keys - herebox.keys).map{|key| therebox[key] }
414
+ else
415
+ toadd = (herebox.keys - therebox.keys).map{|key| herebox[key] }
416
+ todel = (therebox.keys - herebox.keys).map{|key| therebox[key] }
417
+ end
418
+ (herebox.keys & therebox.keys).each do |key|
419
+ # Want here to override there except for itemkey.
420
+ mrg = herebox[key].reject{|k,v| k==itemkey}
421
+ mrg = therebox[key].merge(mrg)
422
+ if docmp(herebox[key], therebox[key]) then
423
+ mrg[:diff] = dodiff(mrg) if options.diff
424
+ tomod << mrg
425
+ else
426
+ unchg << mrg
427
+ end
428
+ end
429
+ { :toadd=>toadd, :todel=>todel, :tomod=>tomod, :unchg=>unchg }
430
+ end
431
+ end
432
+ end
433
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,152 @@
1
+ require 'pp'
2
+ require 'erb'
3
+
4
+ class CompletionContext < ::Commander::HelpFormatter::Context
5
+ end
6
+
7
+ class ::Commander::Runner
8
+
9
+ # Not so sure this should go in Runner, but where else?
10
+
11
+ ##
12
+ # Change the '--[no-]foo' switch into '--no-foo' and '--foo'
13
+ def flatswitches(option)
14
+ # if there is a --[no-]foo format, break that into two switches.
15
+ option[:switches].map{ |switch|
16
+ switch = switch.sub(/\s.*$/,'') # drop argument spec if exists.
17
+ if switch =~ /\[no-\]/ then
18
+ [switch.sub(/\[no-\]/, ''), switch.gsub(/[\[\]]/,'')]
19
+ else
20
+ switch
21
+ end
22
+ }.flatten
23
+ end
24
+
25
+ ##
26
+ # If the switches take an argument, retun =
27
+ def takesArg(option, yes='=', no='')
28
+ if option[:switches].select { |switch| switch =~ /\s\S+$/ }.empty? then
29
+ no
30
+ else
31
+ yes
32
+ end
33
+ end
34
+
35
+ ##
36
+ # truncate the description of an option
37
+ def optionDesc(option)
38
+ option[:description].sub(/\n.*$/,'')
39
+ end
40
+
41
+ ##
42
+ # Get a tree of all commands and sub commands
43
+ def cmdTree
44
+ tree={}
45
+ @commands.sort.each do |name,cmd|
46
+ levels = name.split
47
+ pos = tree
48
+ levels.each do |step|
49
+ pos[step] = {} unless pos.has_key? step
50
+ pos = pos[step]
51
+ end
52
+ pos["\0cmd"] = cmd
53
+ end
54
+ tree
55
+ end
56
+
57
+ ##
58
+ # Get maximum depth of sub-commands.
59
+ def cmdMaxDepth
60
+ depth=0
61
+ @commands.sort.each do |name,cmd|
62
+ levels = name.split
63
+ depth = levels.count if levels.count > depth
64
+ end
65
+ depth
66
+ end
67
+
68
+ ##
69
+ # Alternate tree of sub-commands.
70
+ def cmdTreeB
71
+ tree={}
72
+ @commands.sort.each do |name,cmd|
73
+ levels = name.split
74
+ tree[levels.join(' ')] = {:cmd=>cmd}
75
+
76
+ # load parent.
77
+ left = levels[0..-2]
78
+ right = levels[-1]
79
+ key = left.join(' ')
80
+ tree[key] = {} unless tree.has_key? key
81
+ if tree[key].has_key?(:subs) then
82
+ tree[key][:subs] << right
83
+ else
84
+ tree[key][:subs] = [right]
85
+ end
86
+ end
87
+ tree
88
+ end
89
+
90
+ end
91
+
92
+ command :completion do |c|
93
+ c.syntax = %{mr completion}
94
+ c.summary = %{Generate a completion file}
95
+ c.description = %{For starts, this is zsh only. Because that is what I use.
96
+
97
+ eval "$(mr completion)"
98
+ or
99
+ mr completion > _mr
100
+ source _mr
101
+ }
102
+ c.option '--subs', 'List sub commands'
103
+ #c.option '--opts CMD', 'List options for subcommand'
104
+ #c.option '--gopts', 'List global options'
105
+
106
+ # Changing direction.
107
+ # Will poop out the file to be included as the completion script.
108
+
109
+ c.action do |args, options|
110
+
111
+ runner = ::Commander::Runner.instance
112
+
113
+ if options.gopts then
114
+ opts = runner.instance_variable_get(:@options)
115
+ pp opts.first
116
+ pp runner.takesArg(opts.first)
117
+ # opts.each do |o|
118
+ # puts runner.optionLine o, 'GlobalOption'
119
+ # end
120
+
121
+ elsif options.subs then
122
+ runner.instance_variable_get(:@commands).sort.each do |name,cmd|
123
+ #desc = cmd.instance_variable_get(:@summary) #.lines[0]
124
+ #say "#{name}:'#{desc}'"
125
+ say "#{name}"
126
+ end
127
+
128
+ elsif options.opts then
129
+ cmds = runner.instance_variable_get(:@commands)
130
+ cmd = cmds[options.opts]
131
+ pp cmd.syntax
132
+ # looking at OptionParser to help figure out what kind of params a switch
133
+ # gets. And hopefully derive a completer for it
134
+ # !!!!! OptionParser::Completion what is this?
135
+ opts = OptionParser.new
136
+ cmds[options.opts].options.each do |o|
137
+ pp opts.make_switch(o[:args])
138
+ end
139
+
140
+ else
141
+
142
+ tmpl=ERB.new(File.read(File.join(File.dirname(__FILE__), "zshcomplete.erb")), nil, '-<>')
143
+
144
+ pc = CompletionContext.new(runner)
145
+ puts tmpl.result(pc.get_binding)
146
+ end
147
+
148
+
149
+ end
150
+ end
151
+
152
+ # vim: set ai et sw=2 ts=2 :
@@ -1,3 +1,4 @@
1
+ require 'MrMurano/Product'
1
2
 
2
3
  command 'content list' do |c|
3
4
  c.syntax = %{mr content list}
@@ -9,7 +10,7 @@ command 'content list' do |c|
9
10
  }
10
11
  c.action do |args, options|
11
12
  prd = MrMurano::ProductContent.new
12
- prd.list.each{|item| say item}
13
+ prd.outf prd.list
13
14
  end
14
15
  end
15
16
  alias_command :content, 'content list'
@@ -23,11 +24,11 @@ command 'content info' do |c|
23
24
  HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
24
25
  }
25
26
  c.action do |args, options|
27
+ prd = MrMurano::ProductContent.new
26
28
  if args[0].nil? then
27
- say_error "Missing <content id>"
29
+ prd.error "Missing <content id>"
28
30
  else
29
- prd = MrMurano::ProductContent.new
30
- prd.info(args[0]).each{|line| say "#{args[0]} #{line.join(' ')}"}
31
+ prd.tabularize prd.info(args[0])
31
32
  end
32
33
  end
33
34
  end
@@ -41,11 +42,11 @@ command 'content delete' do |c|
41
42
  HTTP Device API. (http://docs.exosite.com/http/#list-available-content)
42
43
  }
43
44
  c.action do |args, options|
45
+ prd = MrMurano::ProductContent.new
44
46
  if args[0].nil? then
45
- say_error "Missing <content id>"
47
+ prd.error "Missing <content id>"
46
48
  else
47
- prd = MrMurano::ProductContent.new
48
- pp prd.remove(args[0])
49
+ prd.outf prd.remove(args[0])
49
50
  end
50
51
  end
51
52
  end
@@ -62,20 +63,20 @@ command 'content upload' do |c|
62
63
 
63
64
  c.action do |args, options|
64
65
  options.defaults :meta=>' '
66
+ prd = MrMurano::ProductContent.new
65
67
 
66
68
  if args[0].nil? then
67
- say_error "Missing <content id>"
69
+ prd.error "Missing <content id>"
68
70
  elsif args[1].nil? then
69
- say_error "Missing <file>"
71
+ prd.error "Missing <file>"
70
72
  else
71
- prd = MrMurano::ProductContent.new
72
73
 
73
74
  ret = prd.info(args[0])
74
75
  if ret.nil? then
75
- pp prd.create(args[0], options.meta)
76
+ prd.outf prd.create(args[0], options.meta)
76
77
  end
77
78
 
78
- pp prd.upload(args[0], args[1])
79
+ prd.outf prd.upload(args[0], args[1])
79
80
  end
80
81
  end
81
82
  end
@@ -90,10 +91,10 @@ command 'content download' do |c|
90
91
  }
91
92
  c.option '-o','--output FILE',%{save to this file}
92
93
  c.action do |args, options|
94
+ prd = MrMurano::ProductContent.new
93
95
  if args[0].nil? then
94
- say_error "Missing <content id>"
96
+ prd.error "Missing <content id>"
95
97
  else
96
- prd = MrMurano::ProductContent.new
97
98
 
98
99
  if options.output.nil? then
99
100
  prd.download(args[0]) # to stdout