Specifying minimum length when using FFaker::Internet.user_name - ruby-on-rails

I have a spec that keeps failing because FFaker::Internet.user_name generates a word that is less than 5 characters.
How do I specify a minimum length in this stmt:
username { FFaker::Internet.user_name }

String#ljust — Returns a copy of self of a given length, right-padded with a given other string.
username { FFaker::Internet.user_name.ljust(5,"12345") }

From what I see you try to use FFaker in your factory. Why overcomplicate things for your specs, when you could define sequence
sequence(:username) do |n|
"username-#{n}"
end
But the question is valid and you may have some legitimate needs to use ffaker, and there are many ways to do it. You can just concatenate username twice, why not?
username { FFaker.username + FFaker.username }
Or keep looking for a username that length is of minimal lenght:
username do
do
name = FFaker.username
while name.length < 5
name
end
Or monkeypatch ffaker and implement it yourself https://github.com/ffaker/ffaker/blob/0578f7fd31c9b485e9c6fa15f25b3eca724790fe/lib/ffaker/internet.rb#L43 + https://github.com/ffaker/ffaker/blob/0578f7fd31c9b485e9c6fa15f25b3eca724790fe/lib/ffaker/name.rb#L75
for example
class FFaker
def long_username(min_length = 5)
fetch_sample(FIRST_NAMES.select {|name| name.length >= min_lenght })
end
end

There're many ways you can achieve this, but if I had to do it, I'd do something like
(FFaker::Internet.user_name + '___')[0...5]
#=> "Lily_"
There are three underscores because after the quick lookup to the name list, I found the minimum length of first name is two characters so two plus three will always be at least five characters.
I'm only taking five character substring so as to not always have trailing underscore, but that's just my personal preference, you can use username plus three underscores and your test case will do fine.

You can't, but you could do FFaker::Name.name.join, this generates first name and middle name

You can also build it manually with regex using FFaker::String like so:
# 5 characters username, with first character being a letter
username { FFaker::String.from_regexp(/[a-zA-Z]\w{4}/) }
# random 5-10 characters username with first character being a letter
regex = Regexp.new("[a-zA-Z]\\w{#{Random.rand(4..9)}}")
username { FFaker::String.from_regexp(regex) }

