spectre-core 1.8.3 → 1.11.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/exe/spectre +53 -16
- data/lib/spectre/assertion.rb +15 -21
- data/lib/spectre/curl.rb +45 -22
- data/lib/spectre/helpers.rb +84 -14
- data/lib/spectre/http.rb +32 -17
- data/lib/spectre/mixin.rb +9 -16
- data/lib/spectre/reporter/console.rb +1 -0
- data/lib/spectre/reporter/junit.rb +2 -0
- data/lib/spectre.rb +17 -13
- metadata +19 -65
- data/lib/spectre/database/postgres.rb +0 -78
- data/lib/spectre/ftp.rb +0 -195
- data/lib/spectre/mysql.rb +0 -97
- data/lib/spectre/ssh.rb +0 -149
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8333942c6050c7adf1bb9c238538b5364b5648514264612aefc161f715c48e58
|
4
|
+
data.tar.gz: 34ca46ef693108549a69c754bc3b22d5be33e642d9a6dfde040b664af47cfe58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a840c8d9afd7010e0873cf273400a8a5d82b28ad05941d1c4562c03a8aed60fabc7ed704dc2538c5960a940440204df0228d0ce77b6ee11ec68f820c3deb6954
|
7
|
+
data.tar.gz: f6913fa578a938341fbd15455c1e122a4c425bacb68d1353ce7c5f0c8ed96b206eba2625d28d1d8df91fec4161343a28b15db5535c3785000c88cbee914c0f2b
|
data/exe/spectre
CHANGED
@@ -48,6 +48,7 @@ DEFAULT_CONFIG = {
|
|
48
48
|
},
|
49
49
|
'debug' => false,
|
50
50
|
'out_path' => './reports',
|
51
|
+
'secure_keys' => ['password', 'secret', 'token', 'secure', 'authorization'],
|
51
52
|
'spec_patterns' => ['./specs/**/*.spec.rb'],
|
52
53
|
'mixin_patterns' => ['../common/mixins/**/*.mixin.rb', './mixins/**/*.mixin.rb'],
|
53
54
|
'env_patterns' => ['./environments/**/*.env.yml'],
|
@@ -68,10 +69,6 @@ DEFAULT_CONFIG = {
|
|
68
69
|
'spectre/http/basic_auth',
|
69
70
|
'spectre/http/keystone',
|
70
71
|
'spectre/resources',
|
71
|
-
'spectre/ssh',
|
72
|
-
'spectre/ftp',
|
73
|
-
'spectre/mysql',
|
74
|
-
# 'spectre/postgres',
|
75
72
|
],
|
76
73
|
'include' => [
|
77
74
|
|
@@ -126,11 +123,15 @@ Specific options:}
|
|
126
123
|
cmd_options['colored'] = false
|
127
124
|
end
|
128
125
|
|
126
|
+
opts.on('--ignore-failure', 'Always exit with code 0') do
|
127
|
+
cmd_options['ignore_failure'] = true
|
128
|
+
end
|
129
|
+
|
129
130
|
opts.on('-o PATH', '--out PATH', 'Output directory path') do |path|
|
130
|
-
cmd_options['out_path'] = path
|
131
|
+
cmd_options['out_path'] = File.absolute_path(path)
|
131
132
|
end
|
132
133
|
|
133
|
-
opts.on('-r NAME', '--
|
134
|
+
opts.on('-r NAME', '--reporters NAME', Array, "A list of reporters to use") do |reporters|
|
134
135
|
cmd_options['reporters'] = reporters
|
135
136
|
end
|
136
137
|
|
@@ -205,8 +206,9 @@ envs = {}
|
|
205
206
|
read_env_files = {}
|
206
207
|
cfg['env_patterns'].each do |pattern|
|
207
208
|
Dir.glob(pattern).each do|f|
|
208
|
-
spec_env = YAML.load_file(f)
|
209
|
-
|
209
|
+
spec_env = YAML.load_file(f) || {}
|
210
|
+
|
211
|
+
name = spec_env['name'] || 'default'
|
210
212
|
|
211
213
|
if envs.key? name
|
212
214
|
existing_env_file = read_env_files[name]
|
@@ -236,6 +238,10 @@ cfg.merge! env if env
|
|
236
238
|
|
237
239
|
String.colored! if cfg['colored']
|
238
240
|
|
241
|
+
# Load environment exlicitly before loading specs to make it available in spec definition
|
242
|
+
require_relative '../lib/spectre/environment' unless cfg['exclude'].include? 'spectre/environment'
|
243
|
+
Spectre.configure(cfg)
|
244
|
+
|
239
245
|
|
240
246
|
###########################################
|
241
247
|
# Load Specs
|
@@ -258,7 +264,7 @@ if action == 'list'
|
|
258
264
|
colors = [:blue, :magenta, :yellow, :green]
|
259
265
|
specs = Spectre.specs(cfg['specs'], cfg['tags'])
|
260
266
|
|
261
|
-
exit 1
|
267
|
+
exit 1 unless specs.any?
|
262
268
|
|
263
269
|
counter = 0
|
264
270
|
|
@@ -298,22 +304,38 @@ if action == 'run'
|
|
298
304
|
FileUtils.makedirs log_dir if !Dir.exists? log_dir
|
299
305
|
|
300
306
|
# Load Modules
|
307
|
+
|
301
308
|
cfg['modules']
|
302
309
|
.concat(cfg['include'])
|
303
310
|
.select { |mod| !cfg['exclude'].include? mod }
|
304
311
|
.each do |mod|
|
305
312
|
begin
|
306
|
-
|
307
|
-
|
308
|
-
|
313
|
+
spectre_lib_mod = File.join('../lib', mod)
|
314
|
+
|
315
|
+
if File.exists? mod
|
309
316
|
require_relative mod
|
317
|
+
|
318
|
+
elsif File.exists? spectre_lib_mod
|
319
|
+
require_relative spectre_lib_mod
|
320
|
+
|
321
|
+
else
|
322
|
+
require mod
|
310
323
|
end
|
311
|
-
|
324
|
+
|
325
|
+
rescue LoadError => e
|
312
326
|
puts "Unable to load module #{mod}. Check if the module exists or remove it from your spectre config:\n#{e.message}"
|
313
327
|
exit 1
|
314
328
|
end
|
315
329
|
end
|
316
330
|
|
331
|
+
# Load mixins
|
332
|
+
|
333
|
+
cfg['mixin_patterns'].each do |pattern|
|
334
|
+
Dir.glob(pattern).each do|f|
|
335
|
+
require_relative File.join(Dir.pwd, f)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
317
339
|
Spectre.configure(cfg)
|
318
340
|
|
319
341
|
Spectre::Logger.debug! if cfg['debug']
|
@@ -325,7 +347,7 @@ if action == 'run'
|
|
325
347
|
|
326
348
|
specs = Spectre.specs(cfg['specs'], cfg['tags'])
|
327
349
|
|
328
|
-
if specs.
|
350
|
+
if not specs.any?
|
329
351
|
puts "No specs found in #{Dir.pwd}"
|
330
352
|
exit 1
|
331
353
|
end
|
@@ -337,7 +359,11 @@ if action == 'run'
|
|
337
359
|
reporter.report(run_infos)
|
338
360
|
end
|
339
361
|
|
340
|
-
|
362
|
+
errors = run_infos.select { |x| x.error != nil or x.failure != nil }
|
363
|
+
|
364
|
+
exit 0 if cfg['ignore_failure'] or not errors.any?
|
365
|
+
|
366
|
+
exit 1
|
341
367
|
end
|
342
368
|
|
343
369
|
|
@@ -347,7 +373,7 @@ end
|
|
347
373
|
|
348
374
|
|
349
375
|
if action == 'envs'
|
350
|
-
exit 1
|
376
|
+
exit 1 unless envs.any?
|
351
377
|
puts envs.pretty
|
352
378
|
exit 0
|
353
379
|
end
|
@@ -464,6 +490,16 @@ reports/
|
|
464
490
|
**/environments/*.env.secret.yml
|
465
491
|
]
|
466
492
|
|
493
|
+
DEFAULT_GEMFILE = %[source 'https://rubygems.org'
|
494
|
+
|
495
|
+
gem 'spectre-core', '>= #{Spectre::VERSION}'
|
496
|
+
# gem 'spectre-mysql', '>= 1.0.0'
|
497
|
+
# gem 'spectre-ssh', '>= 1.0.0'
|
498
|
+
# gem 'spectre-ftp', '>= 1.0.0'
|
499
|
+
# gem 'spectre-curl', '>= 1.0.0'
|
500
|
+
# gem 'spectre-git', '>= 0.1.0'
|
501
|
+
]
|
502
|
+
|
467
503
|
if action == 'init'
|
468
504
|
DEFAULT_FILES = [
|
469
505
|
['./environments/default.env.yml', DEFAULT_ENV_CFG],
|
@@ -471,6 +507,7 @@ if action == 'init'
|
|
471
507
|
['./specs/sample.spec.rb', SAMPLE_SPEC],
|
472
508
|
['./spectre.yml', DEFAULT_SPECTRE_CFG],
|
473
509
|
['./.gitignore', DEFAULT_GITIGNORE],
|
510
|
+
['./Gemfile', DEFAULT_GEMFILE],
|
474
511
|
]
|
475
512
|
|
476
513
|
%w(environments logs specs).each do |dir_name|
|
data/lib/spectre/assertion.rb
CHANGED
@@ -6,19 +6,19 @@ module Spectre
|
|
6
6
|
module Assertion
|
7
7
|
class ::Object
|
8
8
|
def should_be(val)
|
9
|
-
|
9
|
+
raise AssertionFailure.new("The value '#{self.to_s.trim}' should be '#{val.to_s.trim}'", val, self) unless self.to_s == val.to_s
|
10
10
|
end
|
11
11
|
|
12
12
|
def should_be_empty
|
13
|
-
raise AssertionFailure.new("The value '#{self.to_s.trim}' should be empty", nil, self) unless self
|
13
|
+
raise AssertionFailure.new("The value '#{self.to_s.trim}' should be empty", nil, self) unless self.nil?
|
14
14
|
end
|
15
15
|
|
16
16
|
def should_not_be(val)
|
17
|
-
|
17
|
+
raise AssertionFailure.new("The value '#{self.to_s.trim}' should not be '#{val.to_s.trim}'", val, self) unless self.to_s != val.to_s
|
18
18
|
end
|
19
19
|
|
20
20
|
def should_not_exist
|
21
|
-
|
21
|
+
raise AssertionFailure.new("The value '#{self.to_s.trim}' should not exist, but it does", val, self) unless self.to_s != nil
|
22
22
|
end
|
23
23
|
|
24
24
|
def should_not_be_empty
|
@@ -76,7 +76,7 @@ module Spectre
|
|
76
76
|
val = OpenStruct.new(val)
|
77
77
|
end
|
78
78
|
|
79
|
-
raise AssertionFailure.new("The list [#{list.join(', ').trim}] should contain '#{val.trim}'", val, list) unless list.include? val
|
79
|
+
raise AssertionFailure.new("The list [#{list.join(', ').trim}] should contain '#{val.to_s.trim}'", val, list) unless list.include? val
|
80
80
|
end
|
81
81
|
|
82
82
|
def should_not_contain(val)
|
@@ -87,15 +87,15 @@ module Spectre
|
|
87
87
|
val = OpenStruct.new(val)
|
88
88
|
end
|
89
89
|
|
90
|
-
raise AssertionFailure.new("The list [#{list.join(', ').trim}] should not contain '#{val.trim}'", val, list) if list.include? val
|
90
|
+
raise AssertionFailure.new("The list [#{list.join(', ').trim}] should not contain '#{val.to_s.trim}'", val, list) if list.include? val
|
91
91
|
end
|
92
92
|
|
93
93
|
def should_be_empty
|
94
|
-
raise AssertionFailure.new('empty list', self) unless self.
|
94
|
+
raise AssertionFailure.new('empty list', self) unless self.empty?
|
95
95
|
end
|
96
96
|
|
97
97
|
def should_not_be_empty
|
98
|
-
raise AssertionFailure.new('no empty list', self)
|
98
|
+
raise AssertionFailure.new('no empty list', self) if self.empty?
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
@@ -110,7 +110,7 @@ module Spectre
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def should_not_be(val)
|
113
|
-
|
113
|
+
raise AssertionFailure.new("The text '#{self.trim}' should not be '#{val.to_s.trim}'", val, self) unless self != val
|
114
114
|
end
|
115
115
|
|
116
116
|
def should_not_be_empty
|
@@ -118,8 +118,10 @@ module Spectre
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def should_contain(value)
|
121
|
+
raise AssertionFailure.new("`value' must not be nil") if value.nil?
|
122
|
+
|
121
123
|
predicate = proc { |x| self.include? x.to_s }
|
122
|
-
evaluation = SingleEvaluation.new
|
124
|
+
evaluation = SingleEvaluation.new(value)
|
123
125
|
success = evaluation.call(predicate)
|
124
126
|
|
125
127
|
return if success
|
@@ -165,7 +167,7 @@ module Spectre
|
|
165
167
|
|
166
168
|
class SingleEvaluation < Evaluation
|
167
169
|
def initialize value
|
168
|
-
super
|
170
|
+
super(value, nil)
|
169
171
|
end
|
170
172
|
|
171
173
|
def call predicate
|
@@ -180,7 +182,7 @@ module Spectre
|
|
180
182
|
|
181
183
|
class OrEvaluation < Evaluation
|
182
184
|
def initialize value, other
|
183
|
-
super
|
185
|
+
super(value, other)
|
184
186
|
end
|
185
187
|
|
186
188
|
def call predicate
|
@@ -195,7 +197,7 @@ module Spectre
|
|
195
197
|
|
196
198
|
class AndEvaluation < Evaluation
|
197
199
|
def initialize value, other
|
198
|
-
super
|
200
|
+
super(value, other)
|
199
201
|
end
|
200
202
|
|
201
203
|
def call predicate
|
@@ -222,14 +224,6 @@ module Spectre
|
|
222
224
|
class << self
|
223
225
|
@@success = nil
|
224
226
|
|
225
|
-
def eval_assertion predicate, val
|
226
|
-
if val.is_a? Proc
|
227
|
-
val.call(predicate)
|
228
|
-
else
|
229
|
-
predicate.call(val)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
227
|
def expect desc
|
234
228
|
begin
|
235
229
|
Logger.log_process("expect #{desc}")
|
data/lib/spectre/curl.rb
CHANGED
@@ -192,7 +192,8 @@ module Spectre::Curl
|
|
192
192
|
return str unless str or str.empty?
|
193
193
|
|
194
194
|
begin
|
195
|
-
json = JSON.parse
|
195
|
+
json = JSON.parse(str)
|
196
|
+
json.obfuscate!(@@secure_keys) if not @@debug
|
196
197
|
|
197
198
|
if pretty
|
198
199
|
str = JSON.pretty_generate(json)
|
@@ -206,6 +207,25 @@ module Spectre::Curl
|
|
206
207
|
str
|
207
208
|
end
|
208
209
|
|
210
|
+
def is_secure? key
|
211
|
+
@@secure_keys.any? { |x| key.to_s.downcase.include? x.downcase }
|
212
|
+
end
|
213
|
+
|
214
|
+
def header_to_s headers
|
215
|
+
s = ''
|
216
|
+
|
217
|
+
return s unless headers
|
218
|
+
|
219
|
+
headers.each do |header|
|
220
|
+
key = header[0].to_s
|
221
|
+
value = header[1].to_s
|
222
|
+
value = '*****' if is_secure?(key) and not @@debug
|
223
|
+
s += "#{key.ljust(30, '.')}: #{value}\n"
|
224
|
+
end
|
225
|
+
|
226
|
+
s
|
227
|
+
end
|
228
|
+
|
209
229
|
def invoke req
|
210
230
|
cmd = [@@curl_path]
|
211
231
|
|
@@ -230,11 +250,11 @@ module Spectre::Curl
|
|
230
250
|
uri += '?'
|
231
251
|
uri += req['query']
|
232
252
|
.map { |x| x.join '='}
|
233
|
-
.join
|
253
|
+
.join('&')
|
234
254
|
end
|
235
255
|
|
236
|
-
cmd.append
|
237
|
-
cmd.append
|
256
|
+
cmd.append('"' + uri + '"')
|
257
|
+
cmd.append('-X', req['method']) unless req['method'] == 'GET' or (req['body'] and req['method'] == 'POST')
|
238
258
|
|
239
259
|
# Call all registered modules
|
240
260
|
@@modules.each do |mod|
|
@@ -243,43 +263,41 @@ module Spectre::Curl
|
|
243
263
|
|
244
264
|
# Add headers to curl command
|
245
265
|
req['headers'].each do |header|
|
246
|
-
cmd.append
|
266
|
+
cmd.append('-H', '"' + header.join(':') + '"')
|
247
267
|
end if req['headers']
|
248
268
|
|
249
269
|
# Add request body
|
250
270
|
if req['body'] != nil and not req['body'].empty?
|
251
271
|
req_body = try_format_json(req['body']).gsub(/"/, '\\"')
|
252
|
-
cmd.append
|
272
|
+
cmd.append('-d', '"' + req_body + '"')
|
253
273
|
elsif ['POST', 'PUT', 'PATCH'].include? req['method'].upcase
|
254
|
-
cmd.append
|
274
|
+
cmd.append('-d', '"\n"')
|
255
275
|
end
|
256
276
|
|
257
277
|
# Add certificate path if one if given
|
258
278
|
if req['cert']
|
259
279
|
raise "Certificate '#{req['cert']}' does not exist" unless File.exists? req['cert']
|
260
|
-
cmd.append
|
280
|
+
cmd.append('--cacert', req['cert'])
|
261
281
|
elsif req['use_ssl'] or uri.start_with? 'https'
|
262
|
-
cmd.append
|
282
|
+
cmd.append('-k')
|
263
283
|
end
|
264
284
|
|
265
|
-
cmd.append
|
266
|
-
cmd.append
|
285
|
+
cmd.append('-i')
|
286
|
+
cmd.append('-v')
|
267
287
|
|
268
|
-
@@request = OpenStruct.new
|
288
|
+
@@request = OpenStruct.new(req)
|
269
289
|
|
270
|
-
sys_cmd = cmd.join
|
290
|
+
sys_cmd = cmd.join(' ')
|
271
291
|
|
272
|
-
@@logger.debug
|
292
|
+
@@logger.debug(sys_cmd)
|
273
293
|
|
274
294
|
req_id = SecureRandom.uuid()[0..5]
|
275
295
|
|
276
296
|
req_log = "[>] #{req_id} #{req['method']} #{uri}\n"
|
277
|
-
req['headers']
|
278
|
-
|
279
|
-
end if req['headers']
|
280
|
-
req_log += req['body'] if req['body'] != nil and not req['body'].empty?
|
297
|
+
req_log += header_to_s(req['headers'])
|
298
|
+
req_log += try_format_json(req['body'], pretty: true)
|
281
299
|
|
282
|
-
@@logger.info
|
300
|
+
@@logger.info(req_log)
|
283
301
|
|
284
302
|
start_time = Time.now
|
285
303
|
|
@@ -297,7 +315,7 @@ module Spectre::Curl
|
|
297
315
|
|
298
316
|
raise "Unable to request #{uri}. Please check if this service is reachable." unless output
|
299
317
|
|
300
|
-
@@logger.debug
|
318
|
+
@@logger.debug("[<] #{req_id} stdout:\n#{output}")
|
301
319
|
|
302
320
|
header, body = output.split /\r?\n\r?\n/
|
303
321
|
|
@@ -342,7 +360,7 @@ module Spectre::Curl
|
|
342
360
|
|
343
361
|
@@logger.info res_log
|
344
362
|
|
345
|
-
@@response = SpectreHttpResponse.new
|
363
|
+
@@response = SpectreHttpResponse.new(res)
|
346
364
|
|
347
365
|
raise "Response did not indicate success: #{@@response.code} #{@@response.message}" if req['ensure_success'] and not @@response.success?
|
348
366
|
|
@@ -351,7 +369,12 @@ module Spectre::Curl
|
|
351
369
|
end
|
352
370
|
|
353
371
|
Spectre.register do |config|
|
354
|
-
@@
|
372
|
+
@@debug = config['debug']
|
373
|
+
|
374
|
+
@@logger = ::Logger.new(config['log_file'], progname: 'spectre/curl')
|
375
|
+
@@logger.level = @@debug ? Logger::DEBUG : Logger::INFO
|
376
|
+
|
377
|
+
@@secure_keys = config['secure_keys'] || []
|
355
378
|
|
356
379
|
@@curl_path = config['curl_path'] || 'curl'
|
357
380
|
|
data/lib/spectre/helpers.rb
CHANGED
@@ -2,6 +2,7 @@ require 'securerandom'
|
|
2
2
|
require 'json'
|
3
3
|
require 'date'
|
4
4
|
require 'ostruct'
|
5
|
+
require 'jsonpath'
|
5
6
|
|
6
7
|
class ::String
|
7
8
|
def as_json
|
@@ -12,17 +13,57 @@ class ::String
|
|
12
13
|
DateTime.parse(self)
|
13
14
|
end
|
14
15
|
|
16
|
+
def as_timestamp
|
17
|
+
DateTime.parse(self).to_time.to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def with mapping
|
21
|
+
return self unless mapping and mapping.is_a? Hash
|
22
|
+
|
23
|
+
new_string = self
|
24
|
+
|
25
|
+
mapping.each do |key, value|
|
26
|
+
new_string = new_string.gsub('#{' + key.to_s + '}', value.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
new_string
|
30
|
+
end
|
31
|
+
|
32
|
+
def trim size = 50
|
33
|
+
if (self.length + 3) > size
|
34
|
+
return self[0..size-4] + '...'
|
35
|
+
end
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def pick path
|
41
|
+
raise ArgumentError.new("`path' must not be nil or empty") if path.nil? or path.empty?
|
42
|
+
|
43
|
+
begin
|
44
|
+
JsonPath.on(self, path)
|
45
|
+
|
46
|
+
rescue MultiJson::ParseError
|
47
|
+
# do nothing and return nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# File helpers
|
52
|
+
|
15
53
|
def content with: nil
|
16
54
|
fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
|
17
55
|
file_content = File.read(self)
|
18
56
|
|
19
57
|
if with
|
20
|
-
with
|
21
|
-
|
22
|
-
|
58
|
+
file_content.with(with)
|
59
|
+
else
|
60
|
+
file_content
|
23
61
|
end
|
62
|
+
end
|
24
63
|
|
25
|
-
|
64
|
+
def file_size
|
65
|
+
fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
|
66
|
+
File.size(self)
|
26
67
|
end
|
27
68
|
|
28
69
|
def exists?
|
@@ -31,17 +72,8 @@ class ::String
|
|
31
72
|
|
32
73
|
def remove!
|
33
74
|
fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
|
34
|
-
|
35
75
|
File.delete self
|
36
76
|
end
|
37
|
-
|
38
|
-
def trim count=50
|
39
|
-
if (self.length + 3) > count
|
40
|
-
return self[0..count] + '...'
|
41
|
-
end
|
42
|
-
|
43
|
-
self
|
44
|
-
end
|
45
77
|
end
|
46
78
|
|
47
79
|
|
@@ -49,6 +81,22 @@ class ::OpenStruct
|
|
49
81
|
def to_json *args, **kwargs
|
50
82
|
self.to_h.inject({}) { |memo, (k,v)| memo[k] = v.is_a?(OpenStruct) ? v.to_h : v; memo }.to_json(*args, **kwargs)
|
51
83
|
end
|
84
|
+
|
85
|
+
def pick path
|
86
|
+
raise ArgumentError.new("`path' must not be nil or empty") if path.nil? or path.empty?
|
87
|
+
|
88
|
+
JsonPath.on(self, path)
|
89
|
+
end
|
90
|
+
|
91
|
+
def default_to! defaults
|
92
|
+
defaults.each_key do |key|
|
93
|
+
if not self[key] != nil
|
94
|
+
self[key] = defaults[key]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
alias :defaults_to! :default_to!
|
52
100
|
end
|
53
101
|
|
54
102
|
|
@@ -56,9 +104,31 @@ class ::Hash
|
|
56
104
|
def symbolize_keys
|
57
105
|
self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
|
58
106
|
end
|
107
|
+
|
108
|
+
def default_to! defaults
|
109
|
+
defaults.each_key do |key|
|
110
|
+
if not self[key] != nil
|
111
|
+
self[key] = defaults[key]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
alias :defaults_to! :default_to!
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
class ::Array
|
121
|
+
def last
|
122
|
+
self[-1]
|
123
|
+
end
|
59
124
|
end
|
60
125
|
|
61
126
|
|
62
127
|
def uuid length = 5
|
63
|
-
SecureRandom.uuid().gsub('-', '')[0..length]
|
128
|
+
SecureRandom.uuid().gsub('-', '')[0..length-1]
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def now
|
133
|
+
Time.now
|
64
134
|
end
|