How to create email address generator with Ruby? - ruby-on-rails

def emails
i = 0
number = rand(252...4350)
males = ["tom", "jack", "adam"]
females = ["elizabeth", "rose", "juliet"]
surnameMales = ["oak", "yew", "timber"]
surnameFemales = ["rosewelth", "gates", "jobs"]
providers = ["gmail.com", "hotmail.com", "yahoo.com"]
while i <= 100 do
#addresses <<
end
end
What I want to do is pick a random number, name, surname and provider and put them all together + attach a random number at the end of the surname so that it looks, for example, like this: rose.gates643#gmail.com, but kind of got stuck at the point where I have to put the whole thing together. As you can probably tell, I want to create a 100 random emails for testing purposes.
Can you please point me out how I could do what I have intended to do with Ruby?

Is your goal to do this as an exercise or is this just something you're trying to get done? If it's the latter, install the faker gem and just use it:
Faker::Internet.email #=> "kirsten.greenholt#corkeryfisher.info"
For 100:
100.times.map { Faker::Internet.email }

Use a combination of string interpolation and Array#sample.
"#{females.sample}.#{surnameFemales.sample}#{rand(252...4350)}##{providers.sample}"
>> "rose.rosewelth3266#gmail.com"

There's a couple ways to join strings in Ruby. Inline placeholders seems to make the most sense here:
#addressess << "#{ males.sample }.#{ surnameMales.sample }#{ number }##{ providers.sample }"
In a double-quoted string, #{ expr } is evaluated and subbed inline. "#{ 1 + 1 }" outputs "2", for example.
Also a couple ways you can distinguish male or female — for example, checking if a random number is even or odd:
name = if rand(1..100) % 2 == 0 ? "#{ males.sample }.#{ surnameMales.sample }" : "#{ females.sample }.#{ surnameFemales.sample }"
#addresses << "#{ name }#{ number }##{ providers.sample }"

Related

Better way to query instead of looping through all data

I have the following method that takes a 10 digit number and searches for matches:
def self.get_agents_by_phone_number(phone_number)
all_agents = Agent.all
agents = Array.new
all_agents.each do |a|
if "1" + a.phone_number.scan(/\d/).join('').last(10) == phone_number
agents << a
end
end
return agents
end
The catch is that the phone_number field in the DB have not been scrubbed and may be in a few different formats. Here is a sample:
2.5.3 :089 > Agent.all.pluck :phone_number
(0.5ms) SELECT "agents"."phone_number" FROM "agents"
=> ["1-214-496-5089", "193.539.7577", "557-095-1452", "(734) 535-5668", "(279) 691-4148", "(474) 777-3615", "137.158.9465", "(280) 680-8618", "296.094.7455", "1-500-079-7285", "1-246-171-1355", "1-444-626-9429", "(614) 603-6276", "594.170.4795", "1-535-859-1377", "676.706.4384", "256-312-4417", "1-592-904-2339", "174.912.8838", "677.137.7019", "319-013-7526", "(200) 790-1698", "576-106-0746", "(214) 042-9715", "(312) 188-5862", "1-823-392-9020", "663.331.4191", "237-101-0271", "1-836-465-1204", "394-499-0004", "713-068-1726", "1-223-484-7856"]
The method I shared above works but feels pretty inefficient. Any better ways of doing this without touching the data in the DB?
What about:
#sample if incoming phone_num = "333.111.4444"
#You need to adjust it to possible ways and push to array.
options = Array.new
options << "1.333.111.4444"
options << "333.111.4444"
options << "1-333-111-4444"
options << "333-111-4444"
# and so on ...
# then query in where
Agent.where("phone_number IN (?)", options)
It's way better than looping, only you need to adjust the phone number to get better performance. I do not know, how big is your data but since you are fetching .all agents it could be huge :)
I guess you can use the LIKE operator with multiple '%' wildcards between digit.
phone_number = '1234567890'
# split the number in groups
last4 = phone_number[-4..-1] # 7890
middle3 = phone_number[-7..-5] # 456
first3or4 = phone_number[0..-8] # 123
# build the pattern
pattern = "#{first3or4}%#{middle3}%#{last4}" # 123%456%7890
# use it for LIKE query
Agent.where('phone_number LIKE ?', pattern)
It won't be a fast query with all those wildcards.
https://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html#operator_like

