blackstack-core 1.2.28 → 1.2.29
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/lib/functions.rb +194 -163
- 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: 61e89c40560e966edd297c6fbce7e0099a94bce4dd88c0653f7e20feceb8234e
|
4
|
+
data.tar.gz: 28543d06207cc6399371e857b0982ec7432801e5344372304165a0aaa4053660
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8545ef59846393d06cb272b8e503847b3c11e9837933ac770c546acf724265a6e560407dea7bcbe67bccf49ec505b89463b4aab52bb83094fdeb792b517f7479
|
7
|
+
data.tar.gz: 3eaac8af64b460a4cf941d3756faa219da65a75300cbe47fe1574ae4bb9426e3a5d6a2c028dcb81e940646bc4566f433ed48493cbf4665c5eee58d76b341823f
|
data/lib/functions.rb
CHANGED
@@ -20,7 +20,7 @@ module BlackStack
|
|
20
20
|
|
21
21
|
def self.api_url
|
22
22
|
@@api_url
|
23
|
-
end
|
23
|
+
end
|
24
24
|
|
25
25
|
def self.api_port
|
26
26
|
@@api_port
|
@@ -37,7 +37,7 @@ module BlackStack
|
|
37
37
|
def self.classes
|
38
38
|
@@classes
|
39
39
|
end # def self.classes
|
40
|
-
|
40
|
+
|
41
41
|
def self.set_client(
|
42
42
|
api_key: ,
|
43
43
|
api_url: ,
|
@@ -60,7 +60,7 @@ module BlackStack
|
|
60
60
|
end # def self.set_server
|
61
61
|
|
62
62
|
def self.post(
|
63
|
-
endpoint: ,
|
63
|
+
endpoint: ,
|
64
64
|
params: {}
|
65
65
|
)
|
66
66
|
begin
|
@@ -84,7 +84,7 @@ module BlackStack
|
|
84
84
|
|
85
85
|
# Base class.
|
86
86
|
# List of methods you have to overload if you develop a profile type.
|
87
|
-
#
|
87
|
+
#
|
88
88
|
class Base
|
89
89
|
# object json descriptor
|
90
90
|
attr_accessor :desc
|
@@ -120,10 +120,10 @@ module BlackStack
|
|
120
120
|
|
121
121
|
|
122
122
|
# Get array of hash descriptor of profile.
|
123
|
-
#
|
124
|
-
# Parameters:
|
123
|
+
#
|
124
|
+
# Parameters:
|
125
125
|
# - id_account: guid. Id of the account to bring the profiles. Sysowner must provide the id_account for getting an account value. For non sysowner it is assigned to his account.
|
126
|
-
# - promiscuous: boolean. It works only for Sysowner. If true, it will bring all non-deleted rows. If false, it will bring only rows matching id_profile. Default: false.
|
126
|
+
# - promiscuous: boolean. It works only for Sysowner. If true, it will bring all non-deleted rows, including the ones that are not owned by Sysowner. If false, it will bring only rows matching id_profile. Default: false.
|
127
127
|
# - page: integer. Page number.
|
128
128
|
# - limit: integer. Number of profiles per page.
|
129
129
|
# - params: hash. Additional filter parameters used by the specific child class.
|
@@ -147,8 +147,8 @@ module BlackStack
|
|
147
147
|
end # def self.base
|
148
148
|
|
149
149
|
# Get array of hash descriptors of profiles.
|
150
|
-
#
|
151
|
-
# Parameters:
|
150
|
+
#
|
151
|
+
# Parameters:
|
152
152
|
# - id: guid. Id of the profile to bring.
|
153
153
|
#
|
154
154
|
def self.count
|
@@ -163,8 +163,8 @@ module BlackStack
|
|
163
163
|
end # def self.count
|
164
164
|
|
165
165
|
# Get array of hash descriptors of profiles.
|
166
|
-
#
|
167
|
-
# Parameters:
|
166
|
+
#
|
167
|
+
# Parameters:
|
168
168
|
# - id: guid. Id of the profile to bring.
|
169
169
|
#
|
170
170
|
def self.get(id)
|
@@ -179,6 +179,23 @@ module BlackStack
|
|
179
179
|
return self.new(ret['result']).child_class_instance
|
180
180
|
end # def self.get
|
181
181
|
|
182
|
+
# Delete object.
|
183
|
+
#
|
184
|
+
# Parameters:
|
185
|
+
# - id: guid. Id of the profile to bring.
|
186
|
+
#
|
187
|
+
def self.delete(id)
|
188
|
+
params = {}
|
189
|
+
params['id'] = id
|
190
|
+
params['backtrace'] = BlackStack::API.backtrace
|
191
|
+
ret = BlackStack::API.post(
|
192
|
+
endpoint: "#{self.object_name}/delete",
|
193
|
+
params: params
|
194
|
+
)
|
195
|
+
raise "Error calling get endpoint: #{ret['status']}" if ret['status'] != 'success'
|
196
|
+
return self.new(ret['result']).child_class_instance
|
197
|
+
end # def self.get
|
198
|
+
|
182
199
|
# Submit a hash descriptor to the server for an update
|
183
200
|
#
|
184
201
|
def self.update(desc)
|
@@ -248,6 +265,20 @@ module BlackStack
|
|
248
265
|
return ret['result'] == {} ? nil : self.new(ret['result']).child_class_instance
|
249
266
|
end # def self.upsert
|
250
267
|
|
268
|
+
# Submit a hash descriptor to the server for an upsert
|
269
|
+
#
|
270
|
+
def self.upsert2(desc)
|
271
|
+
params = {}
|
272
|
+
params['desc'] = desc
|
273
|
+
params['backtrace'] = BlackStack::API.backtrace
|
274
|
+
ret = BlackStack::API.post(
|
275
|
+
endpoint: "#{self.object_name}/upsert2",
|
276
|
+
params: params
|
277
|
+
)
|
278
|
+
raise "Error calling upsert endpoint: #{ret['status']}" if ret['status'] != 'success'
|
279
|
+
return ret['result'] == {} ? nil : self.new(ret['result']).child_class_instance
|
280
|
+
end # def self.upsert
|
281
|
+
|
251
282
|
# Submit a hash descriptor to the server for an upsert
|
252
283
|
#
|
253
284
|
def upsert
|
@@ -266,7 +297,7 @@ module BlackStack
|
|
266
297
|
ret = nil
|
267
298
|
# getting the HTML
|
268
299
|
zyte = ZyteClient.new(key: api_key)
|
269
|
-
html = zyte.extract(url: url, options: options, data_filename: data_filename)
|
300
|
+
html = zyte.extract(url: url, options: options, data_filename: data_filename)
|
270
301
|
# return the URL of the file in the cloud
|
271
302
|
return html
|
272
303
|
end # def zyte_html
|
@@ -284,7 +315,7 @@ module BlackStack
|
|
284
315
|
def zyte_snapshot(url, api_key:, options:, data_filename:, dropbox_folder:nil, retry_times: 3)
|
285
316
|
# "The garbage character must be due to the 520 error code which was caused on the second request."
|
286
317
|
garbage = "\x9E\xE9e"
|
287
|
-
|
318
|
+
|
288
319
|
ret = nil
|
289
320
|
raise "Either dropbox_folder parameter or self.desc['id_account'] are required." if dropbox_folder.nil? && self.desc['id_account'].nil?
|
290
321
|
dropbox_folder = self.desc['id_account'] if dropbox_folder.nil?
|
@@ -302,7 +333,7 @@ module BlackStack
|
|
302
333
|
try = 0
|
303
334
|
html = garbage
|
304
335
|
while try < retry_times && html == garbage
|
305
|
-
html = zyte.extract(url: url, options: options, data_filename: data_filename)
|
336
|
+
html = zyte.extract(url: url, options: options, data_filename: data_filename)
|
306
337
|
try += 1
|
307
338
|
end
|
308
339
|
# save the HTML in the local file in /tmp
|
@@ -318,9 +349,9 @@ module BlackStack
|
|
318
349
|
|
319
350
|
end # class Base
|
320
351
|
|
321
|
-
# -----------------------------------------------------------------------------------------
|
352
|
+
# -----------------------------------------------------------------------------------------
|
322
353
|
# PRY Supporting Functions
|
323
|
-
# -----------------------------------------------------------------------------------------
|
354
|
+
# -----------------------------------------------------------------------------------------
|
324
355
|
module Debugging
|
325
356
|
@@allow_breakpoints = false
|
326
357
|
@@verbose = false
|
@@ -341,7 +372,7 @@ module BlackStack
|
|
341
372
|
print "Breakpoint are not allowed" if @@verbose
|
342
373
|
end
|
343
374
|
|
344
|
-
Binding.class_eval do
|
375
|
+
Binding.class_eval do
|
345
376
|
alias_method :old_pry, :pry
|
346
377
|
define_method :pry, new_pry
|
347
378
|
end
|
@@ -349,21 +380,21 @@ module BlackStack
|
|
349
380
|
end
|
350
381
|
end
|
351
382
|
|
352
|
-
# -----------------------------------------------------------------------------------------
|
383
|
+
# -----------------------------------------------------------------------------------------
|
353
384
|
# OCRA Supporting Functions
|
354
|
-
# -----------------------------------------------------------------------------------------
|
385
|
+
# -----------------------------------------------------------------------------------------
|
355
386
|
module OCRA
|
356
387
|
# OCRA files run into a temp folder, where the script is unpacked.
|
357
|
-
#
|
388
|
+
#
|
358
389
|
# This function is useful to require a configuration file when the
|
359
390
|
# script is running inside an OCRA temp folder, since the local folder
|
360
391
|
# of the running command is not the filder where the exe file is hosted.
|
361
|
-
#
|
362
|
-
# More information:
|
392
|
+
#
|
393
|
+
# More information:
|
363
394
|
# * https://stackoverflow.com/questions/1937743/how-to-get-the-current-working-directorys-absolute-path-from-irb
|
364
395
|
# * https://stackoverflow.com/questions/8577223/ruby-get-the-file-being-executed
|
365
396
|
# * https://stackoverflow.com/questions/7399882/ruby-getting-path-from-pathfilename/7400057
|
366
|
-
#
|
397
|
+
#
|
367
398
|
def self.require_in_working_path(filename, path, show_path_info=false)
|
368
399
|
puts '' if show_path_info
|
369
400
|
path = File.expand_path File.dirname(path)
|
@@ -375,13 +406,13 @@ module BlackStack
|
|
375
406
|
end
|
376
407
|
end # module OCRA
|
377
408
|
|
378
|
-
# -----------------------------------------------------------------------------------------
|
409
|
+
# -----------------------------------------------------------------------------------------
|
379
410
|
# DateTime Functions
|
380
|
-
# -----------------------------------------------------------------------------------------
|
381
|
-
module DateTime
|
382
|
-
# -----------------------------------------------------------------------------------------
|
411
|
+
# -----------------------------------------------------------------------------------------
|
412
|
+
module DateTime
|
413
|
+
# -----------------------------------------------------------------------------------------
|
383
414
|
# Encoding
|
384
|
-
# -----------------------------------------------------------------------------------------
|
415
|
+
# -----------------------------------------------------------------------------------------
|
385
416
|
module Encoding
|
386
417
|
# Convierte un objeto date-time a un string con formato sql-datetime (yyyy-mm-dd hh:mm:ss).
|
387
418
|
def self.datetime_to_sql(o)
|
@@ -389,50 +420,50 @@ module BlackStack
|
|
389
420
|
end
|
390
421
|
end # module Encode
|
391
422
|
|
392
|
-
# -----------------------------------------------------------------------------------------
|
423
|
+
# -----------------------------------------------------------------------------------------
|
393
424
|
# Miscelaneous
|
394
|
-
# -----------------------------------------------------------------------------------------
|
425
|
+
# -----------------------------------------------------------------------------------------
|
395
426
|
module Misc
|
396
427
|
def self.datetime_values_check(year,month,day,hour,minute,second)
|
397
428
|
if (year.to_i<1900 || year.to_i>=2100)
|
398
429
|
return false
|
399
430
|
end
|
400
|
-
|
431
|
+
|
401
432
|
if (month.to_i<1 || month.to_i>12)
|
402
433
|
return false
|
403
434
|
end
|
404
|
-
|
435
|
+
|
405
436
|
# TODO: Considerar la cantidad de dias de cada mes, y los anios biciestos. Buscar alguna funcion existente.
|
406
437
|
if (day.to_i<1 || day.to_i>31)
|
407
438
|
return false
|
408
439
|
end
|
409
|
-
|
440
|
+
|
410
441
|
if (hour.to_i<0 || hour.to_i>23)
|
411
442
|
return false
|
412
443
|
end
|
413
|
-
|
444
|
+
|
414
445
|
if (minute.to_i<0 || minute.to_i>59)
|
415
446
|
return false
|
416
447
|
end
|
417
|
-
|
448
|
+
|
418
449
|
if (second.to_i<0 || second.to_i>59)
|
419
450
|
return false
|
420
451
|
end
|
421
|
-
|
452
|
+
|
422
453
|
return true
|
423
454
|
end # datetime_values_check
|
424
455
|
end # module Misc
|
425
456
|
end # module DateTime
|
426
457
|
|
427
|
-
# -----------------------------------------------------------------------------------------
|
458
|
+
# -----------------------------------------------------------------------------------------
|
428
459
|
# Numeric Functions
|
429
|
-
# -----------------------------------------------------------------------------------------
|
460
|
+
# -----------------------------------------------------------------------------------------
|
430
461
|
module Number
|
431
|
-
# -----------------------------------------------------------------------------------------
|
462
|
+
# -----------------------------------------------------------------------------------------
|
432
463
|
# Encoding
|
433
|
-
# -----------------------------------------------------------------------------------------
|
464
|
+
# -----------------------------------------------------------------------------------------
|
434
465
|
module Encoding
|
435
|
-
# Converts number to a string with a format like xx,xxx,xxx.xxxx
|
466
|
+
# Converts number to a string with a format like xx,xxx,xxx.xxxx
|
436
467
|
# number: it may be int or float
|
437
468
|
def self.format_with_separator(number)
|
438
469
|
whole_part, decimal_part = number.to_s.split('.')
|
@@ -444,7 +475,7 @@ module BlackStack
|
|
444
475
|
# Ejemplo: "4 hours, 30 minutes"
|
445
476
|
# Ejemplo: "3 days, 4 hour"
|
446
477
|
def self.encode_minutes(n)
|
447
|
-
# TODO: validar que n sea un entero mayor a 0
|
478
|
+
# TODO: validar que n sea un entero mayor a 0
|
448
479
|
if (n<0)
|
449
480
|
return "?"
|
450
481
|
end
|
@@ -459,13 +490,13 @@ module BlackStack
|
|
459
490
|
end # module Encode
|
460
491
|
end # module Number
|
461
492
|
|
462
|
-
# -----------------------------------------------------------------------------------------
|
493
|
+
# -----------------------------------------------------------------------------------------
|
463
494
|
# String Functions
|
464
|
-
# -----------------------------------------------------------------------------------------
|
495
|
+
# -----------------------------------------------------------------------------------------
|
465
496
|
module Strings
|
466
497
|
|
467
498
|
GUID_SIZE = 36
|
468
|
-
MATCH_PASSWORD = /(?=.*[a-zA-Z])(?=.*[0-9]).{6,}/
|
499
|
+
MATCH_PASSWORD = /(?=.*[a-zA-Z])(?=.*[0-9]).{6,}/
|
469
500
|
MATCH_GUID = /{?[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]\-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]\-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]\-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]}?/
|
470
501
|
MATCH_FILENAME = /[\w\-\_\.]+/
|
471
502
|
MATCH_EMAIL = /[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{1,25}/
|
@@ -474,7 +505,7 @@ module BlackStack
|
|
474
505
|
MATCH_PHONE = /(?:\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}/
|
475
506
|
|
476
507
|
# Note: MATCH_URL gets the URL up to '?', but it doesn't retrieves the parameters.
|
477
|
-
# Exmaple:
|
508
|
+
# Exmaple:
|
478
509
|
# https://foo.com/bar?param1=value1¶m2=value2 --> https://foo.com/bar?
|
479
510
|
# https://foo.com/bar/?param1=value1¶m2=value2 --> https://foo.com/bar/?
|
480
511
|
MATCH_URL = /(https?:\/\/)?([\da-z\.-]+)([\.\:])([\da-z]{2,6})([\/[\da-z\.\-]+]*[\da-z])(\/)?(\?)?/i
|
@@ -484,32 +515,32 @@ module BlackStack
|
|
484
515
|
MATCH_CONTENT_SPINNING = /{[^}]+}/
|
485
516
|
MATCH_SPINNED_TEXT = /code me/ # TODO: define this regex for the issue #1226
|
486
517
|
|
487
|
-
# -----------------------------------------------------------------------------------------
|
518
|
+
# -----------------------------------------------------------------------------------------
|
488
519
|
# Fuzzy String Comparsion Functions: How similar are 2 strings that are not exactly equal.
|
489
|
-
# -----------------------------------------------------------------------------------------
|
520
|
+
# -----------------------------------------------------------------------------------------
|
490
521
|
module SQL
|
491
522
|
def self.string_to_sql_string(s)
|
492
523
|
#return s.force_encoding("UTF-8").gsub("'", "''").to_s
|
493
524
|
return s.gsub("'", "''").to_s
|
494
525
|
end
|
495
526
|
end
|
496
|
-
|
497
|
-
# -----------------------------------------------------------------------------------------
|
527
|
+
|
528
|
+
# -----------------------------------------------------------------------------------------
|
498
529
|
# Fuzzy String Comparsion Functions: How similar are 2 strings that are not exactly equal.
|
499
|
-
# -----------------------------------------------------------------------------------------
|
530
|
+
# -----------------------------------------------------------------------------------------
|
500
531
|
module Comparing
|
501
532
|
# retorna 0 si los strings son iguales
|
502
533
|
# https://stackoverflow.com/questions/16323571/measure-the-distance-between-two-strings-with-ruby
|
503
|
-
def self.levenshtein_distance(s, t)
|
534
|
+
def self.levenshtein_distance(s, t)
|
504
535
|
s.downcase!
|
505
536
|
t.downcase!
|
506
|
-
|
537
|
+
|
507
538
|
m = s.length
|
508
539
|
n = t.length
|
509
540
|
return m if n == 0
|
510
541
|
return n if m == 0
|
511
542
|
d = Array.new(m+1) {Array.new(n+1)}
|
512
|
-
|
543
|
+
|
513
544
|
(0..m).each {|i| d[i][0] = i}
|
514
545
|
(0..n).each {|j| d[0][j] = j}
|
515
546
|
(1..n).each do |j|
|
@@ -526,7 +557,7 @@ module BlackStack
|
|
526
557
|
end
|
527
558
|
d[m][n]
|
528
559
|
end
|
529
|
-
|
560
|
+
|
530
561
|
# retorna la cantidad de palabras con mas de 3 caracteres que se encuentran en el parametro s
|
531
562
|
def self.max_sardi_distance(s)
|
532
563
|
s.downcase!
|
@@ -541,14 +572,14 @@ module BlackStack
|
|
541
572
|
}
|
542
573
|
n
|
543
574
|
end
|
544
|
-
|
575
|
+
|
545
576
|
# retorna la cantidad de palabras con mas de 3 caracteres del parametro s que se encuentran en el parametro t
|
546
577
|
def self.sardi_distance(s, t)
|
547
578
|
s.downcase!
|
548
579
|
t.downcase!
|
549
580
|
s.gsub!(/-/,' ')
|
550
581
|
t.gsub!(/-/,' ')
|
551
|
-
max_distance = max_sardi_distance(s)
|
582
|
+
max_distance = max_sardi_distance(s)
|
552
583
|
ss = s.scan(/\b([a-z]+)\b/)
|
553
584
|
tt = t.scan(/\b([a-z]+)\b/)
|
554
585
|
n = 0
|
@@ -563,20 +594,20 @@ module BlackStack
|
|
563
594
|
return max_distance - n
|
564
595
|
end
|
565
596
|
end # module Comparing
|
566
|
-
|
567
|
-
# -----------------------------------------------------------------------------------------
|
597
|
+
|
598
|
+
# -----------------------------------------------------------------------------------------
|
568
599
|
# Encoding: Make a string nice to be shown into an HTML string.
|
569
|
-
# -----------------------------------------------------------------------------------------
|
600
|
+
# -----------------------------------------------------------------------------------------
|
570
601
|
module Encoding
|
571
602
|
# Then it makes it compatible with UTF-8.
|
572
|
-
# More details here: https://bitbucket.org/leandro_sardi/blackstack/issues/961
|
603
|
+
# More details here: https://bitbucket.org/leandro_sardi/blackstack/issues/961
|
573
604
|
def self.encode_string(s)
|
574
605
|
s.encode("UTF-8")
|
575
606
|
end
|
576
|
-
|
607
|
+
|
577
608
|
# Escape the string to be shown into an HTML screen.
|
578
609
|
# Then it makes it compatible with UTF-8.
|
579
|
-
# More details here: https://bitbucket.org/leandro_sardi/blackstack/issues/961
|
610
|
+
# More details here: https://bitbucket.org/leandro_sardi/blackstack/issues/961
|
580
611
|
def self.encode_html(s)
|
581
612
|
encode_string(CGI.escapeHTML(s.to_s))
|
582
613
|
end
|
@@ -584,9 +615,9 @@ module BlackStack
|
|
584
615
|
# Generates a description string from an exception object.
|
585
616
|
# Eescapes the string to be shown into an HTML screen.
|
586
617
|
# Makes it compatible with UTF-8.
|
587
|
-
# More details here: https://bitbucket.org/leandro_sardi/blackstack/issues/961
|
618
|
+
# More details here: https://bitbucket.org/leandro_sardi/blackstack/issues/961
|
588
619
|
def self.encode_exception(e, include_backtrace=true)
|
589
|
-
ret = encode_html(e.to_s)
|
620
|
+
ret = encode_html(e.to_s)
|
590
621
|
if (include_backtrace == true)
|
591
622
|
e.backtrace.each { |s|
|
592
623
|
ret += "<br/>" + encode_html(s)
|
@@ -596,8 +627,8 @@ module BlackStack
|
|
596
627
|
end
|
597
628
|
|
598
629
|
# Returns a string with a description of a period of time, to be shown in the screen.
|
599
|
-
# period: it may be 'H', 'D', 'W', 'M', 'Y'
|
600
|
-
# units: it is a positive integer
|
630
|
+
# period: it may be 'H', 'D', 'W', 'M', 'Y'
|
631
|
+
# units: it is a positive integer
|
601
632
|
def self.encode_period(period, units)
|
602
633
|
s = "Last "
|
603
634
|
s += units.to_i.to_s + " " if units.to_i > 1
|
@@ -626,87 +657,87 @@ module BlackStack
|
|
626
657
|
|
627
658
|
end # module Encoding
|
628
659
|
|
629
|
-
# -----------------------------------------------------------------------------------------
|
660
|
+
# -----------------------------------------------------------------------------------------
|
630
661
|
# DateTime
|
631
|
-
# -----------------------------------------------------------------------------------------
|
632
|
-
module DateTime
|
662
|
+
# -----------------------------------------------------------------------------------------
|
663
|
+
module DateTime
|
633
664
|
# Check the string has the format yyyymmddhhmmss.
|
634
665
|
# => Return true if success. Otherwise, return false.
|
635
666
|
# => Year cannot be lower than 1900.
|
636
|
-
# => Year cannot be higher or equal than 2100.
|
667
|
+
# => Year cannot be higher or equal than 2100.
|
637
668
|
def self.datetime_api_check(s)
|
638
|
-
return false if (s.size!=14)
|
669
|
+
return false if (s.size!=14)
|
639
670
|
year = s[0..3]
|
640
671
|
month = s[4..5]
|
641
|
-
day = s[6..7]
|
642
|
-
hour = s[8..9]
|
643
|
-
minute = s[10..11]
|
644
|
-
second = s[12..13]
|
672
|
+
day = s[6..7]
|
673
|
+
hour = s[8..9]
|
674
|
+
minute = s[10..11]
|
675
|
+
second = s[12..13]
|
645
676
|
BlackStack::DateTime::Misc::datetime_values_check(year,month,day,hour,minute,second)
|
646
677
|
end # def datetime_api_check
|
647
678
|
|
648
679
|
# Check the string has the format yyyy-mm-dd hh:mm:ss.
|
649
680
|
# => Return true if success. Otherwise, return false.
|
650
681
|
# => Year cannot be lower than 1900.
|
651
|
-
# => Year cannot be higher or equal than 2100.
|
682
|
+
# => Year cannot be higher or equal than 2100.
|
652
683
|
def self.datetime_sql_check(s)
|
653
684
|
return false if (s.size!=19)
|
654
685
|
year = s[0..3]
|
655
686
|
month = s[5..6]
|
656
|
-
day = s[8..9]
|
657
|
-
hour = s[11..12]
|
658
|
-
minute = s[14..15]
|
659
|
-
second = s[17..18]
|
687
|
+
day = s[8..9]
|
688
|
+
hour = s[11..12]
|
689
|
+
minute = s[14..15]
|
690
|
+
second = s[17..18]
|
660
691
|
BlackStack::DateTime::Misc::datetime_values_check(year,month,day,hour,minute,second)
|
661
692
|
end # def datetime_sql_check
|
662
|
-
|
693
|
+
|
663
694
|
# Convierte un string con formato api-datatime (yyyymmddhhmmss) a un string con formato sql-datetime (yyyy-mm-dd hh:mm:ss).
|
664
695
|
def self.datetime_api_to_sql(s)
|
665
696
|
raise "Wrong Api DataTime Format." if (datetime_api_check(s)==false)
|
666
697
|
year = s[0..3]
|
667
698
|
month = s[4..5]
|
668
|
-
day = s[6..7]
|
669
|
-
hour = s[8..9]
|
670
|
-
minute = s[10..11]
|
671
|
-
second = s[12..13]
|
672
|
-
ret = "#{year}-#{month}-#{day} #{hour}:#{minute}:#{second}"
|
699
|
+
day = s[6..7]
|
700
|
+
hour = s[8..9]
|
701
|
+
minute = s[10..11]
|
702
|
+
second = s[12..13]
|
703
|
+
ret = "#{year}-#{month}-#{day} #{hour}:#{minute}:#{second}"
|
673
704
|
return ret
|
674
|
-
end # def datetime_api_to_sql
|
675
|
-
|
705
|
+
end # def datetime_api_to_sql
|
706
|
+
|
676
707
|
# Convierte un string con formato sql-datatime a un string con formato sql-datetime.
|
677
708
|
def self.datetime_sql_to_api(s)
|
678
709
|
raise "Wrong SQL DataTime Format." if (datetime_sql_check(s)==false)
|
679
710
|
year = s[0..3]
|
680
711
|
month = s[5..6]
|
681
|
-
day = s[8..9]
|
682
|
-
hour = s[11..12]
|
683
|
-
minute = s[14..15]
|
684
|
-
second = s[17..18]
|
685
|
-
ret = "#{year}#{month}#{day}#{hour}#{minute}#{second}"
|
712
|
+
day = s[8..9]
|
713
|
+
hour = s[11..12]
|
714
|
+
minute = s[14..15]
|
715
|
+
second = s[17..18]
|
716
|
+
ret = "#{year}#{month}#{day}#{hour}#{minute}#{second}"
|
686
717
|
return ret
|
687
718
|
end # def datetime_sql_to_api
|
688
719
|
end # module DateTime
|
689
720
|
|
690
721
|
|
691
|
-
# -----------------------------------------------------------------------------------------
|
722
|
+
# -----------------------------------------------------------------------------------------
|
692
723
|
# Spinning
|
693
|
-
# -----------------------------------------------------------------------------------------
|
724
|
+
# -----------------------------------------------------------------------------------------
|
694
725
|
module Spinning
|
695
726
|
# Esta funcion retorna una variacion al azar del texto que se pasa.
|
696
727
|
# Esta funcion se ocupa de dividir el texto en partes, para eviar el error "too big to product" que arroja la libraría.
|
697
728
|
def self.random_spinning_variation(text)
|
698
729
|
ret = text
|
699
|
-
|
730
|
+
|
700
731
|
text.scan(MATCH_CONTENT_SPINNING).each { |s|
|
701
732
|
a = ContentSpinning.new(s).spin
|
702
733
|
rep = a[rand(a.size)]
|
703
734
|
ret = ret.gsub(s, rep)
|
704
735
|
a = nil
|
705
736
|
}
|
706
|
-
|
737
|
+
|
707
738
|
return ret
|
708
739
|
end
|
709
|
-
|
740
|
+
|
710
741
|
# retorna true si la sintaxis del texto spineado es correcta
|
711
742
|
# caso contrario retorna false
|
712
743
|
# no soporta spinnings anidados. ejemplo: {my|our|{a car of mine}}
|
@@ -714,16 +745,16 @@ module BlackStack
|
|
714
745
|
# valido que exste
|
715
746
|
n = 0
|
716
747
|
s.split('').each { |c|
|
717
|
-
n+=1 if c=='{'
|
748
|
+
n+=1 if c=='{'
|
718
749
|
n-=1 if c=='}'
|
719
750
|
if n!=0 && n!=1
|
720
751
|
#raise "Closing spining char '}' with not previous opening spining char '{'." if n<0
|
721
752
|
#raise "Opening spining char '{' inside another spining block." if n>1
|
722
753
|
return false if n<0 # Closing spining char '}' with not previous opening spining char '{'.
|
723
754
|
return false if n>1 # Opening spining char '{' inside another spining block.
|
724
|
-
end
|
755
|
+
end
|
725
756
|
}
|
726
|
-
|
757
|
+
|
727
758
|
return false if n!=0
|
728
759
|
=begin
|
729
760
|
# obtengo cada uno de los spinnings
|
@@ -731,21 +762,21 @@ module BlackStack
|
|
731
762
|
a = x.split('|')
|
732
763
|
raise "No variations delimited by '|' inside spinning block." if a.size <= 1
|
733
764
|
}
|
734
|
-
=end
|
765
|
+
=end
|
735
766
|
true
|
736
767
|
end
|
737
|
-
|
768
|
+
|
738
769
|
# returns true if the text is spinned.
|
739
770
|
# otherwise, returns false.
|
740
771
|
def self.spintax?(s)
|
741
772
|
s.scan(MATCH_CONTENT_SPINNING).size > 0
|
742
773
|
end
|
743
774
|
end # module Spinning
|
744
|
-
|
745
|
-
|
746
|
-
# -----------------------------------------------------------------------------------------
|
775
|
+
|
776
|
+
|
777
|
+
# -----------------------------------------------------------------------------------------
|
747
778
|
# Miscelaneus
|
748
|
-
# -----------------------------------------------------------------------------------------
|
779
|
+
# -----------------------------------------------------------------------------------------
|
749
780
|
module Misc
|
750
781
|
# make a Ruby string safe for a filesystem.
|
751
782
|
# References:
|
@@ -756,7 +787,7 @@ module BlackStack
|
|
756
787
|
# NOTE: File.basename doesn't work right with Windows paths on Unix
|
757
788
|
# get only the filename, not the whole path
|
758
789
|
name.gsub!(/^.*(\\|\/)/, '')
|
759
|
-
|
790
|
+
|
760
791
|
# Strip out the non-ascii character
|
761
792
|
name.gsub!(/[^0-9A-Za-z.\-]/, '_')
|
762
793
|
end
|
@@ -765,9 +796,9 @@ module BlackStack
|
|
765
796
|
end # module Misc
|
766
797
|
|
767
798
|
|
768
|
-
# -----------------------------------------------------------------------------------------
|
799
|
+
# -----------------------------------------------------------------------------------------
|
769
800
|
# Email Appending Functions
|
770
|
-
# -----------------------------------------------------------------------------------------
|
801
|
+
# -----------------------------------------------------------------------------------------
|
771
802
|
module Appending
|
772
803
|
APPEND_PATTERN_FNAME_DOT_LNAME = 0
|
773
804
|
APPEND_PATTERN_FNAME = 1
|
@@ -775,7 +806,7 @@ module BlackStack
|
|
775
806
|
APPEND_PATTERN_F_LNAME = 3
|
776
807
|
APPEND_PATTERN_F_DOT_LNAME = 4
|
777
808
|
|
778
|
-
#
|
809
|
+
#
|
779
810
|
def self.name_pattern(pattern, fname, lname)
|
780
811
|
if (pattern==APPEND_PATTERN_FNAME_DOT_LNAME)
|
781
812
|
return "#{fname}.#{lname}"
|
@@ -792,13 +823,13 @@ module BlackStack
|
|
792
823
|
end
|
793
824
|
end
|
794
825
|
|
795
|
-
#
|
826
|
+
#
|
796
827
|
def self.get_email_variations(first_name, last_name, domain, is_a_big_company)
|
797
828
|
variations = Array.new
|
798
829
|
variations << first_name + "." + last_name + "@" + domain
|
799
830
|
variations << first_name[0] + last_name + "@" + domain
|
800
831
|
variations << first_name + "_" + last_name + "@" + domain
|
801
|
-
variations << first_name[0] + "." + last_name + "@" + domain
|
832
|
+
variations << first_name[0] + "." + last_name + "@" + domain
|
802
833
|
if (is_a_big_company == false)
|
803
834
|
variations << last_name + "@" + domain
|
804
835
|
variations << first_name + "@" + domain
|
@@ -821,20 +852,20 @@ module BlackStack
|
|
821
852
|
end
|
822
853
|
end # module Appending
|
823
854
|
end # module String
|
824
|
-
|
825
|
-
# -----------------------------------------------------------------------------------------
|
855
|
+
|
856
|
+
# -----------------------------------------------------------------------------------------
|
826
857
|
# Network
|
827
|
-
# -----------------------------------------------------------------------------------------
|
858
|
+
# -----------------------------------------------------------------------------------------
|
828
859
|
module Netting
|
829
860
|
CALL_METHOD_GET = 'get'
|
830
861
|
CALL_METHOD_POST = 'post'
|
831
862
|
DEFAULT_SSL_VERIFY_MODE = OpenSSL::SSL::VERIFY_NONE
|
832
863
|
SUCCESS = 'success'
|
833
|
-
|
834
|
-
@@lockfiles = []
|
864
|
+
|
865
|
+
@@lockfiles = []
|
835
866
|
|
836
867
|
@@max_api_call_channels = 0 # 0 means infinite
|
837
|
-
|
868
|
+
|
838
869
|
def self.max_api_call_channels()
|
839
870
|
@@max_api_call_channels
|
840
871
|
end
|
@@ -846,7 +877,7 @@ module BlackStack
|
|
846
877
|
def self.set(h)
|
847
878
|
@@max_api_call_channels = h[:max_api_call_channels]
|
848
879
|
@@lockfiles = []
|
849
|
-
|
880
|
+
|
850
881
|
i = 0
|
851
882
|
while i<@@max_api_call_channels
|
852
883
|
@@lockfiles << File.open("./apicall.channel_#{i.to_s}.lock", "w")
|
@@ -857,18 +888,18 @@ module BlackStack
|
|
857
888
|
|
858
889
|
class ApiCallException < StandardError
|
859
890
|
attr_accessor :description
|
860
|
-
|
891
|
+
|
861
892
|
def initialize(s)
|
862
893
|
self.description = s
|
863
894
|
end
|
864
|
-
|
895
|
+
|
865
896
|
def to_s
|
866
897
|
self.description
|
867
898
|
end
|
868
899
|
end
|
869
|
-
|
900
|
+
|
870
901
|
# New call_get
|
871
|
-
def self.call_get(url, params = {}, ssl_verify_mode=BlackStack::Netting::DEFAULT_SSL_VERIFY_MODE, support_redirections=true)
|
902
|
+
def self.call_get(url, params = {}, ssl_verify_mode=BlackStack::Netting::DEFAULT_SSL_VERIFY_MODE, support_redirections=true)
|
872
903
|
uri = URI(url)
|
873
904
|
uri.query = URI.encode_www_form(params)
|
874
905
|
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https', :verify_mode => ssl_verify_mode) do |http|
|
@@ -883,7 +914,7 @@ module BlackStack
|
|
883
914
|
end
|
884
915
|
end
|
885
916
|
end
|
886
|
-
|
917
|
+
|
887
918
|
# Call the API and return th result.
|
888
919
|
#
|
889
920
|
# Unlike `Net::HTTP::Post`, this method support complex json descriptors in order to submit complex data strucutres to access points.
|
@@ -891,25 +922,25 @@ module BlackStack
|
|
891
922
|
#
|
892
923
|
# url: valid internet address
|
893
924
|
# body: hash of body to attach in the call
|
894
|
-
# ssl_verify_mode: you can disabele SSL verification here.
|
925
|
+
# ssl_verify_mode: you can disabele SSL verification here.
|
895
926
|
# max_channels: this method use lockfiles to prevent an excesive number of API calls from each datacenter. There is not allowed more simultaneous calls than max_channels.
|
896
|
-
#
|
927
|
+
#
|
897
928
|
# TODO: parameter support_redirections has been deprecated.
|
898
929
|
#
|
899
930
|
def self.call_post(url, body = {}, ssl_verify_mode=BlackStack::Netting::DEFAULT_SSL_VERIFY_MODE, support_redirections=true)
|
900
|
-
# issue: https://github.com/leandrosardi/mysaas/issues/59
|
931
|
+
# issue: https://github.com/leandrosardi/mysaas/issues/59
|
901
932
|
#
|
902
933
|
# when ruby pushes hash of hashes (or hash of arrays), all values are converted into strings.
|
903
934
|
# and arrays are mapped to the last element only.
|
904
935
|
#
|
905
936
|
# the solution is to convert each element of the hash into a string using `.to_json` method.
|
906
|
-
#
|
907
|
-
# references:
|
937
|
+
#
|
938
|
+
# references:
|
908
939
|
# - https://stackoverflow.com/questions/1667630/how-do-i-convert-a-string-object-into-a-hash-object
|
909
940
|
# - https://stackoverflow.com/questions/67572866/how-to-build-complex-json-to-post-to-a-web-service-with-rails-5-2-and-faraday-ge
|
910
|
-
#
|
941
|
+
#
|
911
942
|
# iterate the keys of the hash
|
912
|
-
#
|
943
|
+
#
|
913
944
|
params = {} # not needed for post calls to access points
|
914
945
|
path = URI::parse(url).path
|
915
946
|
domain = url.gsub(/#{Regexp.escape(path)}/, '')
|
@@ -939,7 +970,7 @@ module BlackStack
|
|
939
970
|
if (parsed['status']==BlackStack::Netting::SUCCESS)
|
940
971
|
bSuccess = true
|
941
972
|
else
|
942
|
-
sError = "Status: #{parsed['status'].to_s}. Description: #{parsed['value'].to_s}."
|
973
|
+
sError = "Status: #{parsed['status'].to_s}. Description: #{parsed['value'].to_s}."
|
943
974
|
end
|
944
975
|
rescue Errno::ECONNREFUSED => e
|
945
976
|
sError = "Errno::ECONNREFUSED:" + e.to_console
|
@@ -947,7 +978,7 @@ module BlackStack
|
|
947
978
|
sError = "Exception:" + e2.to_console
|
948
979
|
end
|
949
980
|
end # while
|
950
|
-
|
981
|
+
|
951
982
|
if (bSuccess==false)
|
952
983
|
raise "#{sError}"
|
953
984
|
end
|
@@ -958,7 +989,7 @@ module BlackStack
|
|
958
989
|
# to: must be a valid path to a folder.
|
959
990
|
def self.download(url, to)
|
960
991
|
uri = URI(url)
|
961
|
-
domain = uri.host.start_with?('www.') ? uri.host[4..-1] : uri.host
|
992
|
+
domain = uri.host.start_with?('www.') ? uri.host[4..-1] : uri.host
|
962
993
|
path = uri.path
|
963
994
|
filename = path.split("/").last
|
964
995
|
Net::HTTP.start(domain) do |http|
|
@@ -970,7 +1001,7 @@ module BlackStack
|
|
970
1001
|
end
|
971
1002
|
|
972
1003
|
# Return the extension of the last path into an URL.
|
973
|
-
# Example: get_url_extension("http://connect.data.com/sitemap_index.xml?foo_param=foo_value") => ".xml"
|
1004
|
+
# Example: get_url_extension("http://connect.data.com/sitemap_index.xml?foo_param=foo_value") => ".xml"
|
974
1005
|
def self.get_url_extension(url)
|
975
1006
|
return File.extname(URI.parse(url).path.to_s)
|
976
1007
|
end
|
@@ -1011,12 +1042,12 @@ module BlackStack
|
|
1011
1042
|
httpc = HTTPClient.new
|
1012
1043
|
resp = httpc.get(url)
|
1013
1044
|
res = resp.header['Location']
|
1014
|
-
|
1045
|
+
|
1015
1046
|
if res.size == 0
|
1016
1047
|
uri = URI.parse(url)
|
1017
1048
|
uri_params = CGI.parse(uri.query)
|
1018
|
-
redirected_url = uri_params['url'][0]
|
1019
|
-
|
1049
|
+
redirected_url = uri_params['url'][0]
|
1050
|
+
|
1020
1051
|
if ( redirected_url != nil )
|
1021
1052
|
res = redirected_url
|
1022
1053
|
else
|
@@ -1040,25 +1071,25 @@ module BlackStack
|
|
1040
1071
|
# => p = CGI::parse(URI.parse(url).query)
|
1041
1072
|
# => La linea de abajo hace un gsbub que hace que esta url siga funcionando como busqueda de google, y ademas se posible parsearla.
|
1042
1073
|
url = url.gsub("webhp#q=", "webhp?q=")
|
1043
|
-
|
1074
|
+
|
1044
1075
|
return CGI::parse(URI.parse(url).query)
|
1045
1076
|
end
|
1046
|
-
|
1077
|
+
|
1047
1078
|
# Add a parameter to the url. It doesn't validate if the param already exists.
|
1048
1079
|
def self.add_param(url, param_name, param_value)
|
1049
1080
|
uri = URI(url)
|
1050
1081
|
params = URI.decode_www_form(uri.query || '')
|
1051
|
-
|
1082
|
+
|
1052
1083
|
if (params.size==0)
|
1053
1084
|
params << [param_name, param_value]
|
1054
1085
|
uri.query = URI.encode_www_form(params)
|
1055
1086
|
return uri.to_s
|
1056
1087
|
else
|
1057
1088
|
uri.query = URI.encode_www_form(params)
|
1058
|
-
return uri.to_s + "&" + param_name + "=" + param_value
|
1089
|
+
return uri.to_s + "&" + param_name + "=" + param_value
|
1059
1090
|
end
|
1060
1091
|
end
|
1061
|
-
|
1092
|
+
|
1062
1093
|
# Changes the value of a parameter in the url. It doesn't validate if the param already exists.
|
1063
1094
|
def self.change_param(url, param_name, param_value)
|
1064
1095
|
uri = URI(url)
|
@@ -1068,10 +1099,10 @@ module BlackStack
|
|
1068
1099
|
uri.query = URI.encode_www_form(params)
|
1069
1100
|
uri.to_s
|
1070
1101
|
end
|
1071
|
-
|
1102
|
+
|
1072
1103
|
# Change or add the value of a parameter in the url, depending if the parameter already exists or not.
|
1073
1104
|
def self.set_param(url, param_name, param_value)
|
1074
|
-
params = BlackStack::Netting::params(url)
|
1105
|
+
params = BlackStack::Netting::params(url)
|
1075
1106
|
if ( params.has_key?(param_name) == true )
|
1076
1107
|
newurl = BlackStack::Netting::change_param(url, param_name, param_value)
|
1077
1108
|
else
|
@@ -1079,24 +1110,24 @@ module BlackStack
|
|
1079
1110
|
end
|
1080
1111
|
return newurl
|
1081
1112
|
end
|
1082
|
-
|
1113
|
+
|
1083
1114
|
# get the domain from any url
|
1084
1115
|
def self.getDomainFromUrl(url)
|
1085
|
-
if (url !~ /^http:\/\//i && url !~ /^https:\/\//i)
|
1116
|
+
if (url !~ /^http:\/\//i && url !~ /^https:\/\//i)
|
1086
1117
|
url = "http://#{url}"
|
1087
1118
|
end
|
1088
|
-
|
1119
|
+
|
1089
1120
|
if (URI.parse(url).host == nil)
|
1090
|
-
raise "Cannot get domain for #{url}"
|
1121
|
+
raise "Cannot get domain for #{url}"
|
1091
1122
|
end
|
1092
|
-
|
1123
|
+
|
1093
1124
|
if (url.to_s.length>0)
|
1094
1125
|
return URI.parse(url).host.sub(/^www\./, '')
|
1095
1126
|
else
|
1096
1127
|
return nil
|
1097
1128
|
end
|
1098
1129
|
end
|
1099
|
-
|
1130
|
+
|
1100
1131
|
def self.getDomainFromEmail(email)
|
1101
1132
|
if email.email?
|
1102
1133
|
return email.split("@").last
|
@@ -1104,35 +1135,35 @@ module BlackStack
|
|
1104
1135
|
raise "getDomainFromEmail: Wrong email format."
|
1105
1136
|
end
|
1106
1137
|
end
|
1107
|
-
|
1138
|
+
|
1108
1139
|
def self.getWhoisDomains(domain, allow_heuristic_to_avoid_hosting_companies=false)
|
1109
1140
|
a = Array.new
|
1110
1141
|
c = Whois::Client.new
|
1111
1142
|
r = c.lookup(domain)
|
1112
|
-
|
1143
|
+
|
1113
1144
|
res = r.to_s.scan(/Registrant Email: (#{BlackStack::Strings::MATCH_EMAIL})/).first
|
1114
1145
|
if (res!=nil)
|
1115
1146
|
a << BlackStack::Netting::getDomainFromEmail(res[0].downcase)
|
1116
1147
|
end
|
1117
|
-
|
1148
|
+
|
1118
1149
|
res = r.to_s.scan(/Admin Email: (#{BlackStack::Strings::MATCH_EMAIL})/).first
|
1119
1150
|
if (res!=nil)
|
1120
1151
|
a << BlackStack::Netting::getDomainFromEmail(res[0].downcase)
|
1121
1152
|
end
|
1122
|
-
|
1153
|
+
|
1123
1154
|
res = r.to_s.scan(/Tech Email: (#{BlackStack::Strings::MATCH_EMAIL})/).first
|
1124
1155
|
if (res!=nil)
|
1125
1156
|
a << BlackStack::Netting::getDomainFromEmail(res[0].downcase)
|
1126
1157
|
end
|
1127
|
-
|
1158
|
+
|
1128
1159
|
# remover duplicados
|
1129
1160
|
a = a.uniq
|
1130
|
-
|
1131
|
-
#
|
1161
|
+
|
1162
|
+
#
|
1132
1163
|
if (allow_heuristic_to_avoid_hosting_companies==true)
|
1133
1164
|
# TODO: develop this feature
|
1134
1165
|
end
|
1135
|
-
|
1166
|
+
|
1136
1167
|
return a
|
1137
1168
|
end
|
1138
1169
|
|
@@ -1160,11 +1191,11 @@ module BlackStack
|
|
1160
1191
|
raise "Email #{value} is not valid" if !value.email?
|
1161
1192
|
# extract the domain from the email
|
1162
1193
|
domain = value.split('@').last
|
1163
|
-
#
|
1194
|
+
#
|
1164
1195
|
return domain=~/gmail\.com/ || domain=~/hotmail\.com/ || domain=~/outlook\.com/ || domain=~/yahoo\.com/ || domain=~/comcast\.com/ || domain=~/aol\.com/ || domain=~/msn\.com/ || domain=~/sbcglobal\.net/ ? true : false
|
1165
1196
|
end
|
1166
1197
|
|
1167
1198
|
|
1168
1199
|
end # module Netting
|
1169
|
-
|
1200
|
+
|
1170
1201
|
end # module BlackStack
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blackstack-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.29
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leandro Daniel Sardi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: content_spinning
|