Newbie to ruby and a little stuck with if statements - ruby-on-rails

this is most likely a dumb mistake however im a little stuck with setting some if conditions inside this
output = "<option value='#{user.id}' #{strDisabled} >if (user.company_name != "")#{user.company_name},&nbsp end if (user.name != "")#{user.name},&nbsp end if (user.email != "")#{user.email} end</option>".html_safe
It is just outputting the ruby code, i am most likely miles out but im on a project that uses ruby and i have not :)
Thanks in advance

Here's a quick refactor to make it easier to read the code.
output = "<option value='#{user.id}' #{strDisabled}>"
output << "#{user.company_name }, " if user.company_name.present?
output << "#{user.name}, " if user.name.present?
output << user.email if user.email.present?
output << "</option>"
output.html_safe
one more way to do it is
output = "<option value='#{user.id}' #{strDisabled}>"
output << ([user.company_name, user.name, user.email].reject(&:blank?) * ', ')
output << "</option>"
output.html_safe
the second line just selects which of the 3 strings is not blank and joins them with ,

Rails has a content_tag helper for generating HTML tags:
content_tag :option, value: user.id do
output = []
output << user.company_name unless user.company_name.blank?
output << user.email unless user.email.blank?
output.join(",&nbsp").html_safe
end
#=> "<option value=\"123\">user's company name, user's email</option>"

You need to put each piece that is Ruby code into #{} not just variables.
I don't think so that putting if inside a string is a good/proper thing to do.

I've attempted to reformat your code to approximate what it seems like you're trying to achieve:
output = "<option value='#{user.id}' #{strDisabled} >"
if (user.company_name != "")
output << "#{user.company_name}, "
end
if (user.name != "")
output << "#{user.name}, "
end
if (user.email != "")
output << "#{user.email}"
end
output << "</option>"
output = output.html_safe
You were missing numerous string terminators and semicolons for the non-breaking space HTML escape codes. But your major issue was that you were trying to do everything on the same line, and weren't ensuring your if/else constructs were
Outside the HTML string and
Properly formed.
Are you using an IDE, or are you attempting to do this in a text editor? If the latter, I highly recommend you attempt to at least find one which will do syntax highlighting, as this will make the issues you were suffering from a lot more visible.

you can write like this :
output = "<option value='#{user.id} #{strDisabled}'>
#{user.company_name if !user.company_name.blank?},
#{user.name if !user.name.blank?},
#{user.email if !user.email.blank?}
</option>".html_safe

Related

When trying to multiply with a method I get this output ஸ

Ruby Noob here - Trying to make a very simple appointment booking form that outputs a confirmation and the amount of time the appointment will take. I've gotten the concatenation working on the output but I keep getting this character (ஸ) where the amount of time should be. Below is my ruby document and the output.
print "Whats your name?"
name = gets.to_s
print "What is the address for your listing?"
appointment_address = gets
print "Square footage?"
sq_ft = gets.to_i
print "listing price"
listing_price = gets
# PHOTOGRAPHERS
def tps
tps = 3.to_i
end
def ryan(sq_ft,tps)
p sq_ft.to_i * tps.to_i
end
appointment_confirmation = 'Hey, '<< name.to_s.strip << '! Your appointment at ' << appointment_address.to_s.strip << ' will take us about ' << sq_ft*tps << ' to complete.'
p appointment_confirmation.strip
Output:
Hey, Alex! Your appointment at 102 Alex will take us about ஸ to complete.
When you add an integer to a string, the integer is converted into its corresponding character code. Cast your integer to a string:
' will take us about ' << (sq_ft*tps).to_s << ' to com ...'
You are appending an integer to a string. The integer is treated as character code.
The following prints our the letters A-Z because 65 is the code of "A" and 90 the code of "Z".
s = ""
(65..90).each do |ascii_code|
s << ascii_code
end
puts s
Either convert the amount to a string or (and this is my preferred way) use string interpolation:
"Hey, #{name} the duration is #{sq_ft*tps}"
Where #{} can be used to interpolate ruby values into a string. This only works with double quoted strings.

Truncate sentences in rails?

I've got a helper that I'm using to truncate strings in Rails, and it works great when I truncate sentences that end in periods. How should I modify the code to also truncate sentences when they end in question marks or exclamation points?
def smart_truncate(s, opts = {})
opts = {:words => 12}.merge(opts)
if opts[:sentences]
return s.split(/\.(\s|$)+/).reject{ |s| s.strip.empty? }[0, opts[:sentences]].map{|s| s.strip}.join('. ') + '...'
end
a = s.split(/\s/) # or /[ ]+/ to only split on spaces
n = opts[:words]
a[0...n].join(' ') + (a.size > n ? '... (more)' : '')
end
Thanks!!!
You have the truncate method
'Once upon a time in a world far far away'.truncate(27, separator: /\s/, ommission: "....")
which will return "Once upon a time in a..."
And if you need to truncate by number of words instead then use the newly introduced truncate_words (since Rails 4.2.2)
'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
which returns
"And they found that many... (continued)"

How to create email address generator with Ruby?

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 }"

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

ROR/Hpricot: parsing a site and searching/comparing strings with regex