Rails apply a condition within a sort_by looking for value

I'm working on sorting a list of employees by their title with the following:
TAGS = {
'Region Manager' => 1,
'Region Sales Manager' => 2,
'General Manager' => 3,
'Residential Sales Manager' => 4,
'Commercial Sales Manager' => 5,
'Other' => 6
}.freeze
def sorting_by_title(employees)
employees.sort_by do |x|
TAGS[x[:title]]
end
end
Works fine but...I also need to do an additional sort if an employee last name is Smith and needs to go before other individuals.
So I've tried doing something like:
return TAGS[x[:title]] unless x.last_name == "Smith"
Not working. It's erroring out on the show page with undefined method `each' for 2:Integer.
So my thought was I would build out another method to look for the last name.
def nepotism(employees)
employees.group_by do |emp|
case emp.last_name
when /Smith/ then :moocher
end
end
end
So I tried then referencing it like:
return TAGS[x[:title]] unless x.nepotism
return TAGS[x[:title]] unless x.moocher
Neither of those work. Nepotism ends up with undefined method `nepotism' for # and Moocher ends up with the same. Then I realized a simple query would work a bit better:
def nepotism
#nepotism = Employee.where(last_name: "Smith")
end
Is there a better way to sort_by a last_name if it matches Smith and THEN by the tags?
Here's a nice trick: in ruby you can compare arrays. And, consequently, use them as value in sort_by. They are compared element by element. If ary1[0] < ary2[0], then ary1 will be less than ary2, no matter the rest of the elements.
employees.sort_by do |x|
[
x.last_name == "Smith" ? 0 : 1, # all zeroes come before all ones
TAGS[x[:title]] # your main ordering parameter
]
end
This would work very well, if there were many Smiths and you needed to sort them by titles between themselves. If there's only one Smith, then #Björn's solution is simpler.
Combine them like this
employees.sort_by do |x|
x.last_name == "Smith" ? 0 : TAGS[x[:title]]
end
You can do it in the database as well (assuming Postgresql here)
def nepotism
tagstring = "array_position(ARRAY"+TAGS.keys.to_s.gsub(/\"/,"'")+", last_name)"
#nepotism = Employee.order("last_name = 'Smith' desc, " + tagstring)
end

Sending array of values to a sql query in ruby?

I'm struggling on what seems to be a ruby semantics issue. I'm writing a method that takes a variable number of params from a form and creates a Postgresql query.
def self.search(params)
counter = 0
query = ""
params.each do |key,value|
if key =~ /^field[0-9]+$/
query << "name LIKE ? OR "
counter += 1
end
end
query = query[0..-4] #remove extra OR and spacing from last
params_list = []
(1..counter).each do |i|
field = ""
field << '"%#{params[:field'
field << i.to_s
field << ']}%", '
params_list << field
end
last_item = params_list[-1]
last_item = last_item[0..-3] #remove trailing comma and spacing
params_list[-1] = last_item
if params
joins(:ingredients).where(query, params_list)
else
all
end
end
Even though params_list is an array of values that match in number to the "name LIKE ?" parts in query, I'm getting an error: wrong number of bind variables (1 for 2) in: name LIKE ? OR name LIKE ? I tried with params_list as a string and that didn't work any better either.
I'm pretty new to ruby.
I had this working for 2 params with the following code, but want to allow the user to submit up to 5 ( :field1, :field2, :field3 ...)
def self.search(params)
if params
joins(:ingredients).where(['name LIKE ? OR name LIKE ?',
"%#{params[:field1]}%", "%#{params[:field2]}%"]).group(:id)
else
all
end
end
Could someone shed some light on how I should really be programming this?
PostgreSQL supports standard SQL arrays and the standard any op (...) syntax:
9.23.3. ANY/SOME (array)
expression operator ANY (array expression)
expression operator SOME (array expression)
The right-hand side is a parenthesized expression, which must yield an array value. The left-hand expression is evaluated and compared to each element of the array using the given operator, which must yield a Boolean result. The result of ANY is "true" if any true result is obtained. The result is "false" if no true result is found (including the case where the array has zero elements).
That means that you can build SQL like this:
where name ilike any (array['%Richard%', '%Feynman%'])
That's nice and succinct so how do we get Rails to build this? That's actually pretty easy:
Model.where('name ilike any (array[?])', names.map { |s| "%#{s}%" })
No manual quoting needed, ActiveRecord will convert the array to a properly quoted/escaped list when it fills the ? placeholder in.
Now you just have to build the names array. Something simple like this should do:
fields = params.keys.select { |k| k.to_s =~ /\Afield\d+\z/ }
names = params.values_at(*fields).select(&:present)
You could also convert single 'a b' inputs into 'a', 'b' by tossing a split and flatten into the mix:
names = params.values_at(*fields)
.select(&:present)
.map(&:split)
.flatten
You can achieve this easily:
def self.search(string)
terms = string.split(' ') # split the string on each space
conditions = terms.map{ |term| "name ILIKE #{sanitize("'%#{term}%'")}" }.join(' OR ')
return self.where(conditions)
end
This should be flexible: whatever the number of terms in your string, it should returns object matching at least 1 of the terms.
Explanation:
The condition is using "ILIKE", not "LIKE":
"ILIKE" is case-insensitive
"LIKE" is case-sensitive.
The purpose of the sanitize("'%#{term}%'") part is the following:
sanitize() will prevent from SQL injections, such as putting '; DROP TABLE users;' as the input to search.
Usage:
User.search('Michael Mich Mickey')
# can return
<User: Michael>
<User: Juan-Michael>
<User: Jean michel>
<User: MickeyMouse>

Ruby regex puncuation

I am having trouble writing this so that it will take a sentence as an argument and perform the translation on each word without affecting the punctuation.
I'd also like to continue using the partition method.
It would be nice if I could have it keep a quote together as well, such as:
"I said this", I said.
would be:
"I aidsay histay", I said.
def convert_sentence_pig_latin(sentence)
p split_sentence = sentence.split(/\W/)
pig_latin_sentence = []
split_sentence.each do |word|
if word.match(/^[^aeiou]+/x)
pig_latin_sentence << word.partition(/^[^aeiou]+/x)[2] + word.partition(/^[^aeiou]+/x)[1] + "ay"
else
pig_latin_sentence << word
end
end
rejoined_pig_sentence = pig_latin_sentence.join(" ").downcase + "."
p rejoined_pig_sentence.capitalize
end
convert_sentence_pig_latin("Mary had a little lamb.")
Your main problem is that [^aeiou] matches every character outside that range, including spaces, commas, quotation marks, etc.
If I were you, I'd use a positive match for consonants, ie. [b-df-hj-np-tv-z] I would also put that regex in a variable, so you're not having to repeat it three times.
Also, in case you're interested, there's a way to make your convert_sentence_pig_latin method a single gsub and it will do the whole sentence in one pass.
Update
...because you asked...
sentence.gsub( /\b([b-df-hj-np-tv-z])(\w+)/i ) { "#{$2}#{$1}ay" }
# iterate over and replace regexp matches using gsub
def convert_sentence_pig_latin2(sentence)
r = /^[^aeiou]+/i
sentence.gsub(/"([^"]*)"/m) {|x| x.gsub(/\w+/) {|y| y =~ r ? "#{y.partition(r)[2]}#{y.partition(r)[1]}ay" : y}}
end
puts convert_sentence_pig_latin2('"I said this", I said.')
# define instance method: String#to_pl
class String
R = Regexp.new '^[^aeiou]+', true # => /^[^aeiou]+/i
def to_pl
self.gsub(/"([^"]*)"/m) {|x| x.gsub(/\w+/) {|y| y =~ R ? "#{y.partition(R)[2]}#{y.partition(R)[1]}ay" : y}}
end
end
puts '"I said this", I said.'.to_pl
sources:
http://www.ruby-doc.org/core-2.1.0/Regexp.html
http://ruby-doc.org/core-2.0/String.html#method-i-gsub

How to pluralize "There is/are N object/objects"?

Pluralizing a single word is simple:
pluralize(#total_users, "user")
But what if I want to print "There is/are N user/users":
There are 0 users
There is 1 user
There are 2 users
, i.e., how to pluralize a sentence?
You can add a custom inflection for it. By default, Rails will add an inflections.rb to config/initializers. There you can add:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular "is", "are"
end
You will then be able to use pluralize(#total_users, "is") to return is/are using the same rules as user/users.
EDIT: You clarified the question on how to pluralize a sentence. This is much more difficult to do generically, but if you want to do it, you'll have to dive into NLP.
As the comment suggests, you could do something with I18n if you just want to do it with a few sentences, you could build something like this:
def pluralize_sentence(count, i18n_id, plural_i18n_id = nil)
if count == 1
I18n.t(i18n_id, :count => count)
else
I18n.t(plural_i18n_id || (i18n_id + "_plural"), :count => count)
end
end
pluralize_sentence(#total_users, "user_count")
And in config/locales/en.yml:
en:
user_count: "There is %{count} user."
user_count_plural: "There are %{count} users."
This is probably best covered by the Rails i18n pluralization features. Adapted from http://guides.rubyonrails.org/i18n.html#pluralization
I18n.backend.store_translations :en, :user_msg => {
:one => 'There is 1 user',
:other => 'There are %{count} users'
}
I18n.translate :user_msg, :count => 2
# => 'There are 2 users'
I think the first part of Martin Gordon's answer is pretty good.
Alternatively, it's kind of messy but you can always just write the logic yourself:
"There #{#users.size == 1 ? 'is' : 'are'} #{#users.size} user#{'s' unless #users.size == 1}."
UPDATE to code: I no longer use the inflections route as stated in #Martin Gordon's answer. For some reason it would cause other non-related functions to error. I did extensive tests to confirm, though could not track down a reason why. So, below is now what I use and it works.
There are many ways to do this. This is how I did it using Rails 6.0.3.4 and Ruby 2.7.1.
I wanted to pluralize this sentence:
Singular: There is 1 private group
Plural: There are 2 private groups
What I did is I went to application_helper.rb and added this code:
def pluralize_private_statement(list, word)
num_in_list = list.count
is_or_are = num_in_list == 1 ? 'is' : 'are'
return "There " + is_or_are + " " + num_in_list.to_s + " private " + word.pluralize(num_in_list)
end
Now, all I have to use in my view is:
<%= pluralize_private_statement(private_groups, "group") %>
# private_groups = 2
# -> There are 2 private groups
What the code in application_helper.rb does is first create a variable for the number of items in the list passed and store it in num_in_list. Then it creates a second varible checking if the num_in_list is equal to 1 and if so returns 'is' otherwise it returns 'are'. Then, it returns the sentence that is constructed with the information obtained.
The first part of the sentence is a simple string, then the is_or_are variable which holds either 'is' or 'are' as explained above. Then it adds a space with the number of list items, converted from an integer to a string, followed by the 'private' word. Then it adds the pluralization of the word passed to the initial function; but, only returns the singular/plural word without a number attached as pluralize(#total_users, "is") would do.
Here is how you could use it for your specific question.
First, add this to your application_helper.rb file:
def pluralize_sentence(list, word)
num_in_list = list.count
is_or_are = num_in_list == 1 ? 'is' : 'are'
return "There " + is_or_are + " " + num_in_list.to_s + " " + word.pluralize(num_in_list)
end
Lastly, you can use this code wherever you wish to have the pluralized sentence:
<%= pluralize_sentence(#total_users, "user") %>
Happy Coding!

Resources