Another idea is to add some random numbers to the end. This worked for me, was easy to implement, and looks fairly natural (since humans tend to do it when creating usernames in the wild).
E.g. this adds "1234" to the end of usernames:
"steve" + "1234"
and with Faker:
Faker::Internet.unique.user_name + "1234"
Note: if you want a random string (instead of "1234", try some of these approaches. However, it may not be necessary if you're already using the Faker .unique method as in the above example.

Related

Generate Random String with Specific Restrictions

Tring to generate a random string but it needs to be formatted a specific way.
N = number
L = Capital Letter
must be NL-NN
needs hyphen as well
examples: 5K-22, 9L-19, 0R-66
every method I have tried has just generated a string but without the hyphen, I know it is probably something simple my brain just hurts thinking on it so I thought I'd see if one of yall could give me a hand.
Thanks
Try this:
function randomchar(a,b)
return string.char(math.random(string.byte(a),string.byte(b)))
end
a=randomchar('0','9')
b=randomchar('A','Z')
c=randomchar('0','9')
d=randomchar('0','9')
print(a..b..'-'..c..d)

Ruby: how to generate unique alphabetic string in ruby

Is there any in built method in ruby which will generate unique alphabetic string every time(it should not have numbers only alphabets)?
i have tried SecureRandom but it doesn't provide method which will return string containing only alphabets.
SecureRandom has a method choose which:
[...] generates a string that randomly draws from a source array of characters.
Unfortunately it's private, but you can call it via send:
SecureRandom.send(:choose, [*'a'..'z'], 8)
#=> "nupvjhjw"
You could also monkey-patch Random::Formatter:
module Random::Formatter
def alphabetic(n = 16)
choose([*'a'..'z'], n)
end
end
SecureRandom.alphabetic
#=> "qfwkgsnzfmyogyya"
Note that the result is totally random and therefore not necessarily unique.
UUID are designed to have extremely low chance of collision. Since UUID only uses 17 characters, it's easy to change the non-alphabetic characters into unused alphabetic slots.
SecureRandom.uuid.gsub(/[\d-]/) { |x| (x.ord + 58).chr }
Is there any in built method in ruby which will generate unique alphabetic string every time(it should not have numbers only alphabets)?
This is not possible. The only way that a string can be unique if you are generating an unlimited number of strings is if the string is infinitely long.
So, it is impossible to generate a string that will be unique every time.
def get_random_string(length=2)
source=("a".."z").to_a + ("A".."Z").to_a
key=""
length.times{ key += source[rand(source.size)].to_s }
key
end
How about something like this if you like some monkey-patching, i have set length 2 here , please feel free to change it as per your needs
get_random_string(7)
I used Time in miliseconds, than converted it into base 36 which gives me unique aplhanumeric value and since it depends on time so, it will be very unique.
Example: Time.now.to_f.to_s.gsub('.', '').ljust(17, '0').to_i.to_s(36) # => "4j26m4zm2ss"
Take a look at this for full answer: https://stackoverflow.com/a/72738840/7365329
Try this one
length = 50
Array.new(length) { [*"A".."Z", *"a".."z"].sample }.join
# => bDKvNSySuKomcaDiAlTeOzwLyqagvtjeUkDBKPnUpYEpZUnMGF

Remove from a string the characters where bytesize is greater than 2 with Ruby

I have a problem with mysql and certain characters. If a user enters "hello ●", I obtain this error:
Mysql2::Error: Incorrect string value: '\\xE2\\x97\\x8F he...' for column 'subject'
I would like to exclude all characters whose bytesize is greater than two, i.e., keep French characters like é, à, ç, and remove emojis or characters like ●.
Given string = "hèllö>●!", I would like to obtain "hèllö>!". In order to do so, I wrote this:
def bytesize(var)
var.each_char do |char|
puts char.bytesize
end
end
bytesize(string)
1
2
1
1
2
1
3
1
# => "hèllö>●!"
which is not what I expected. What is the best way to remove from all characters whose the bytesize is greater than two from a string?
I don't do that in the model because I can manage this with a gem, but my problem appears when a job wants to put the string in the logs of Amazon SES.
Elaborating on OP's efforts, not using regular expressions:
string = "hèllö>●!"
cleaned = string.each_char.with_object("") do |char, str|
str << char unless char.bytesize > 2
end
p cleaned
I suspect that you are getting that error message because you have the wrong column text encoding. If you are using Unicode in your system, and this day and age you should be, your column type should be utf8mb4. See this on how to change your column types.
Taking your comment into account the following will remove any characters outside the BMP
sentence.gsub(/[\u{10000}-\u{10FFFF}]/,'')

Regex password validation for non-sequence data

I'm attempting to validate that my passwords may not contain any sequential characters such as "123" or "abcd" etc. I'm new to regex and i'm trying to do it by having the following code in my User model:
validate :password_complexity
def password_complexity
if password.present? and not password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d). /)
errors.add :password, "must include at least one lowercase letter, one uppercase letter, and one digit"
end
end
This is probably somewhat outside the bounds of regex. I would recommend looking into zxcvbn, which has also been ported into a ruby gem of the same name and even exists as a devise plugin. This will provide you with significantly more robust verification. You may also consider just inspecting the code for these projects, for inspiration, if you are determined to learn how one might implement something like this from scratch.
I agree that this isn't a job for a regex: maybe you could do it but it would be very complicated. Here's how i would do it.
def contains_sequential_characters?(string)
sequential = false
string.split("").each_with_index do |char,i|
if (string[i+1] == string[i]+1) && char =~ /[a-y0-8]/i
sequential = true
end
end
sequential
end
At the heart of this solution is that calling an array index on a string (eg "foo"[2]) will give you the ascii code of the character at that position. We only care about ascii codes that are next to each other between a-z and 0-9, which is why i'm also matching the first character against a-y (not z) and 0-8 (not 9).
You can use the below regex, which will accept a minimum of 13 digits and will not accept either sequential digits or sequential characters:
(?=.*?[0-9])(?!.*((12)|(23)|(34)|(45)|(56)|(67)|(78)|(90)|(01)))(?=.*?[a-zäöüß])(?!.*((ab)|(bc)|(cd)|(ef)|(fg)|(gh)|(hi)|(ij)|(jk)|(kl)|(lm)|(mn)|(no)|(op)|(pq)|(qr)|(rs)|(st)|(tu)|(uv)|(vw)|(wx)|(xy)|(yz)))(?=.*?[A-ZÄÖÜ])(?!.*((AB)|(BC)|(CD)|(EF)|(FG)|(GH)|(HI)|(IJ)|(JK)|(KL)|(LM)|(MN)|(NO)|(OP)|(PQ)|(QR)|(RS)|(ST)|(TU)|(UV)|(VW)|(WX)|(xy)|(yz))).{13,}

