ActiveRecord touch(:column) always also updates :updated_at - ruby-on-rails

I've got pretty much the same problem as this guy: http://www.ruby-forum.com/topic/197440.
I'm trying to touch a column (:touched_at) without having it auto-update :updated_at, but watching the SQL queries, it always updates both to the current time.
I thought it might be something to do with the particular model I was using it on, so I tried a couple different ones with the same result.
Does anyone know what might be causing it to always set :updated_at when touching a different column? touch uses write_attribute internally, so it shouldn't be doing this.
Edit:
Some clarification... the Rails 2.3.5 docs for touch state that "If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes." But mine isn't acting that way. Perhaps it's a case of the docs having drifted away from the actual state of the code?

You pretty much want to write custom SQL:
def touch!
self.class.update_all({:touched_at => Time.now.utc}, {:id => self.id})
end
This will generate this SQL:
UPDATE posts SET touched_at = '2010-01-01 00:00:00.0000' WHERE id = 1
which is what you're after. If you call #save, this will end up calling #create_with_timestamps or #update_with_timestamps, and these are what update the updated_on/updated_at/created_on/created_at columns.
By the way, the source for #touch says it all.

Related

How would I go about forming this controller correctly in Rails?

How would I define in my controller to run and do the following when the Class is fetched in URL;
I need to find all "jobs" to current_user I'm guessing something like this;
#joblisting = current_user.Joblisting.find(params[:id])
Then I need to take those jobs and check if their column "job_status" has the text "completed" in them or other"
If the jobs_status is "completed" then I need to run code so I do an "if"
I would have to pass the calculation.
#joblisting = current_user.Joblisting.find(params[:id])
if #joblisting.where(:project_status => "completed")
number_to_currency(current_user.Joblisting.where(:project_status => 'completed').sum('jobprice') * 1.60 - current_user.Joblisting.where(:project_status => 'completed').sum('jobprice'))
Notifier.notify_payout(current_user).deliver
#joblisting.project_status = 'paid'
#joblisting.save
end
This is what I've got and I'm stuck with passing the calculation to the Notifier.notify_payout template.
I'm sure whomever knows rails better then me, will right away see my mistakes.
My answer will not give you the code you seek. I'm doing this because I feel like you still have a lot to learn, but I will tell you what to do, just not give you the code. If you say that this line of code works....
#joblisting = Joblisting.where(:developer_id => current_developer[:id])
if #joblisting.where(:project_status => "completed")
Notifier.notify_payout(current_developer).deliver
end
then so be it. As for updating the :project_status column from "completed" to "paid", there can be multiply ways to handle this. You could create a method inside your model (let's call it project_is_paid) that changes project status to paid and declare it inside notify_payout method on success.
But that might be confusing for someone else looking at your code and wondering why all of a sudden the record is changing from "completed" to "paid" inside the database. Plus you would have to pass the joblisting object as an argument which is even more work.
Another way to think of it is to just write a simple conditional statement inside the controller. If the mail is delivered, then call the method project_is_paid. Just be careful not to start adding all this logic to your controller, your controller should be concise and short. Let the model deal with the logic.
I want to conclude with going back to your working controller code you posted. I'm willing to bet that you can turn the two following lines into one.
#joblisting = Joblisting.where(:developer_id => current_developer[:id])
if #joblisting.where(:project_status => "completed")
Why do I say that? Well you are making two queries to the same table Job Listings. Here is a little help if you can't figure out how... link
And if I failed to answer your latest question in the comments, I'm sorry. Post an update in your question and I would be happy to update my own answer.

Updating a lot of records frequently

I have a Rails 3 app that has several hundred records in a mySQL-DB that need to be updated multiple times each hour. The actual updating is done through delayed_job which is triggered in controller-logic (checking if enough time has passed since the last update, only then sth. happens).
Each update is slow, it can take up to a second in some cases (although it averages at 3 - 5 updates/sec.).
Code looks like this:
class Thing < ActiveRecord::Base
...
def self.scheduled_update
Thing.all.each do |t|
...
t.some_property = new_value
t.save
end
end
end
I've observed that the execution stalls after 300 - 400 records and then the delayed job just seems to hang and times out eventually (entries in delayed_job.log). After a while the next one starts, also fails, and so forth, so not all records get updated.
What is the proper way to do this?
How does Rails handle database-connections when used like that? Could it be some timeout issue that is not detected/handled properly?
There must be a default way to do this, but couldn't find anything so far..
Any help is appreciated.
Another options is update_all.
Rails is a bad choice for mass data records. See if you can create a sql stored procedure or some other way that would avoid active record.
Use object.save_with_validation(false) if you are ok with skipping validations altogether.
When finding records, use :select => 'a,b,c,other_fields' to limit the fields you want ('a', 'b', 'c' and 'other' in this example).
Use :include for eager loading when you are initially selecting and joining across multiple tables.
So I solved my problem.
There was some issue with the rails-version I was using (3.0.3), the Timeout was caused by some bug I suspect. Updating to a later version of the 3.0.x branch solved it and everything runs perfectly now.

Concurrency and Mongoid

I'm currently trying my hand at developing a simple web based game using rails and Mongoid. I've ran into some concurrency issues that i'm not sure how to solve.
The issue is i'm not sure how to atomically do a check and take an action based upon it in Mongoid.
Here is a sample of the relevant parts of the controller code to give you an idea of what i'm trying to do:
battle = current_user.battle
battle.submitted = true
battle.save
if Battle.where(opponent: current_user._id, submitted: true, resolving: false).any?
battle.update_attribute(:resolving, true)
#Resolve turn
A battle is between two users, but i only want one of the threads to run the #Resolve turn. Now unless i'm completely off both threads could check the condition one after another, but before setting resolving to true, therefore both end up running the '#Resolve turn' code.
I would much appreciate any ideas on how to solve this issue.
I am however getting an increasing feeling that doing user synchronization in this way is fairly impractical and that there's a better way altogether. So suggestions for other techniques that could accomplish the same thing would be greatly appreciated!
Sounds like you want the mongo findAndModify command which allows you to atomically retrieve and update a row.
Unfortunately mongoid doesn't appear to expose this part of the mongo api, so it looks like you'll have to drop down to the driver level for this one bit:
battle = Battle.collection.find_and_modify(query: {oppenent: current_user._id, ...},
update: {'$set' => {resolving: true})
By default the returned object does not include the modification made, but you can turn this on if you want (pass {:new => true})
The value returned is a raw hash, if my memory is correct you can do Battle.instantiate(doc) to get a Battle object back.

ActiveModel::MissingAttributeError occurs after deploying and then goes away after a while

I have a Rails 3.0.9 app that, once it is deployed, suffers from a bunch of ActiveModel::MissingAttributeErrors that crop up causing 500s. The errors occur fairly randomly, sometimes a page will load, other times it won't, but the attributes are all existing attributes in the database and should be found.
The strange part is that after a while, the errors go away. Suddenly, they stop causing an issue.
I have searched about for a solution to this, but this error mostly occurs either when someone has done Model.all(:select => 'column_x,column_y') and are calling for column_z or when they are using cache_money. I am doing neither of these things.
Can anyone help?
You probably have a query that doesn't return all the columns (i.e. uses :select) and then cache_money; or some other ActiveRecord plugin uses an after_initialize callback, which executes whenever a new ActiveRecord object is created (i.e. when fetched from the database).
In that initialize callback, something tries to access or use an attribute that wasn't included in the :select. You'd expect this to return nil for that attribute, but an ActiveRecord::MissingAttributeError is thrown instead.
You can rescue ActiveRecord::MissingAttributeError like the article suggests, or patch the plugin(s) to use has_attribute?(:attribute_name) before they try to access or modify the attribute.
If you have been having this issue only directly after updating your database without any deploys or server restarts following, then what worked for me may work for you:
Run heroku restart and it should be fixed. Before the dyno restarts old data sometimes remains cached on the server, so starting it up again will scrub all of that data and prevent it from causing errors of that sort. Hope this helps.
I found an interesting take on this that resulted in the same error. In an attempt to reuse code we subclasses a presenters class with a presenters class that performed grouping to use in a graph view.
To simplify, it was something like:
class PostPresenter
def query
Post.where(...stuff....).includes(:wombat)
end
end
The the aggregator did something like the following to build a table of posts per day:
class AggregatePostPresenter < PostPresenter
def group_query
query.select('count(*) as cnt, date(created_at)').group('date(created_at)')
end
end
A call to "group_query" results in an ActiveModel::MissingAttributeError since, I think, the attempt to "includes" Wombat fails because "wombat_id" wasn't in the attributes included in the "select".
This is probably not your answer, however, since it happens regardless of whether or not cache is enabled.
I encountered this issue. Make sure your select: includes all fields referenced in your view, including any relationship IDs and any attributes called within your methods.
The missing attribute can be difficult to identify whenever your views and relationships are complex. The easiest way to debug this is to remove the select portion of your where clause and see if the query/scope/method runs correctly. If so, then add all of the attributes to the select and remove unneeded attributes one-at-a-time until you find the offending attribute.
A similar problem was annoying me when I was trying to make Ajax (actually angularjs) calls to populate an edit-in-place select fields.
I just wanted an id and name attributes to_json and kept getting the MissingAttributeError.
Realised I gotcha'd myself by having an as_json method in the model which is used for the main index and show calls on the model. Basically it was the as_json that was not seeing the attributes it expected.
#foo=Foo.select("id,name")
respond_to do |format|
format.json { render :json => #foo.to_json }
end
gave the error but
respond_to do |format|
format.json { render :json => { :foo=>#foo.as_json(:only=>[:id,:name]) } }
end
seems to be working. I was close to sussing it myself but I found a great explanation at.
http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/
I fixed this by adding .to_json to the end of my controller render.
you need to add line
rescue ActiveRecord::MissingAttributeError
in your after_initialize() method of the model

Saving updates to objects in rails

I'm trying to update one of my objects in my rails app and the changes just don't stick. There are no errors, and stepping through with the debugger just reveals that it thinks everything is updating.
Anyway, here is the code in question...
qm = QuestionMembership.find(:first, :conditions => ["question_id = ? AND form_id = ?", q_id, form_id])
qm.position = x
qm.save
For reference sake, QuestionMembership has question_id, form_id, and position fields. All are integers, and have no db constraints.
That is basically my join table between Forms and Questions.
Stepping through the code, qm gets a valid object, the position of the object does get changed to the value of x, and save returns 'true'.
However, after the method exits, the object in the db is unchanged.
What am I missing?
You may not be finding the object that you think you are. Some experimenting in irb might be enlightening.
Also, as a general rule when changing only one attribute, it's better to write
qm.update_attribute(:position, x)
instead of setting and saving. Rails will then update only that column instead of the entire row. And you also get the benefit of the data being scrubbed.
Is there an after_save?
Is the correct SQL being emitted?
In development log, you can actually see the sql that is generated.
For something like this:
qm = QuestionMembership.find(:first, :conditions => ["question_id = ? AND form_id = ?", q_id, form_id])
qm.position = x
qm.save
You should see something to the effect of:
SELECT * FROM question_memberships WHERE question_id=2 AND form_id=6 LIMIT 1
UPDATE question_memberships SET position = x WHERE id = 5
Can you output what sql you are actually seeing so we can compare?
Either update the attribute or call:
qm.reload
after the qm.save
What is the result of qm.save? True or false? And what about qm.errors, does that provide anything that makes sense to you? And what does the development.log say?
I have run into this problem rather frequently. (I was about to say consistently, but I cannot, as that would imply that I would know when it was about to happen.)
While I have no solution to the underlying issue, I have found that it seems to happen to me only when I am trying to update mysql text fields. My workaround has been to set the field to do something like:
qm.position = ""
qm.save
qm.position = x
qm.save
And to answer everyone else... when I run qm.save! I get no errors. I have not tried qm.save?
When I run through my code in the rails console everything works perfectly as evidenced by re-finding the object using the same query brings the expected results.
I have the same issue when using qm.update_attribute(... as well
My workaround has gotten me limping this far, but hopefully someone on this thread will be able to help.
Try changing qm.save to qm.save! and see if you get an exception message.
Edit: What happens when you watch the log on the call to .save!? Does it generate the expected SQL?
Use ./script/console and run this script.. step by step..
see if the position field for the object is update or not when you run line 2
then hit qm.save or qm.save!... to test
see what happens. Also as mentioned by Tim .. check the logs
Check your QuestionMembership class and verify that position does not have something like
attr_readonly :position
Best way to debug this is to do
tail -f log/development.log
And then open another console and do the code executing the save statement. Verify that the actual SQL Update statement is executed.
Check to make sure your database settings are correct. If you're working with multiple databases (or haven't changed the default sqlite3 database to MySQL) you may be working with the wrong database.
Run the commands in ./script/console to see if you see the same behavior.
Verify that a similar object (say a Form or Question) saves.
If the Form or Question saves, find the difference between the QuestionMembership and Form or Question object.
Turns out that it was emitting the wrong SQL. Basically it was looking for the QuestionMembeship object by the id column which doesn't exist.
I was under the impression that that column was unnecessary with has_many_through relationships, although it seems I was misguided.
To fix, I simply added the id column to the table as a primary key. Thanks for all the pointers.

Resources