I just started with Ruby On Rails, and want to create a simple web site crawler which:
Goes through all the Sherdog fighters' profiles.
Gets the Referees' names.
Compares names with the old ones (both during the site parsing and from the file).
Prints and saves all the unique names to the file.
An example URL is: http://www.sherdog.com/fighter/Fedor-Emelianenko-1500
I am searching for the tag entries like <span class="sub_line">Dan Miragliotta</span>, unfortunately, additionally to the proper Referee names I need, the same kind of class is used with:
The date.
"N/A" when the referee name is not known.
I need to discard all the results with a "N/A" string as well as any string which contains numbers. I managed to do the first part but couldn't figure out how to do the second. I tried searching, thinking and experimenting, but, after experimenting and rewriting, managed to break the whole program and don't know how to (properly) fix it:
require 'rubygems'
require 'hpricot'
require 'simplecrawler'
# Set up a new crawler
sc = SimpleCrawler::Crawler.new("http://www.sherdog.com/fighter/Fedor-Emelianenko-1500")
sc.maxcount = 1
sc.include_patterns = [".*/fighter/.*$", ".*/events/.*$", ".*/organizations/.*$", ".*/stats/fightfinder\?association/.*$"]
# The crawler yields a Document object for each visited page.
sc.crawl { |document|
# Parse page title with Hpricot and print it
hdoc = Hpricot(document.data)
(hdoc/"td/span[#class='sub_line']").each do |span|
if span.inner_html == 'N/A' || Regexp.new(".*/\d\.*$").match(span.inner_html)
# puts "Test"
else
puts span.inner_html
#File.open("File_name.txt", 'a') {|f| f.puts(hdoc.span.inner_html) }
end
end
}
I would also appreciate help with ideas on the rest of the program: How do I properly read the current names from the file, if the program is run multiple times, and how do I make the comparisons for the unique names?
Edit:
After some proposed improvements, here is what I got:
require 'rubygems'
require 'simplecrawler'
require 'nokogiri'
#require 'open-uri'
sc = SimpleCrawler::Crawler.new("http://www.sherdog.com/fighter/Fedor-Emelianenko-1500")
sc.maxcount = 1
sc.crawl { |document|
doc = Nokogiri::HTML(document.data)
names = doc.css('td:nth-child(4) .sub-line').map(&:content).uniq.reject { |c| c == 'N/A' }
puts names
}
Unfortunately, the code still doesn't work - it returns a blank.
If instead of doc = Nokogiri::HTML(document.data), I write doc = Nokogiri::HTML(open(document.data)), then it gives me the whole page, but, parsing still doesn't work.
hpricot isn't maintained anymore. How about using nokogiri instead?
names = document.css('td:nth-child(4) .sub-line').map(&:content).uniq.reject { |c| c == 'N/A' }
=> ["Yuji Shimada", "Herb Dean", "Dan Miragliotta", "John McCarthy"]
A breakdown of the different parts:
document.css('td:nth-child(4) .sub-line')
This returns an array of html elements with the class name sub-line that are in the forth table column.
.map(&:content)
For each element in the previous array, return element.content (the inner html). This is equivalent to map({ |element| element.content }).
.uniq
Remove duplicate values from the array.
.reject { |c| c == 'N/A' }
Remove elements whose value is "N/A"
You would use array math (-) to compare them:
get referees from the current page
current_referees = doc.search('td[4] .sub_line').map(&:inner_text).uniq - ['N/A']
read old referees from the file
old_referees = File.read('old_referees.txt').split("\n")
use Array#- to compare them
new_referees = current_referees - old_referees
write the new file
File.open('new_referees.txt','w'){|f| f << new_referees * "\n"}
This will return all the names, ignoring dates and "N/A":
puts doc.css('td span.sub_line').map(&:content).reject{ |s| s['/'] }.uniq
It results in:
Yuji Shimada
Herb Dean
Dan Miragliotta
John McCarthy
Adding these to a file and removing duplicates is left as an exercise for you, but I'd use some magical combination of File.readlines, sort and uniq followed by a bit of File.open to write the results.
Here is the final answer
require 'rubygems'
require 'simplecrawler'
require 'nokogiri'
require 'open-uri'
# Mute log messages
module SimpleCrawler
class Crawler
def log(message)
end
end
end
n = 0 # Counters how many pages/profiles processed
sc = SimpleCrawler::Crawler.new("http://www.sherdog.com/fighter/Fedor-Emelianenko-1500")
sc.maxcount = 150000
sc.include_patterns = [".*/fighter/.*$", ".*/events/.*$", ".*/organizations/.*$", ".*/stats/fightfinder\?association/.*$"]
old_referees = File.read('referees.txt').split("\n")
sc.crawl { |document|
doc = Nokogiri::HTML(document.data)
current_referees = doc.search('td[4] .sub_line').map(&:text).uniq - ['N/A']
new_referees = current_referees - old_referees
n +=1
# If new referees found, print statistics
if !new_referees.empty? then
puts n.to_s + ". " + new_referees.length.to_s + " new : " + new_referees.to_s + "\n"
end
new_referees = new_referees + old_referees
old_referees = new_referees.uniq
old_referees.reject!(&:empty?)
# Performance optimization. Saves only every 10th profile.
if n%10 == 0 then
File.open('referees.txt','w'){|f| f << old_referees * "\n" }
end
}
File.open('referees.txt','w'){|f| f << old_referees * "\n" }

Resources