\n to <br> and multiple \n to <p> question - ruby-on-rails

I use bbruby gem to replace text in bbcode with html.
It replaces \r\n \n with <br>, and mutiple \r\n \ns with <p>.
# https://github.com/cpjolicoeur/bb-ruby/blob/master/lib/bb-ruby.rb
def simple_format(text)
start_tag = '<p>'
text = text.to_s.dup
text.gsub!(/\r\n?/, "\n") # \r\n and \r => \n
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline => paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline => br
text.insert 0, start_tag
text << "</p>"
end
It looks fine!
But when the text contains <table>, it becomes terrible! I want to avoid replacing \n when \n is in a table tag, and I try to replace \n in a table before bbruby replaces it, but it doesn't work.

text.gsub!(/\r\n?/, "\n")
Should be
text.gsub!(/\r?\n/, "\n")
You could get into look-ahead and look-behind in your regex to see if you're within a table tag (depending on the version of ruby you're using, this may not be available to you). You may want to instead just start your method by splitting your string on table tags, giving you an odd number of strings. Run the regexs above only on even indexed strings. Then join the strings together with table tags. This would allow you to properly terminate and start paragraph tags and let you ignore the line breaks in the tables.
def simple_format( text )
strings = text.split(/<\/?table>/)
strings.each_with_index do |i, string|
if i % 2 == 0 # even index == outside of table tags
string.gsub!(/\r?\n/, "\n") # \r\n and \r => \n
# ...
strings[i] = "<p>" + string + "</p>"
else # odd index == inside of table tags
strings[i] = "<table>" + string + "</table>"
end
end
strings.join
end
That said, you may want to run away from regex entirely for this as the solution I described assumes that there are no table tags within table tags or unterminated table tags.

well, somewhat like this?
def simple_format( text )
return text if ( text =~ /(<table.*>)/ ) # return text unchanged
start_tag = '<p>'
text = text.to_s.dup
text.gsub!(/\r\n?/, "\n") # \r\n and \r => \n
text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline => paragraph
text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline => br
text.insert 0, start_tag
text << "</p>"
end

bbcode and HTML do not mix. In fact bbcode was designed especially to NOT allow html tags. Since this is the design of bbcode, I don't see ways to hack around it. If you wish to continue using bbcode, you should consider it's not going to work with HTML input.
As far as I know, bbcode does not have syntax for html tables. If you absolutely need tables, consider switching to a different parser, or allow a full HTML editor like tinymce.

Related

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 remove "$" and "," from a price field in Rails

I am saving a price string to my database in a decimal-type column.
The price comes in like this "$ 123.99" which is fine because I wrote a bit of code to remove the "$ ".
However, I forgot that the price may include a comma, so "$ 1,234.99" breaks my code. How can I also remove the comma?
This is my code to remove dollar sign and space:
def price=(price_str)
write_attribute(:price, price_str.sub("$ ", ""))
# possible code to remove comma also?
end
You can get there two ways easily.
String's delete method is good for removing all occurrences of the target strings:
'$ 1.23'.delete('$ ,') # => "1.23"
'$ 123,456.00'.delete('$ ,') # => "123456.00"
Or, use String's tr method:
'$ 1.23'.tr('$, ', '') # => "1.23"
'$ 123,456.00'.tr('$ ,', '') # => "123456.00"
tr takes a string of characters to search for, and a string of characters used to replace them. Consider it a chain of gsub methods, one for each character.
BUT WAIT! THERE'S MORE! If the replacement string is empty, all characters in the search string will be removed.

How to remove mysterious whitespace from line breaks in Ruby on Rails?