How would i generate a random and unique string in Ruby?

In a Ruby on Rails app I am working on I allow users to upload files and want to give these files a short, random alphanumeric name. (Eg 'g7jf8' or '3bp76'). What is the best way to do this?
I sas thinking of generating a hash / encrypted string from the original filename and timestamp. Then query the database to double check it doesnt exist. If it does, generate another and repeat.
The issue i see with this approach is if there is high propability of duplicate strings, it could add quite a lote of datbase load.
I use this :)
def generate_token(column, length = 64)
begin
self[column] = SecureRandom.urlsafe_base64 length
end while Model.exists?(column => self[column])
end
Replace Model by your model name
SecureRandom.uuid
Will give you a globally unique String. http://en.m.wikipedia.org/wiki/Universally_unique_identifier
SecureRandom.hex 32
Will give a random String, but it's algorithm is not optimised for uniqueness. Of course the chance of collision with 32 digits, assuming true randomness, is basically theoretical. You could make 1 billion per second for 100 years and have only a 50% chance of a collision.
Use Ruby's SecureRandom.hex function with optional number of character you wanted to generate.
This will always produce new uniq 40 size alpha-numeric string, because it has Time stamp also.
loop do
random_token = Digest::SHA1.hexdigest([Time.now, rand(111..999)].join)
break random_token unless Model.exists?(column_name: random_token)
end
Note: Replace Model by your model_name and column_name by any existing column of your model.
You can assign a unique id by incrementing it each time a new file is added, and convert that id into an encrypted string using OpenSSL::Cipher with a constant key that you save somewhere.
If you end up generating a hex or numeric digest, you can keep the code shorter by representing the number as e.g. Base 62:
# This is a lightweight base62 encoding for Ruby integers.
B62CHARS = ('0'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a
def base62_string nbr
b62 = ''
while nbr > 0
b62 << B62CHARS[nbr % 62]
nbr /= 62
end
b62.reverse
end
If it is important for you to restrict the character set used (for instance not have uppercase chars in file names), then this code can easily be adapted, provided you can find a way of feeding in a suitable random number.
If your file names are supposed to be semi-secure, you need to arrange that there are many more possible names than actual names in storage.
It looks like you actually need a unique filenames, right? Why not forget about complex solutions and simply use Time#nsec?
t = Time.now #=> 2007-11-17 15:18:03 +0900
"%10.9f" % t.to_f #=> "1195280283.536151409"
You can use Time in miliseconds and than convert it into base 36 to reduce it length.
and since it depends on time to it will be very unique.
Example:
Time.now.to_f.to_s.gsub('.', '').ljust(17, '0').to_i.to_s(36) # => "4j26lna7g62"
Take a look at this answer:
https://stackoverflow.com/a/72738840/7365329

Resources