phoseum-cli 0.0.9 → 0.0.14
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/bin/phoseum-cli +134 -67
- data/lib/phoseum/phoseum-cli-lib.rb +107 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 102e4fe0316e79e37507ece6aa7119cdf23f8f2029d27e97d7211dae8aedcc08
|
4
|
+
data.tar.gz: bfe78eca818f6e7d9ef1b015c76aecd03d52db5521424cc3d835739322232b4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f3bbe28c768e6924090fa64cd3ff35d9125fe10a4fed80f495bb52d5d8576176d6cb9fc48cd577ac3f2a17a9f698b1eecd0e14e3f2070300e8da11815f7c0ed
|
7
|
+
data.tar.gz: 7710d08185a06695a4f461aca529a68219710051c48fcc43b356ab155970a7618fa72320a6f519393caf46cf2c0adb04aabb9a2561f9cf580876ab8df03fc4e6
|
data/bin/phoseum-cli
CHANGED
@@ -23,17 +23,17 @@ $MULTI = options[:multiple] ? true : false
|
|
23
23
|
|
24
24
|
client_checks
|
25
25
|
|
26
|
-
def upload(album)
|
27
|
-
|
28
|
-
if $config['TOKEN']
|
29
|
-
headers={ "bearer" => "#{$config['TOKEN']}" }
|
30
|
-
else
|
26
|
+
def upload(album,syn_name='',description='')
|
27
|
+
headers = {}
|
28
|
+
if !validate_token($config['TOKEN'])
|
31
29
|
if token = user_login()
|
32
30
|
headers = { "bearer" => token }
|
33
31
|
else
|
34
32
|
puts "Login Failed".red
|
35
33
|
exit 1
|
36
34
|
end
|
35
|
+
else
|
36
|
+
headers={ "bearer" => "#{$config['TOKEN']}" }
|
37
37
|
end
|
38
38
|
|
39
39
|
list=Dir.entries("./")
|
@@ -65,7 +65,8 @@ def upload(album)
|
|
65
65
|
# last resort solution for accents and unrecognizable characters
|
66
66
|
# but then will need to remove extension, pass the filter, then add extension again.
|
67
67
|
# human_name = check_string_sanity(filename)
|
68
|
-
human_name = filename
|
68
|
+
human_name = filename.dup
|
69
|
+
human_name = human_name.gsub!(/(.*)\.JPG|JPEG|PNG|GIF$/i,'\1')
|
69
70
|
|
70
71
|
somefound = search_image(image.signature)
|
71
72
|
if somefound
|
@@ -84,6 +85,12 @@ def upload(album)
|
|
84
85
|
post_body << "\r\n--#{BOUNDARY}--\r\n"
|
85
86
|
http = Net::HTTP.new(uri.host, uri.port)
|
86
87
|
headers = headers.merge({ "filename" => "#{image.signature}.#{image.type}", "album" => "#{album}", "name" => human_name })
|
88
|
+
if ! syn_name.nil?
|
89
|
+
headers = headers.merge({ "album_name" => syn_name })
|
90
|
+
end
|
91
|
+
if ! description.nil?
|
92
|
+
headers = headers.merge({ "description" => description })
|
93
|
+
end
|
87
94
|
request = Net::HTTP::Post.new(uri, headers)
|
88
95
|
request.body = post_body.join
|
89
96
|
request["Content-Type"] = "multipart/form-data, boundary=#{BOUNDARY}"
|
@@ -154,15 +161,16 @@ end
|
|
154
161
|
|
155
162
|
def health(what='',name='')
|
156
163
|
headers = {}
|
157
|
-
|
158
|
-
|
159
|
-
else
|
164
|
+
|
165
|
+
if !validate_token($config['TOKEN']) || !$config['TOKEN']
|
160
166
|
if token = user_login()
|
161
167
|
headers = { "bearer" => token }
|
162
168
|
else
|
163
169
|
puts "Login Failed".red
|
164
170
|
exit 1
|
165
171
|
end
|
172
|
+
else
|
173
|
+
headers={ "bearer" => "#{$config['TOKEN']}" }
|
166
174
|
end
|
167
175
|
some_uri=''
|
168
176
|
if what == "album"
|
@@ -233,6 +241,10 @@ def user_mgmt(action,user,pass='',role='')
|
|
233
241
|
post_body={}
|
234
242
|
if action == "add_user"
|
235
243
|
post_body=JSON.generate({"action" => "create-user", "user" => "#{user}", "password" => "#{pass}", "role" => "#{role}"})
|
244
|
+
if !$config['DEFAULT_SECRET']
|
245
|
+
puts "You must have a DEFAULT_SECRET setting to be able to create a user".red
|
246
|
+
exit 1
|
247
|
+
end
|
236
248
|
end
|
237
249
|
base = URI.parse("#{$config['SERVERURL']}")
|
238
250
|
puts "\nConnecting to: #{$config['SERVERURL']}".yellow if !$QUIET
|
@@ -264,29 +276,28 @@ def user_mgmt(action,user,pass='',role='')
|
|
264
276
|
exit 0
|
265
277
|
end
|
266
278
|
|
267
|
-
def user_login()
|
279
|
+
def user_login(puser='',ppass='')
|
268
280
|
post_body={}
|
269
|
-
user=''
|
270
|
-
pass=''
|
281
|
+
user= !puser ? '' : puser
|
282
|
+
pass= !ppass ? '' : ppass
|
271
283
|
if $config['USER']
|
272
284
|
user=$config['USER']
|
273
285
|
else
|
274
286
|
print "Username: "
|
275
|
-
user=STDIN.
|
287
|
+
user=STDIN.gets.chomp
|
276
288
|
end
|
277
289
|
if $config['PASS']
|
278
290
|
pass=$config['PASS']
|
279
|
-
puts "
|
291
|
+
puts "\nKeeping your password on the configuration file is UNSAFE".red
|
280
292
|
else
|
281
|
-
|
282
|
-
pass=STDIN.noecho(&:gets).chomp
|
293
|
+
pass=STDIN.getpass('Password: ')
|
283
294
|
end
|
284
295
|
post_body=JSON.generate({"action" => "user-login", "user" => "#{user}", "password" => "#{pass}"})
|
285
296
|
base = URI.parse("#{$config['SERVERURL']}")
|
286
297
|
puts "\nLogging to: #{$config['SERVERURL']}".yellow if !$QUIET
|
287
298
|
request = Net::HTTP::Post.new(base)
|
288
299
|
request.body = post_body
|
289
|
-
request.basic_auth("
|
300
|
+
request.basic_auth("cli", "loginNOauth")
|
290
301
|
response = Net::HTTP.start(base.hostname, $config['PORT'],
|
291
302
|
:timeout => $config['CALL_TIMEOUT'],
|
292
303
|
:use_ssl => base.scheme == "https",
|
@@ -323,50 +334,40 @@ end
|
|
323
334
|
|
324
335
|
|
325
336
|
def delete(what='',path='')
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
if confirm == "YES"
|
331
|
-
headers={}
|
332
|
-
if $config['TOKEN']
|
333
|
-
headers={ "bearer" => "#{$config['TOKEN']}" }
|
337
|
+
if !validate_token($config['TOKEN'])
|
338
|
+
if token = user_login()
|
339
|
+
headers = { "bearer" => token }
|
334
340
|
else
|
335
|
-
|
336
|
-
headers = { "bearer" => token }
|
337
|
-
else
|
338
|
-
puts "Login Failed".red
|
339
|
-
exit 1
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
base = URI.parse("#{$config['SERVERURL']}/#{path}")
|
344
|
-
puts "Connecting to: #{$config['SERVERURL']}".yellow if !$QUIET
|
345
|
-
request = Net::HTTP::Delete.new(base,headers)
|
346
|
-
response = Net::HTTP.start(base.hostname, $config['PORT'],
|
347
|
-
:timeout => $config['CALL_TIMEOUT'],
|
348
|
-
:use_ssl => base.scheme == "https",
|
349
|
-
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
|
350
|
-
:ca_file => $config['CA_TRUST']
|
351
|
-
) do |http|
|
352
|
-
http.request(request)
|
353
|
-
end
|
354
|
-
begin
|
355
|
-
list = JSON.parse(response.body)
|
356
|
-
if list['error']
|
357
|
-
puts list['error'].red
|
358
|
-
exit 1
|
359
|
-
else
|
360
|
-
puts list['success'].green
|
361
|
-
exit 0
|
362
|
-
end
|
363
|
-
rescue
|
364
|
-
puts "\nThe server sent out an Error:".red
|
365
|
-
puts clean_html(response.body)
|
341
|
+
puts "Login Failed".red
|
366
342
|
exit 1
|
367
343
|
end
|
368
344
|
else
|
369
|
-
|
345
|
+
headers={ "bearer" => "#{$config['TOKEN']}" }
|
346
|
+
end
|
347
|
+
base = URI.parse("#{$config['SERVERURL']}/#{path}")
|
348
|
+
puts "Connecting to: #{$config['SERVERURL']}".yellow if !$QUIET
|
349
|
+
request = Net::HTTP::Delete.new(base,headers)
|
350
|
+
response = Net::HTTP.start(base.hostname, $config['PORT'],
|
351
|
+
:timeout => $config['CALL_TIMEOUT'],
|
352
|
+
:use_ssl => base.scheme == "https",
|
353
|
+
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
|
354
|
+
:ca_file => $config['CA_TRUST']
|
355
|
+
) do |http|
|
356
|
+
http.request(request)
|
357
|
+
end
|
358
|
+
begin
|
359
|
+
list = JSON.parse(response.body)
|
360
|
+
if list['error']
|
361
|
+
puts list['error'].red
|
362
|
+
exit 1
|
363
|
+
else
|
364
|
+
puts list['success'].green
|
365
|
+
exit 0
|
366
|
+
end
|
367
|
+
rescue
|
368
|
+
puts "\nThe server sent out an Error:".red
|
369
|
+
puts clean_html(response.body)
|
370
|
+
exit 1
|
370
371
|
end
|
371
372
|
end
|
372
373
|
|
@@ -387,10 +388,10 @@ case options
|
|
387
388
|
puts "You need to select one of the valid Roles".red
|
388
389
|
exit 1
|
389
390
|
else
|
390
|
-
print "Please type the password twice for user #{value}.\n\
|
391
|
-
new_password=STDIN.
|
392
|
-
print "\
|
393
|
-
confirm=STDIN.
|
391
|
+
print "Please type the password twice for user #{value}.\n\t"
|
392
|
+
new_password=STDIN.getpass('Password: ')
|
393
|
+
print "\t"
|
394
|
+
confirm=STDIN.getpass('Confirm: ')
|
394
395
|
if new_password == confirm
|
395
396
|
user_mgmt(what,value,new_password,options[:role])
|
396
397
|
end
|
@@ -405,19 +406,66 @@ case options
|
|
405
406
|
puts "It is Not possible to delete base album".red
|
406
407
|
exit 1
|
407
408
|
end
|
409
|
+
if !confirm_action("Are you sure you want to delete the #{what} #{value}")
|
410
|
+
puts "Deleting #{what} cancelled.".green
|
411
|
+
end
|
408
412
|
end
|
409
413
|
if options[:image]
|
410
414
|
if _found = check_image_exists(options[:image])
|
411
|
-
|
412
|
-
|
415
|
+
if _found =~ /^Signature Found at \[\"(.*)\"\]/
|
416
|
+
capt = _found.match(/^Signature Found at \[\"(.*)\"\]/).captures
|
417
|
+
capt_part = capt[0].match(/.*(Albums.*)$/).captures
|
418
|
+
value = capt_part[0]
|
419
|
+
else
|
420
|
+
img_data = JSON.parse(_found)
|
421
|
+
value="#{img_data['web_path']}/#{img_data['image']}"
|
422
|
+
end
|
413
423
|
what='image'
|
414
424
|
else
|
415
425
|
puts "Image not found on server".red
|
416
426
|
exit 1
|
417
427
|
end
|
428
|
+
if confirm_action("Are you sure you want to delete the #{what} #{value}")
|
429
|
+
puts "Deleting #{what} #{value}".green if !$QUIET
|
430
|
+
delete(what,value)
|
431
|
+
else
|
432
|
+
puts "Deleting #{what} cancelled.".green
|
433
|
+
end
|
434
|
+
end
|
435
|
+
if options[:user]
|
436
|
+
what='user'
|
437
|
+
value=options[:user]
|
438
|
+
if confirm_action("Are you sure you want to delete the #{what} #{value}")
|
439
|
+
puts "Deleting #{what} #{value}".green if !$QUIET
|
440
|
+
if delete_user(value)
|
441
|
+
puts "#{value} successfully deleted.".green
|
442
|
+
else
|
443
|
+
puts "#{value} failed to be deleted.".red
|
444
|
+
end
|
445
|
+
else
|
446
|
+
puts "Deleting #{what} cancelled.".green
|
447
|
+
end
|
448
|
+
end
|
449
|
+
if !what
|
450
|
+
puts "Got no object to delete. Exiting.".red
|
451
|
+
exit 1
|
452
|
+
end
|
453
|
+
when -> (o) { o[:options] }
|
454
|
+
if options[:user]
|
455
|
+
print "Please confirm #{options[:user]} password.\n\t"
|
456
|
+
current_password=STDIN.getpass('Password: ')
|
457
|
+
if token = user_login(options[:user],current_password)
|
458
|
+
print "\n Change [P]assword, [U]sername, [R]ole :[P/U/R]: "
|
459
|
+
confirm=STDIN.gets.chomp
|
460
|
+
else
|
461
|
+
puts "User Check Failed".red
|
462
|
+
exit 1
|
463
|
+
end
|
464
|
+
else
|
465
|
+
# if !what
|
466
|
+
puts "Got no user to work with. Exiting.".red
|
467
|
+
exit 1
|
418
468
|
end
|
419
|
-
puts "Deleting #{what} #{value}".green if !$QUIET
|
420
|
-
delete(what,value)
|
421
469
|
when -> (u) { u[:upload] }
|
422
470
|
puts "Preparing to upload images to API server".green if !$QUIET
|
423
471
|
if !options.key?(:album)
|
@@ -427,8 +475,27 @@ case options
|
|
427
475
|
exit 1
|
428
476
|
else
|
429
477
|
album_clean=check_string_sanity(options[:album])
|
430
|
-
|
431
|
-
|
478
|
+
usable_name= options[:name] ? options[:name].dup : ''
|
479
|
+
usable_desc= options[:description] ? options[:description].dup : ''
|
480
|
+
if usable_name.length > 255 || usable_desc.length > 255
|
481
|
+
if usable_name.length > 255
|
482
|
+
puts "Synthetic name has #{usable_name.length} characters!!".red
|
483
|
+
end
|
484
|
+
if usable_desc.length > 255
|
485
|
+
puts "SHORT Description has #{usable_desc.length} characters!!".red
|
486
|
+
end
|
487
|
+
puts "This is a huge-ass string mate, what are you doing?".red
|
488
|
+
puts "We should limit this to 255 chars. Keep it short. Aborting.".red
|
489
|
+
exit 1
|
490
|
+
end
|
491
|
+
if options[:name]
|
492
|
+
usable_name = clean_name(options[:name])
|
493
|
+
usable_desc = clean_name(options[:description])
|
494
|
+
puts "Using Album destination: #{album_clean} --> Named as '#{usable_name}'".light_blue if !$QUIET
|
495
|
+
else
|
496
|
+
puts "Using Album destination: #{album_clean}".light_blue if !$QUIET
|
497
|
+
end
|
498
|
+
upload(album_clean,usable_name,usable_desc)
|
432
499
|
end
|
433
500
|
when -> (l) { l[:login] }
|
434
501
|
puts "Start login process".green if !$QUIET
|
@@ -20,6 +20,10 @@ def option_parser(opts)
|
|
20
20
|
options[:delete] = d
|
21
21
|
end
|
22
22
|
|
23
|
+
opts.on("-D", "--description 'SHORTDESC'", "Add album description (up to 255 chars). Use with: [album-path]") do |ds|
|
24
|
+
options[:description] = ds
|
25
|
+
end
|
26
|
+
|
23
27
|
opts.on("-f", "--file-cfg FILENAME", "Use alternative configuration file") do |f|
|
24
28
|
options[:filecfg] = f
|
25
29
|
end
|
@@ -36,6 +40,10 @@ def option_parser(opts)
|
|
36
40
|
options[:multiple] = m
|
37
41
|
end
|
38
42
|
|
43
|
+
opts.on("-N", "--name 'REALNAME'", "Set Album's synthetic name. Use with: [album-path]") do |n|
|
44
|
+
options[:name] = n
|
45
|
+
end
|
46
|
+
|
39
47
|
opts.on("-v", "--verbose", "Run verbosely (DEBUG mode)") do |v|
|
40
48
|
options[:verbose] = v
|
41
49
|
end
|
@@ -44,6 +52,10 @@ def option_parser(opts)
|
|
44
52
|
options[:role] = r
|
45
53
|
end
|
46
54
|
|
55
|
+
opts.on("-o", "--options", "Update options from object. Use with: [user]") do |o|
|
56
|
+
options[:options] = o
|
57
|
+
end
|
58
|
+
|
47
59
|
opts.on("-q", "--quiet", "Avoid regular info, useful for JSON output") do |q|
|
48
60
|
options[:quiet] = q
|
49
61
|
end
|
@@ -56,6 +68,10 @@ def option_parser(opts)
|
|
56
68
|
options[:upload] = u
|
57
69
|
end
|
58
70
|
|
71
|
+
opts.on("-U", "--User USERNAME", "Username for delete or change password / role. Use with: [delete, options]") do |usr|
|
72
|
+
options[:user] = usr
|
73
|
+
end
|
74
|
+
|
59
75
|
opts.on("-h", "-h", "That is this...") do |h|
|
60
76
|
options[:help] = h
|
61
77
|
end
|
@@ -64,6 +80,17 @@ def option_parser(opts)
|
|
64
80
|
return options
|
65
81
|
end
|
66
82
|
|
83
|
+
def confirm_action(msg)
|
84
|
+
puts "You must write 'YES' to confirm, otherwise NO is assumed".yellow
|
85
|
+
print "#{msg} :[YES/NO]: "
|
86
|
+
confirm=STDIN.gets.chomp
|
87
|
+
if confirm == "YES"
|
88
|
+
return true
|
89
|
+
else
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
67
94
|
class String
|
68
95
|
# colorization
|
69
96
|
def colorize(color_code)
|
@@ -130,6 +157,17 @@ def clean_html(msg)
|
|
130
157
|
return msg
|
131
158
|
end
|
132
159
|
|
160
|
+
def clean_name(nameval)
|
161
|
+
name_words = nameval.split(' ')
|
162
|
+
name = ''
|
163
|
+
name_words.each do |word|
|
164
|
+
cword = check_string_sanity(word)
|
165
|
+
name = name.nil? ? cword : [name,cword].join(' ')
|
166
|
+
end
|
167
|
+
name.gsub!(/^\ /,'')
|
168
|
+
return name
|
169
|
+
end
|
170
|
+
|
133
171
|
def check_image_exists(fpath)
|
134
172
|
flocal = fpath.split('/')
|
135
173
|
filename = flocal.last
|
@@ -145,16 +183,15 @@ def check_image_exists(fpath)
|
|
145
183
|
end
|
146
184
|
|
147
185
|
def search_image(sign,album='')
|
148
|
-
|
149
|
-
if $config['TOKEN']
|
150
|
-
headers={ "bearer" => "#{$config['TOKEN']}" }
|
151
|
-
else
|
186
|
+
if !validate_token($config['TOKEN'])
|
152
187
|
if token = user_login()
|
153
188
|
headers = { "bearer" => token }
|
154
189
|
else
|
155
190
|
puts "Login Failed".red
|
156
191
|
exit 1
|
157
192
|
end
|
193
|
+
else
|
194
|
+
headers={ "bearer" => "#{$config['TOKEN']}" }
|
158
195
|
end
|
159
196
|
some_uri= album == '' ? "?signature=#{sign}" : "?signature=#{sign}&inside_album=#{album}"
|
160
197
|
base = URI.parse("#{$config['SERVERURL']}#{some_uri}")
|
@@ -182,3 +219,69 @@ def search_image(sign,album='')
|
|
182
219
|
exit 1
|
183
220
|
end
|
184
221
|
end
|
222
|
+
|
223
|
+
def validate_token(token)
|
224
|
+
headers = {}
|
225
|
+
if $config['TOKEN']
|
226
|
+
headers={ "bearer" => "#{$config['TOKEN']}" }
|
227
|
+
else
|
228
|
+
return false
|
229
|
+
end
|
230
|
+
base = URI.parse("#{$config['SERVERURL']}")
|
231
|
+
request = Net::HTTP::Post.new(base,headers)
|
232
|
+
request.body = JSON.generate({"action" => "check-token" })
|
233
|
+
response = Net::HTTP.start(base.hostname, $config['PORT'],
|
234
|
+
:timeout => $config['CALL_TIMEOUT'],
|
235
|
+
:use_ssl => base.scheme == "https",
|
236
|
+
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
|
237
|
+
:ca_file => $config['CA_TRUST']
|
238
|
+
) do |http|
|
239
|
+
http.request(request)
|
240
|
+
end
|
241
|
+
begin
|
242
|
+
list = JSON.parse(response.body)
|
243
|
+
if list['error']
|
244
|
+
return false
|
245
|
+
end
|
246
|
+
if list['success']
|
247
|
+
return true
|
248
|
+
end
|
249
|
+
rescue
|
250
|
+
puts "\nThe server sent out an Error:".red
|
251
|
+
puts clean_html(response.body)
|
252
|
+
exit 1
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def delete_user(username)
|
257
|
+
headers = {}
|
258
|
+
if $config['TOKEN']
|
259
|
+
headers={ "bearer" => "#{$config['TOKEN']}" }
|
260
|
+
else
|
261
|
+
return false
|
262
|
+
end
|
263
|
+
base = URI.parse("#{$config['SERVERURL']}")
|
264
|
+
request = Net::HTTP::Post.new(base,headers)
|
265
|
+
request.body = JSON.generate({"action" => "delete-user", "username" => username })
|
266
|
+
response = Net::HTTP.start(base.hostname, $config['PORT'],
|
267
|
+
:timeout => $config['CALL_TIMEOUT'],
|
268
|
+
:use_ssl => base.scheme == "https",
|
269
|
+
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
|
270
|
+
:ca_file => $config['CA_TRUST']
|
271
|
+
) do |http|
|
272
|
+
http.request(request)
|
273
|
+
end
|
274
|
+
begin
|
275
|
+
list = JSON.parse(response.body)
|
276
|
+
if list['error']
|
277
|
+
return false
|
278
|
+
end
|
279
|
+
if list['success']
|
280
|
+
return true
|
281
|
+
end
|
282
|
+
rescue
|
283
|
+
puts "\nThe server sent out an Error:".red
|
284
|
+
puts clean_html(response.body)
|
285
|
+
exit 1
|
286
|
+
end
|
287
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phoseum-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julio C Hegedus
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yaml
|