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...
Related
How could we implement a method (eg. rails_version?) that be able to check the current Rails full version like this:
if rails_version?("6.0.3+") # return true if rails version >= 6.0.3.0
# new way
else
# old way
end
Use Rails.version to get the current version as a string.
However, that alone is not enough to compare it to other versions reliably. To do that, the best tool is Gem::Version.
Rails.version #=> "6.0.3.6"
Gem::Version.new(Rails.version) > Gem::Version.new('6.0.3.6') #=> false
Gem::Version.new(Rails.version) > Gem::Version.new('6.0.3') #=> true
Gem::Version.new(Rails.version) >= Gem::Version.new('6.0.3.6') #=> true
Though these would all work with strings as well, there are some cases that don't
Gem::Version.new(Rails.version) > Gem::Version.new('6.0.10') #=> false (correct)
Rails.version > '6.0.10' #=> true (incorrect)
Here is my solution:
def rails_version?(version)
Rails::VERSION::STRING >= version
end
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..."
This is above me today. Hope someone can explain this to me!
Assume you have a rails project with a en.yml file with the following contents:
en:
foo:
foo: foo
bar: bar
Assigning the results of I18n.t(:foo) to a local variable, you get a Hash:
2.0.0-p353 :001 > a = I18n.t(:foo)
=> {:foo=>"foo", :bar=>"bar"}
And now, changing a value for a key in this Hash causes changes in I18n.t('foo.foo'):
2.0.0-p353 :005 > a[:foo] = 'bar'
=> "bar"
2.0.0-p353 :006 > I18n.t(:foo)
=> {:foo=>"bar", :bar=>"bar"}
So, for the question to be clear - why changing a[:foo] from 'foo' to 'bar', causes the changes in I18n.t('foo.foo')?
Thanks in advance!
After
a = I18n.t(:foo)
The reference a does not hold a copy, but reference to the same Hash. Altering the Hash at a modifies the same Hash at I18n.t(:foo).
This is not special behavior of I18n.t, rather this is normal behavior for Ruby.
> a
=> {:foo=>:bar, :baz=>:qux}
> b = a
=> {:foo=>:bar, :baz=>:qux}
> b[:foo] = 1
=> 1
> b
=> {:foo=>1, :baz=>:qux}
> a
=> {:foo=>1, :baz=>:qux}
I have a model called Day that represents a day in a timesheet. I've noticed that whenever I call #day.save it's writing to the database, even though none of the object's properties have had their values changed.
#day = Day.last
=> #<Day lunch_minutes: 0, updated_at: "2012-08-19 12:09:40", work_hours: 5.5>
A day has its length in hours, and the length of its lunch break in minutes, stored. I've cropped out some properties that aren't relevant.
#day.lunch_minutes
=> 0
#day.lunch_minutes = 0
=> 0
#day.changes
=> {"lunch_minutes"=>[0, 0]}
#day.lunch_minutes_changed?
=> true
That should be false. Compare to a value that isn't zero:
#day.work_hours = 5.5
=> 5.5
#day.work_hours_changed?
=> false
So if I call save, this gets called. Ideally there would be no unnecessary database interaction here.
#day.save
(0.5ms) UPDATE "days" SET "lunch_minutes" = 0, "updated_at" = '2012-08-19 12:22:59.586860' WHERE "days"."id" = 48
I'm not sure if this is a Rails bug or if I'm doing some incorrectly somewhere. It looks like it could be an issue in "changes_from_zero_to_string?" - I think adding a && value != 0 to that method would fix it - but I want to know if anyone else has seen this/a fix for this before?
What version of rails are you using? I just had a go in my app (3.1.5/1.8.7) and it doesn't behave this way.. I just used a random integer property on one of my models to test with:
1.8.7 :006 > o = Order.first
=> <Order id:...>
1.8.7 :007 > o.order_items_count
=> 0
1.8.7 :008 > o.order_items_count = 0
=> 0
1.8.7 :009 > o.changes
=> {}
1.8.7 :010 > o.order_items_count = '0'
=> "0"
1.8.7 :011 > o.changes
=> {}
1.8.7 :012 > o.save
(0.1ms) BEGIN
(0.1ms) COMMIT
=> true
It appears to be a bug.
Interestingly, according to the code, if you do:
#day.lunch_minutes = '0'
It will probably think it has not changed!
Try that, and if indeed this change causes #day.lunch_minutes_changed? to be false, then make sure it is reported as an issue to https://github.com/rails/rails.
This is for Rails 3, almost always I think a content_for?(:foo) is followed by content_for(:foo) (in haml):
%title= content_for?(:title_for_page) ? "#{content_for(:title_for_page)} - Our great website" : 'Our great website'
So instead of doing 2 lookups, isn't it better to just do 1 lookup and use longer code:
- title_for_page = content_for(:title_for_page) # is "" when not previously set
%title= title_for_page.blank? ? 'Our great website' : "#{title_for_page} - Our great website"
? But if content_for? is implemented as a hash, then maybe it is super quick anyway, comparable to the blank? anyways?
A one-liner to solve the problem:
- title_for_page = (c = content_for(:title_for_page)).blank? ? 'Our great website' : "#{c} - Our great website"
Only way to find out is to test :)
ruby-1.9.2-p136 :001 > h = {:mike => "test"}
=> {:mike=>"test"}
ruby-1.9.2-p136 :004 > Benchmark.ms do
ruby-1.9.2-p136 :005 > h[:mike].present?
ruby-1.9.2-p136 :006?> end
=> 0.029087066650390625
ruby-1.9.2-p136 :007 > Benchmark.ms do
ruby-1.9.2-p136 :008 > h[:mike].blank?
ruby-1.9.2-p136 :009?> end
=> 0.011205673217773438
I am using present?, as per the source of content_for?
Interesting that blank? is faster than present?, isn't it? Time to explore.
Lets look at the source code for present?:
Woah, it turns out present? just calls blank? and negates it.