truncate text after exceeding certain size - ruby-on-rails

Rails offers the truncate method when you want to truncate text that exceeds a certain character length. This is the example provided here:
truncate("Once upon a time in a world far far away")
# => "Once upon a time in a world..."
It truncates a given text after a given :length if text is longer than :length
But I have the following example:
str = "abcdefhijklmno"
After 12 characters I want it truncated, so the above text should look like this:
abcdefhijklm...
But I tried using the truncate method and cannot get the desired result:
> str = "abcdefhijklmno"
=> "abcdefhijklmno"
> str.truncate(15)
=> "abcdefhijklmno"
> str.truncate(14)
=> "abcdefhijklmno"
> str.truncate(13)
=> "abcdefhijk..."
> str.truncate(12)
=> "abcdefhij..."
I would think that truncate(12) would do it but it truncates after 9 characters. What am I doing wrong?

argument of truncate means size of output string (with "..."):
str.truncate(13)
=> "abcdefhijk..."
str.truncate(13).size
=> 13
You can change default omission(...) by empty space if you want:
str.truncate(13, omission: '')
=> "abcdefhijklmn"
More about Rails String#truncate here.

I don't believe str.truncate will do what you want out of the box. But since it's really just an extra if statement, it's easy to write:
def trunc(str, length)
addition = str.length > length ? '...' : ''
"#{str.truncate(length, omission: '')}#{addition}"
end
Or slightly simplified, and without a Rails dependency (as mentioned by Ilya in the comments):
def trunc(str, len)
"#{str.first(len)}#{'...' if str.size > len}"
end
And the test cases:
2.2.1 :005 > s = 'abcdefghijklmno'
=> "abcdefghijklmno"
2.2.1 :006 > trunc(s, 20)
=> "abcdefghijklmno"
2.2.1 :007 > trunc(s, 15)
=> "abcdefghijklmno"
2.2.1 :008 > trunc(s, 14)
=> "abcdefghijklmn..."
2.2.1 :009 > trunc(s, 13)
=> "abcdefghijklm..."
2.2.1 :010 > trunc(s, 0)
=> "..."
2.2.1 :011 > trunc(s, 1)
=> "a..."

Related

simple_format changes the text itself

In Rails 3.0, the helper method simple_format changes the parameter itself.
I expected that it only returns the wrapped text.
2.0.0-p648 :001 > Rails.version
=> "3.0.20"
2.0.0-p648 :002 > s = "Hello"
=> "Hello"
2.0.0-p648 :003 > helper.simple_format(s)
=> "<p>Hello</p>"
2.0.0-p648 :004 > s
=> "<p>Hello</p>"
I checked with Rails 4.2 and it doesn't change the text.
Can someone please explain it?
Sam
The difference between implementations of this method in Rails 4.2 and Rails 3.0 is that in Rails 3.0 the passed string is modified (mutated by gsub!) and in Rails 4.2 it's not (it just returns a new modified string):
Rails 4.2:
2.4.0 :006 > s = "hello"
=> "hello"
2.4.0 :007 > simple_format s
=> "<p>hello</p>"
2.4.0 :008 > s
=> "hello"
The source code of different implementations can be found in the documentation

Set precision for all big decimals in Ruby on Rails

Is there any way of defining a precision for all the BigDecimal numbers in Ruby or Ruby on Rails?
I setted the precision using the limit method, but that didn't seem to work out:
irb(main):003:0> BigDecimal.limit
=> 25
irb(main):004:0> num = '0.' + 0.to_s * 30 + '1'
=> "0.0000000000000000000000000000001"
irb(main):005:0> decimal = BigDecimal(num)
=> #<BigDecimal:9614780,'0.1E-30',9(45)>
irb(main):006:0> puts decimal.to_s
0.0000000000000000000000000000001
=> nil
irb(main):007:0> BigDecimal.limit
=> 25
Did I misunderstand the usage of the limit method? Is there any other that can achieve what I want?
The app uses Ruby 2.3.4 and Rails 4.2.8
Disclaimer: I already know how to truncate and how to only set the precision for individual values. I really need a way to set this "globally" (I mean, for all the new Big Decimals I instantiate or manipulate).
You can try
2.1.7 :056 > num = '0.' + 0.to_s * 30 + '1'
=> "0.0000000000000000000000000000001"
2.1.7 :057 > decimal = BigDecimal(num)
=> #<BigDecimal:7be2178,'0.1E-30',9(45)>
2.1.7 :058 > puts decimal.to_s
0.0000000000000000000000000000001
=> nil
2.1.7 :059 > '%.2f' % decimal
=> "0.00"

method match1.regex seems not working in ruby

