Get full old record before save - ruby-on-rails

I need to backup a record before save. Not only the changes that were made (I know to do it with Person.name_was) but ALL, COMPLETE record.
I.E. something like Person_was. But there is no such method.
How can I achieve that?

I ended up with this code:
person_history = PersonHistory.new
person.attributes.except("id", "password").each{ |key, value| eval("person_history.#{key} = person.#{key}_was")}
person_history.save

Related

Rails 5 bulk-update

What I need to do is something like this:
users.update_all(number: some_specific_number_different_for_each_user)
Is there a better way of doing it other than iterating through users and updating each one separately? Even if I enclose it in transaction, it seems very inefficient...
Or is there a way to just save all records once I adjust the value, something like:
users.each_with_index { |u,i| u.number = i }
users.save_all
Any ideas?
Not unless the value to be updated can be set by the SQL function.
See(EDIT2)
update_all with a method

first_or_create: determining which is called

I call first_or_create like so:
collection = Collection.first_or_create(:title => title)
Is there a way to determine if the result is an existing entry or a freshly created one? So far the best solution I've come up with is to use first_or_initialize:
collection = Collection.first_or_initialize(:title => title)
if collection.id.nil?
<process>
collection.save
end
But this feels a bit hacky. Is there a way to get this information directly from first_or_create?
first_or_create takes a block that'll only be executed if a new record is created, you can set some flag inside that block to indicate it's a new record, for example
MyObject.where(attr: "value").first_or_create do |obj|
#my_object_created = true
end
As far as I know you can't know. Two options are to check the created_at time (unreliable), or instead use first_or_initialize, then check to see if new_record? is true, and if so, do your other operations and then call save!. This may be the best approach for you anyway, since you may very well not want to finalize the save until the other relations are saved, and you probably want to do all of that in the same database transaction.
Using first_or_create you can't know for sure is it a newly created object or one from the database. Possible tricky solution is to compare created_at value with current time. This works if you don't create objects often.
Btw, why you need to know is it the newly created object or not?

Delete the temporary object from object.new?

In the show action, there is logic that requires me to define:
#object.nested_object.new (or #object.nested_object.build or #object.nested_object.create)
However, when it's time to show a list of the nested_objects using something like:
#object.nested_objects.each do |nested_objects|
#display
end
There is an extra line item for the temporary nested object created with #object.nested_object.new.
Is there a way to forcefully remove that temporary object before I display the list of the actual nested_objects?
Or is there another way of accomplishing the following:
-creating a temporary nested_object for logic tests
-showing a list of nested_objects
I've tried stuff like:
temp_nested_object = #object.nested_object.new
temp_nested_object.delete
but wasn't successful.
Thanks for your time!
UPDATE:
I'm trying to accomplish this:
<% if can? :create, #project.tasks.build %>
From here:
https://github.com/ryanb/cancan/wiki/Nested-Resources
I've tried to find other ways of accomplishing this:
https://github.com/ryanb/cancan/issues/608
but seems like I need to workaround it in the view.
First you should not create a temporary object.
if you follow the previous advice, simply do:
#object.nested_objects.select(&:persisted?).each do |nested_objects|
#display
end
This will filter objects from your db (no need to delete a temporary object, it will disappear at the end of the request).
One possibility is to just remove it from the collection with:
# Remove last object in collection
#object.nested_objects.pop

Fill in unfilled properties with other model

I have an ActiveRecord model #new_profile that has some, but not all of its properties filled in. I have another model #default_profile that has a bunch of values I want to copy over, but only if the properties from the first are not filled in. Is there a built in way to do this besides a block like...
#new_profile.name ||= #default_profile.name
#new_profile.address ||= #default_profile.address
# etc.
This might work
#new_profile.update_attributes!(#default_profile.attributes.merge(#new_profile.attributes))
The problem with this, is that if the attribute is in #new_profile, but it is nil, the merge might leave the value set as nil. You might need to do the following.
new_profile_attrs = #new_profile.attributes.reject{ |key,value| !value }
#new_profile.update_attributes!(#default_profile.attributes.merge(new_profile_attrs))
#new_profile.update_attributes(#default_profile.attributes.merge(#new_profile.attributes))
You could try something like
#new_profile.attributes = #new_profile.attributes.reverse_merge #default_profile.attributes
If you need to copy ALL the attributes (except id of course):
#new_profile.attributes.each{|k,v| #new_profile[k] ||= #default_profile[k] if k != 'id'}
Things like update_attributes won't allow you to copy attr_protected-attributes. This thing should.

is there a way to tell if a record is new after it's been created with find_or_create_by

I'm using this code:
item = Item.find_or_create_by(:name => title.strip)
if item.changed?
results.newItemsCount += 1
end
I want to get the count of newly created records for logging. I was hoping that the .changed property would be true for items that are newly created but it looks like it isn't. The next part of the code may modify the item even if it isn't new, so I really need to know right here if the item is new.
Is there a way to tell if the record is new or not? Didn't see anything like that in the docs. You'd think that just setting the title (in the constructor) would qualify as changed but it seems like that doesn't happen.
I don't think its possible to find whether the call was find or create using changed? or persisted? methods.
Instead you can do this by find_or_initialize_by. Here you can check persisted? value to determine the item is new or not. if its false then you know that its a new document.
item = Item.find_or_initialize_by(:name => title.strip)
unless item.persisted?
item.save
results.newItemsCount += 1
end
Other way is you can use after_create callback. if its a new item this callback will be triggered. And you can do your operation here.
Hope it helps
Maybe it is too late, but it is possible.
You can pass a block like this:
item = Item.find_or_create_by(:name => title.strip) do |_i|
puts "Called only for newly created records"
end
Hope it will help somebody.

Resources