MrMurano 1.6.3 → 1.7.0
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 +4 -4
- data/.gitignore +1 -2
- data/Gemfile +1 -1
- data/MrMurano.gemspec +1 -1
- data/README.markdown +4 -0
- data/TODO.taskpaper +17 -4
- data/lib/MrMurano/Account.rb +5 -1
- data/lib/MrMurano/Config.rb +3 -1
- data/lib/MrMurano/Product-Resources.rb +239 -0
- data/lib/MrMurano/Product.rb +51 -2
- data/lib/MrMurano/Solution-Cors.rb +81 -0
- data/lib/MrMurano/Solution-Endpoint.rb +8 -4
- data/lib/MrMurano/Solution-File.rb +4 -2
- data/lib/MrMurano/Solution-ServiceConfig.rb +74 -1
- data/lib/MrMurano/Solution-Services.rb +4 -2
- data/lib/MrMurano/Solution-Users.rb +6 -3
- data/lib/MrMurano/Solution.rb +6 -274
- data/lib/MrMurano/SyncUpDown.rb +433 -0
- data/lib/MrMurano/commands/completion.rb +152 -0
- data/lib/MrMurano/commands/content.rb +15 -14
- data/lib/MrMurano/commands/cors.rb +11 -38
- data/lib/MrMurano/commands/exportImport.rb +4 -3
- data/lib/MrMurano/commands/keystore.rb +15 -16
- data/lib/MrMurano/commands/logs.rb +2 -2
- data/lib/MrMurano/commands/productCreate.rb +4 -4
- data/lib/MrMurano/commands/productDelete.rb +6 -8
- data/lib/MrMurano/commands/productSpec.rb +31 -36
- data/lib/MrMurano/commands/productWrite.rb +9 -7
- data/lib/MrMurano/commands/serialNumberCmds.rb +4 -4
- data/lib/MrMurano/commands/solutionCreate.rb +4 -4
- data/lib/MrMurano/commands/solutionDelete.rb +5 -5
- data/lib/MrMurano/commands/status.rb +9 -42
- data/lib/MrMurano/commands/sync.rb +15 -71
- data/lib/MrMurano/commands/timeseries.rb +23 -29
- data/lib/MrMurano/commands/tsdb.rb +202 -0
- data/lib/MrMurano/commands/zshcomplete.erb +112 -0
- data/lib/MrMurano/commands.rb +4 -1
- data/lib/MrMurano/http.rb +4 -3
- data/lib/MrMurano/makePretty.rb +15 -6
- data/lib/MrMurano/verbosing.rb +71 -0
- data/lib/MrMurano/version.rb +1 -1
- data/lib/MrMurano.rb +1 -0
- data/spec/ConfigFile_spec.rb +3 -3
- data/spec/ProductContent_spec.rb +2 -2
- data/spec/ProductResources_spec.rb +152 -0
- data/spec/Product_spec.rb +38 -1
- data/spec/Solution-Cors_spec.rb +134 -0
- data/spec/Solution-ServiceConfig_spec.rb +198 -0
- data/spec/SyncRoot_spec.rb +74 -0
- data/spec/cmd_config_spec.rb +51 -0
- data/spec/fixtures/.mrmuranorc +9 -0
- data/spec/{testfiles → fixtures}/configfile +0 -0
- data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
- data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
- data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
- data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
- data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
- data/spec/{lightbulb.yaml → fixtures/product_spec_files/lightbulb.yaml} +0 -0
- data/spec/spec_helper.rb +4 -4
- 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
|
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
|
-
|
29
|
+
prd.error "Missing <content id>"
|
28
30
|
else
|
29
|
-
prd
|
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
|
-
|
47
|
+
prd.error "Missing <content id>"
|
46
48
|
else
|
47
|
-
prd
|
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
|
-
|
69
|
+
prd.error "Missing <content id>"
|
68
70
|
elsif args[1].nil? then
|
69
|
-
|
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
|
-
|
76
|
+
prd.outf prd.create(args[0], options.meta)
|
76
77
|
end
|
77
78
|
|
78
|
-
|
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
|
-
|
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
|