I'm following a tutorial on regular expressions in ruby, but a method called regex that seems not working
Tutorial
re = /(\w*)\s(\w*),\s?([\w\s]*)/
match1 = str1.match re
match2 = str2.match re
match1.regex # => wsw,s[ws] (this is IRB's unique way of showing regular expressions; it will still work normally)
My console
The regex method the method throws an error
1.9.3-p547 :033 > re = /(\w*)\s(\w*),\s?([\w\s]*)/
=> /(\w*)\s(\w*),\s?([\w\s]*)/
1.9.3-p547 :034 > match1 = str1.match re
=> #<MatchData "Joe Schmo, Plumber" 1:"Joe" 2:"Schmo" 3:"Plumber">
1.9.3-p547 :035 > match2 = str2.match re
=> #<MatchData "Stephen Harper, Prime Minister" 1:"Stephen" 2:"Harper" 3:"Prime Minister">
1.9.3-p547 :036 > match1.regex
NoMethodError: undefined method `regex' for #<MatchData "Joe Schmo, Plumber" 1:"Joe" 2:"Schmo" 3:"Plumber">
from (irb):36
from /home/fernando/.rvm/rubies/ruby-1.9.3-p547/bin/irb:12:in `<main>'
1.9.3-p547 :037 >
I think it should be
match1.regexp
with a final 'p'

Simple regex check failing in a rails model

I have a check of the following type
validates :callback_handle, :format => { :with => /[_0-9a-zA-Z]+/ix }, :unless => "callback.nil?"
I do not want any non 0-9, a-z A-Z characters to pass. So i set callback_handle to
"!alksjda" (note ! at the begining).
This test does not fail. What am I doing wrong?
I tried a few things on irb: This is what I got:
1.9.2-p320 :001 > a = "!askldjlad"
=> "!askldjlad"
1.9.2-p320 :002 > a =~ /[_0-9a-zA-Z]+/ix
=> 1
1.9.2-p320 :003 > a = "askldjlad"
=> "askldjlad"
1.9.2-p320 :004 > a =~ /[_0-9a-zA-Z]+/ix
=> 0
I thought it would return false or nil on failure to find the match.
Can someone tell me what is wrong here in my understanding?
EDIT:
I figured out that =~ will return position of a match.
So the question becomes How do I not allow something that has any other character to not match?
Your regular expression is still able to match, because there is at least 1 character in your string that is alpha-numeric. If you want to make sure that the entire string matches then you should define the beginning and end of the match.
Old:
a =~ /[_0-9a-zA-Z]+/ix
This is saying "match at least one of these characters somewhere in a.
New:
a =~ /\A[_0-9a-zA-Z]+\z/ix
This is saying "start at the beginning of the string, then match at least 1 of only these characters, followed by the end of the string" in a.
Your regex just asks that your string contains 1 or more valid characters ... this should fix it :
validates :callback_handle, :format => { :with => /^[_0-9a-zA-Z]+$/ix }, :unless => "callback.nil?"

Vestal Versions changes_between not working for me

I was expecting it to be the union of all changes between 2 versions. (using vestal versions 1.0.2)
ruby-1.8.7-p174 > contact.version
=> 12
ruby-1.8.7-p174 > contact.latest_approved_version
=> 8
ruby-1.8.7-p174 > contact.changes
=> {}
ruby-1.8.7-p174 > contact.versions.last.changes
=> {"first_name"=>["I changed this one baby", "AdminF"]}
ruby-1.8.7-p174 > contact.changes_between(8,12)
=> {}
ruby-1.8.7-p174 > contact.changes_between(9,12)
=> {"deleted"=>[true, false]}
ruby-1.8.7-p174 > contact.changes_between(10,12)
=> {}
ruby-1.8.7-p174 > contact.changes_between(11,12)
=> {"first_name"=>["I changed this one baby", "AdminF"]}
As you can see no changes between 8 and 12, but changes between some of them.
The weird thing is I swear this was working yesterday!
Here is the method vestal is using, im not sure what the problem is:
def changes_between(from, to)
from_number, to_number = versions.number_at(from), versions.number_at(to)
return {} if from_number == to_number
chain = versions.between(from_number, to_number).reject(&:initial?)
return {} if chain.empty?
backward = from_number > to_number
backward ? chain.pop : chain.shift unless from_number == 1 || to_number == 1
chain.inject({}) do |changes, version|
changes.append_changes!(backward ? version.changes.reverse_changes : version.changes)
end
end
It is possible that there is nothing wrong with the example you provided. If for example there are no differences between version 8 and 12. Even though there has been changes in version 9, 10 and 11, the method changes_between will not show any changes if the model's attributes has been reverted to the same values.
I think the easiest way to verify that is to check:
>> contact.revert_to(8)
=> "8"
>> contact.inspect
=> "#<Contact ... >"
>> contact.revert_to(12)
=> "12"
>> contact.inspect
=> "#<Contact ... >"
And then compare the output. I can't think of any other reason for the result you described.
By the way, the call you made to "last_approved_version", is that something you manually added to your model or is it something built-in in vestal_versions? Because I also use the 1.0.2 vestal_version and I can't find any reference to that. So if it really is something built-in then we probably have different forks of vestal_version and that might be the cause...

Resources