In my Rails application I have a field address which is a varchar(255) in my SQLite database.
Yet whenever I save an address consisting of more than one line through a textarea form field, one mysterious whitespace character gets added to the right.
This becomes visible only when the address is right aligned (like e.g. on a letterhead).
Can anybody tell me why this is happening and how it can be prevented?
I am not doing anything special with those addresses in my model.
I already added this attribute writer to my model but it won't remove the whitespace unfortunately:
def address=(a)
write_attribute(:address, a.strip)
end
This is a screenshot:
As you can see only the last line is right aligned. All others contain one character of whitespace at the end.
Edit:
This would be the HTML output from my (Safari) console:
<p>
"John Doe "<br>
"123 Main Street "<br>
"Eggham "<br>
"United Kingdom"<br>
</p>
I don't even know why it's putting the quotes around each line... Maybe that's part of the solution?
I believe textarea is returning CR/LF for line separators and you're seeing one of these characters displayed between each line. See PHP displays \r\n characters when echoed in Textarea for some discussion of this. There are probably better questions out there as well.
You can strip out the whitespace at the start and end of each line. Here are two simple techniques to do that:
# Using simple ruby
def address=(a)
a = a.lines.map(&:strip).join("\n")
write_attribute(:address, a)
end
# Using a regular expression
def address=(a)
a = a.gsub(/^[ \t]+|[ \t]+$/, "")
write_attribute(:address, a)
end
I solved a very similar kind of problem when I ran into something like this,
(I used squish)
think#think:~/CrawlFish$ irb
1.9.3-p385 :001 > "Im calling squish on a string, in irb".squish
NoMethodError: undefined method `squish' for "Im calling squish on a string, in irb":String
from (irb):1
from /home/think/.rvm/rubies/ruby-1.9.3-p385/bin/irb:16:in `<main>'
That proves, there is no squish in irb(ruby)
But rails has squish and squish!(you should know the difference that bang(!) makes)
think#think:~/CrawlFish$ rails console
Loading development environment (Rails 3.2.12)
1.9.3-p385 :001 > str = "Here i am\n \t \n \n, its a new world \t \t \n, its a \n \t new plan\n \r \r,do you like \r \t it?\r"
=> "Here i am\n \t \n \n, its a new world \t \t \n, its a \n \t new plan\n \r \r,do you like \r \t it?\r"
1.9.3-p385 :002 > out = str.squish
=> "Here i am , its a new world , its a new plan ,do you like it?"
1.9.3-p385 :003 > puts out
Here i am , its a new world , its a new plan ,do you like it?
=> nil
1.9.3-p385 :004 >
Take a loot at strip! method
>> #title = "abc"
=> "abc"
>> #title.strip!
=> nil
>> #title
=> "abc"
>> #title = " abc "
=> " abc "
>> #title.strip!
=> "abc"
>> #title
=> "abc"
source
What's the screen shot look like when you do:
def address=(a)
write_attribute(:address, a.strip.unpack("C*").join('-') )
end
Update based on comment answers. Another way to get rid of the \r's at the end of each line:
def address=(a)
a = a.strip.split(/\r\n/).join("\n")
write_attribute(:address, a)
end

Ruby on Rails Strings: Find next character that is not a letter or number?

I'm making a mentions feature right now so when a user types in #, the next part they type for a username is clickable until a space appears. This is assuming they type in a username correctly, which only have letters and numbers. I need it to work though so if they type "Hi #jon!" that it finds the exclamation point (or any symbol that is not a letter or number) as not part of the username and excludes it instead of just looking for the following space.
This is what I have:
while #comment.content.include? "#" do
at = #comment.content.index('#')
space = #comment.content.index(' ', at)
length = space - at
usernotag = #comment.content[at + 1,length - 1]
userwtag = #comment.content[at,length]
#user = User.where(:username => usernotag.downcase).first
#mentioned_users.push(#user)
replacewith = "<a href='/" + usernotag + "'>*%^$&*)()_+!$" + usernotag + "</a>"
#comment.content = #comment.content.gsub(userwtag, replacewith)
end
#comment.content = #comment.content.gsub("*%^$&*)()_+!$", "#")
Any idea what I should do?
You should use a regular expression to parse/extract the user references:
# Transform comment content inline.
#comment.content.gsub!(/#[\w\d]+/) {|user_ref| link_if_user_reference(user_ref) }
#comment.save!
# Helper to generate a link to the user, if user exists
def link_if_user_reference(user_ref)
username = user_ref[1..-1]
return user_ref unless User.find_by_name(username)
link_to user_ref, "/users/#{user_name}"
# => produces link #username => /user/username
end
This assumes your usernames are restricted to alphanumeric characters as you said (letters or numbers). If you have other characters, you can add them to the set included in your regular expression.

ruby on rails, replace last character if it is a * sign

I have a string and I need to check whether the last character of that string is *, and if it is, I need to remove it.
if stringvariable.include? "*"
newstring = stringvariable.gsub(/[*]/, '')
end
The above does not search if the '*' symbol is the LAST character of the string.
How do i check if the last character is '*'?
Thanks for any suggestion
Use the $ anchor to only match the end of line:
"sample*".gsub(/\*$/, '')
If there's the possibility of there being more than one * on the end of the string (and you want to replace them all) use:
"sample**".gsub(/\*+$/, '')
You can also use chomp (see it on API Dock), which removes the trailing record separator character(s) by default, but can also take an argument, and then it will remove the end of the string only if it matches the specified character(s).
"hello".chomp #=> "hello"
"hello\n".chomp #=> "hello"
"hello\r\n".chomp #=> "hello"
"hello\n\r".chomp #=> "hello\n"
"hello\r".chomp #=> "hello"
"hello \n there".chomp #=> "hello \n there"
"hello".chomp("llo") #=> "he"
"hello*".chomp("*") #=> "hello"
String has an end_with? method
stringvariable.chop! if stringvariable.end_with? '*'
You can do the following which will remove the offending character, if present. Otherwise it will do nothing:
your_string.sub(/\*$/, '')
If you want to remove more than one occurrence of the character, you can do:
your_string.sub(/\*+$/, '')
Of course, if you want to modify the string in-place, use sub! instead of sub
Cheers,
Aaron
You can either use a regex or just splice the string:
if string_variable[-1] == '*'
new_string = string_variable.gsub(/[\*]/, '') # note the escaped *
end
That only works in Ruby 1.9.x...
Otherwise you'll need to use a regex:
if string_variable =~ /\*$/
new_string = string_variable.gsub(/[\*]/, '') # note the escaped *
end
But you don't even need the if:
new_string = string_variable.gsub(/\*$/, '')

Resources