Which callback does ActiveRecord use to record timestamp? - ruby-on-rails

I'm just wondering here whether any of you guys know when ActiveRecord use it's "magic" to record the timestamp (e.g. created_at, updated_at).
What i mean when is, at which callback ? (if AR use callback at all).
I'm asking this because I want to create an auto-updating column (that record sequential number for each object) and I want to replicate AR way to do this as much as possible.
EDITED:
It seems that AR does it between after_validation and before_create/before_update. You can do some tests for this by creating a presence validation for created_at column and inserting new record with blank created_at, it would return an error.

I don't know where AR does it, but the proper place for what you describe sounds like before_create

In Rails 3.2.12, this code is located in lib/active_record/timestamp.rb.
As you mention in your question and DGM suggests, Rails will update the timestamps when creating or updating, so sticking your code in before_create and before_update should work.
You may also want to take a look at the ActiveRecord counter_cache functionality. ActiveRecord supports creation of a column that can automatically be incremented/decremented. Additionally, you can perform more complicated logic.

Related

Rails Active record validations - should I validate non-user generated data

I am not sure if I understand totally active record validation role.
Of course, if a user inputs data (like an email or a country), I can and should validate its existence, its uniqueness or its inclusion in a list of countries
But for example, if I have methods in the backend that change an attribute page_clicked or click_date or even the column update_at, that I "control" i.e 'is not generated by a user's input', should I use active record validations ?
I'm asking this because on a very 'hot database' (need speed for millions of frequent updates), I wonder if checking on each update that updated_at is a datetime, and that if a clicked column is true/false and nothing esle is really necessary as the user is not the one inputting/controlling these data but I am through Rails custom methods I wrote
Thanks
I don't think there is a general satisfying answer to your question. It's up to you to enforce validation or not.
Remember that you don't have to use ActiveRecord for validation, you can also use your DBMS to ensure that:
a value will never be NULL (one of the most annoying errors)
a value has the correct TYPE
a FOREIGN KEY always points to an existing row in another table
and depending on your DBMS, a lot more is possible
If you need high INSERT speed and want to go with raw SQL INSERTS, putting some validation in your database can prevent nasty application errors later.
Validations should guard your database and its job should be to stop saving the records that are considered invalid by your application.
There is no hard rule on what is valid record you have to decide it your self by adding the validations. If the record wont pass the validation step it is simply not going to be saved to the database.
From Active Record Callbacks:
3.1 Creating an Object
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback
3.2 Updating an Object
before_validation
after_validation
before_save
around_save
before_update
around_update
after_update
after_save
after_commit/after_rollback
You can see that validation hooks run at the beginning of the object life cycle.
So in your case instead of asking your self a question:
Should I use active record validations if the record is not generated by a user's input.
You should ask your self:
Is this record invalid without page_clicked or click_date(aka them being nil)
UPDATE
If you consider record to be invalid but worrying about speed problems with running validations I would do the validations to make sure that all the records in the database are valid and try to find the way to optimise the speed somewhere else. Plus not 100% sure but time spend on saving invalid records and filtering them later on will be probably much longer then validating in the first place.
When performance is really a priority and that I am sure that we developers / the server are the only ones who can manipulate specific attributes of a Model, I will
Make sure that I create a separate method / wrapper method for this specific action.
In this specific method, I call .save (validate: false) instead of the usual .save
I still write validations for the said attributes for developers' reference to prevent future development errors, and in case a new developer comes in and accidentally save an invalid record, precisely just because there's no validation to safeguard it.
Or, I will use .update_column instead of .save (validate: false) to perform a direct DB call, skipping Model validations and callbacks (If you also do not want callbacks to be called).
Note that .update_column is different from .update.

Updating timestamps with #update_all

When I have list of ids that I want to update their property the updated_at field in the database doesn't seem to change, here is what I mean :
ids = [2,4,51,124,33]
MyObj.where(:id => ids).update_all(:closed => true)
After this update is executed updated_at field doesn't change. However when I enter rails console with rails c and do this :
obj = MyObj.find(2)
obj.closed = false;
obj.save!
After this statement updated_at field changes value. Why is this? I'm relying on this updated_at field in my app as I'm listening to the updates and doing whole app flow when this happens?
Edit
I just found out from dax answer that :
Timestamps
Note that ActiveRecord will not update the timestamp fields (updated_at/updated_on) when using update_all().
I don't want to be updating one record at a time, is there a way around this? without resorting to sql level?
#update_all does not instantiate models.
As such, it does not trigger callbacks nor validations - and timestamp update is made in a callback.
Edit about edit :
If you want to keep the "one query to rule them all", you can update updated_at as well as :closed :
MyObj.where(:id => ids).update_all(closed: true, updated_at: DateTime.now)
But be aware validations are still not run.
Updates all, This method constructs a single SQL UPDATE statement and sends it straight to the database. It does not instantiate the involved models and it does not trigger Active Record callbacks or validations. Values passed to update_all will not go through ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL database.
As such, it does not trigger callbacks nor validations - and timestamp update is made in a callback.update_at is a call back for reference http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-update_all
Timestamps
Note that ActiveRecord will not update the timestamp fields (updated_at/updated_on) when using update_all().
source: http://apidock.com/rails/ActiveRecord/Relation/update_all
If anyone is interested I did make a gist that outlines how to roll it yourself.
https://gist.github.com/timm-oh/9b702a15f61a5dd20d5814b607dc411d
It's a super simple implementation just to get the job done.
If you feel like there is room for improvement please comment on the gist :)

Rails 3.0.x - why the other model is beeing updated?

I am trying to figure out things in code written by someone else. There are two models, with simple has_many relation (A->B).
Now the problem is, that while saving the "B" model, also the "A" model is updated in the database. Now, there is no callbacks of any sort, there is no special relation conditions (:autosave, etc), and also there is no Observers in the code.
The question is, what could be other things, which define this kind of behaviour?
Update: I am trying to debug the save process to track anything, but I am getting overwhelmend by the ammount of calls to internal active_record methods, so this approchach is getting useless.
Bonus question: How can I dump current model callbacks (in case, they were added in some manner which resistant to my grepping skills).
It could have :touch => true. That auto updates the association.
This turned out to be very complicated, internal application bug with Marshaling. In the result records fetched from cache were marked as not persisted, which forced ActiveRecord to create duplicate record while saving related objects.

Calling ActiveRecord's #relationship_ids = [1,2,3] saves immediately. Any workarounds?

I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.

How do I stop ActiveRecord looking for a table?

I am trying to create an ActiveRecord model called 'Search' without a table. I keep getting this error when I do #search = Search.new.
sql::Error: Table 'searchdemo_development.tablelesses' doesn't exist: SELECT * FROM tablelesses
I am using the idea from this comment: Rails model without database. I also get the same kind of error doing the basic:
class Search < ActiveRecord::Base
end
How do I get ActiveRecord stop looking for a table?
I can think of a few reasons you might want to do something like this. Perhaps you want to leverage some of the non-db-related methods on ActiveRecord or you want to pass your object to something that expects and ActiveRecord instance. Without more info, it is impossible to say whether the choice to use AR here is correct or incorrect.
In any event, if you want to continue on this path...
check out this Railscast
http://railscasts.com/episodes/121-non-active-record-model
and also checkout this gem:
http://github.com/kennethkalmer/activerecord-tableless-models/tree/master
Why in the name of the lord would you want an actve record model without a table? The purpose of an active record model is to communicate with a database. I assume that it is impossible to have an active record model without a db table.
Perhaps you want a regular class?
class Search
# your methods here
end

Resources