self.update_attributes not updating the attribute - ruby-on-rails

I'm calling update_attributes but it doesn't seem to be changing the object.
def add_to_vote_count(increment)
vc = vote_count ? vote_count : 0
puts "CALLED a_t_v_c(#{increment}), NEW VOTE COUNT SHOULD BE #{vc + increment}"
self.update_attributes(:vote_count => (vc + increment))
end
Here's some console testing:
ruby-1.8.7-p299 > p = Factory(:playlist)
=> #<Playlist id: 56, user_id: 0, message: "Lorem ipsum dolor sit amet, consectetur adipisicing...", title: "Ipsum Dolor", flag: 2, created_at: "2010-08-12 18:18:51", updated_at: "2010-08-12 18:18:51", moderation_score: 0, photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil, category_id: nil, widget_id: 22100001, permalink: #<ActiveSupport::Multibyte::Chars:0x102feba48 #wrapped_string="ipsum-dolor-27">, cached_tag_list: "", vote_count: 0>
ruby-1.8.7-p299 > p.vote_count
=> 0
ruby-1.8.7-p299 > p.votes
=> []
ruby-1.8.7-p299 > p.votes << Vote.new(:vote => true)
CALLED a_t_v_c(1), NEW VOTE COUNT SHOULD BE 1
VOTE CREATED
=> [#<Vote id: 235, vote: true, voteable_id: 56, voteable_type: "Playlist", voter_id: nil, voter_type: nil, created_at: "2010-08-12 18:19:09", updated_at: "2010-08-12 18:19:09">]
ruby-1.8.7-p299 > p.votes
=> [#<Vote id: 235, vote: true, voteable_id: 56, voteable_type: "Playlist", voter_id: nil, voter_type: nil, created_at: "2010-08-12 18:19:09", updated_at: "2010-08-12 18:19:09">]
ruby-1.8.7-p299 > p.vote_count
=> 0
ruby-1.8.7-p299 > p
=> #<Playlist id: 56, user_id: 0, message: "Lorem ipsum dolor sit amet, consectetur adipisicing...", title: "Ipsum Dolor", flag: 2, created_at: "2010-08-12 18:18:51", updated_at: "2010-08-12 18:18:51", moderation_score: 0, photo_file_name: nil, photo_content_type: nil, photo_file_size: nil, photo_updated_at: nil, category_id: nil, widget_id: 22100001, permalink: #<ActiveSupport::Multibyte::Chars:0x102feba48 #wrapped_string="ipsum-dolor-27">, cached_tag_list: "", vote_count: 0>
Any idea why nothing is getting saved? I don't get any errors, and calling the methods normally returns the 'correct' results.
ruby-1.8.7-p299 > p.update_attributes(:vote_count => 100)
=> true
ruby-1.8.7-p299 > p.vote_count
=> 100
ruby-1.8.7-p299 > p.add_to_vote_count(10)
CALLED a_t_v_c(10), NEW VOTE COUNT SHOULD BE 110
=> true
ruby-1.8.7-p299 > p.vote_count
=> 110
The only difference I can see is that add_to_vote_count is being called by the Vote class in its after_create method. As you can see from the output, though, add_to_vote_count is definitely getting called.
#in vote.rb
def after_create
voteable.add_to_vote_count( vote ? 1 : -1 )
puts "\n\nVOTE CREATED\n\n"
end
Edit: Actually, it turns out that the object is getting updated, but my reference is not. That is to say, p returns the old version with no votes, but Playlist.find(p.id) returns the correct one. I assume this is due to caching (Rails doesn't want to hit the database again for an item it should have in memory), so how do I force Rails to realize that stuff changed?

I really don't know if this is going to help you, but you can use increment instead of update_attributes.
def add_to_vote_count(increment)
self.increment!(:vote_count, increment)
end
If you try, please let me know if this worked :]
Edit
I believe the reload method can solve your problem!

My guess is that your object is failing validation and that's why its not saving. Try using update_attributes! with the exclamation so it throws and error and you can see whats up.
Good luck.

Related

How to get the current index during recursive call

I use the gem ancestry to create comments.
Now, I can list all comments.
But I want to push serial number to each comment.
For example, if there were 3 comments, the first comment is annotated by 1, the next annotated by 2,..
I have no idea how to do it?
show.html.haml
- if notice
%p.alert.alert-success= notice
= nested_comments(#comment.subtree.arrange(:order => :created_at))
helper
def nested_comments(comments)
if comments.respond_to? :map
comments.map do |comment, sub_comments|
render(comment) + content_tag(:div, nested_comments(sub_comments), :class => "nested_comments")
end.join.html_safe
end
end
each_with_index won't work on recursive
if I have 4 comments, I want to show 0,1,2,3 for each comment
But each_with_index can not make it because it's a recursive call.
comments.each_with_index.map do |(comment, sub_comments), i|
comments
=> {#<Comment id: 2, user_id: 1, ip: nil, content: "I'm id2 the second floor VIVOTEK Releases New Vers...", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 03:59:38", updated_at: "2014-11-07 06:56:12", ancestry: nil>=>
{#<Comment id: 4, user_id: 1, ip: nil, content: "lala", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 05:22:41", updated_at: "2014-11-07 05:22:41", ancestry: "2">=>
{#<Comment id: 5, user_id: 1, ip: nil, content: "son of 4", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 06:38:04", updated_at: "2014-11-07 06:38:04", ancestry: "2/4">=>
{},
#<Comment id: 6, user_id: 1, ip: nil, content: "dild last 252", commentable_id: nil, commentable_type: nil, created_at: "2014-11-07 06:52:15", updated_at: "2014-11-07 06:52:15", ancestry: "2/4">=>
{}}}}
You can use with_index with map
comments.map.with_index do |comment, sub_comments, index|
Every enumerable instance in ruby has a method each_with_index, providing an_enumerator. So in your case I would suggest to use:
- comments.map do |comment, sub_comments|
+ comments.each_with_index.map do |idx, comment, sub_comments|
Hope it helps.
I don't know of an elegant solution. But you could pass a counter into your nested_comments function, and deal with the problem manually -- which might well mean without map at all. Ugly, I know.
To take a simpler example, should you need one:
def nested_foo(result, string, index)
index += 1
result << "\n#{index}: #{string}"
if index >= 10
return result
else
return nested_foo(result, string, index)
end
end

How to destroy a built object?

How can I delete or destroy an object located in memory, but not in the database?
irb(main):034:0> mentor.registered_students.build(:user_id => 20)
=> #<RegisteredStudent id: nil, user_id: 20, assigned_mentor_id: 1, description: nil, created_at: nil, updated_at: nil>
irb(main):035:0> mentor.registered_students.last
=> #<RegisteredStudent id: nil, user_id: 20, assigned_mentor_id: 1, description: nil, created_at: nil, updated_at: nil>
irb(main):036:0> mentor.registered_students.last.destroy
(0.3ms) BEGIN
(0.2ms) COMMIT
=> #<RegisteredStudent id: nil, user_id: 20, assigned_mentor_id: 1, description: nil, created_at: nil, updated_at: nil>
irb(main):037:0> mentor.registered_students.last.delete
=> #<RegisteredStudent id: nil, user_id: 20, assigned_mentor_id: 1, description: nil, created_at: nil, updated_at: nil>
irb(main):038:0> mentor.registered_students.last
=> #<RegisteredStudent id: nil, user_id: 20, assigned_mentor_id: 1, description: nil, created_at: nil, updated_at: nil>
I already used destroy or delete but they look for records in database.
CONTROLER ACTION:
def mix
unless params[:mentor_id].nil? || params[:students_id].nil?
#mentor = AssignedMentor.find params[:mentor_id]
#students = params[:students_id]
#students.each do |student|
if student[1] == "0"
registered_student = RegisteredStudent.where("assigned_mentor_id = ? AND user_id = ?", #mentor.id, student[0] ).first
registered_student.destroy
end
if student[1] == "1"
#mentor.registered_students.build(:user_id => student[0])
#mentor.save
if #mentor.errors.size > 0
#mentor.registered_students.reload
end
end
end
#redirect_to bind_admin_users_path
end
#flash[:alert] = t("labels.no_students")
redirect_to bind_admin_users_path
end
mentor.registered_students.reload
does the trick

Rails ActiveRecord : scope incoherence

In my model I have :
#models/friend.rb
scope :approved_friend, where(:approved => true)
And the Rails console outputs :
User.find(2).friends
=> [#<Friend id: 18, user_id: 2, approved: true, created_at: "2013-04-23 09:18:59", updated_at: "2013-04-23 09:18:59", friend_id: 1>]
User.find(2).friends.approved_friend
=> []
Notice that approved is true in the output ...
Where it gets crazy is here :
User.find(1).friends.approved_friend
=> [#<Friend id: 19, user_id: 1, approved: true, created_at: "2013-04-23 09:19:36", updated_at: "2013-04-23 09:19:36", friend_id: 2>]
Am-I missing something ?
EDIT :
On one hand you have this query :
SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 2
=> [#<Friend id: 18, user_id: 2, approved: true, created_at: "2013-04-23 09:18:59", updated_at: "2013-04-23 09:18:59", friend_id: 1>]
On the other hand, you've got this (query through scope) :
SELECT "friends".* FROM "friends" WHERE "friends"."user_id" = 2 AND "friends"."approved" = 't'
=> []
Since the :status field is in the Friend model, you might have to change the scope to this
scope :approved_friend, where('friends.approved' => true).includes(:friend)

Why is my after_save callback stopping my ActiveRecord association from saving properly?

When I comment out my after_save call back, my ActiveRecord associations work just fine. In Rails Console, you'd see:
> #report = Report.create :name => "foo"
=> #<Report id: 9, name: "foo", created_at: "2013-03-05 09:51:55", updated_at: "2013-03-05 09:51:55">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 18, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 09:52:32", updated_at: "2013-03-05 09:52:32", additive: false, instructions: nil>
> #report.questions
=> [#<Question id: 18, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 09:52:32", updated_at: "2013-03-05 09:52:32", additive: false, instructions: nil>]
> #question.reports
=> [#<Report id: 9, name: "foo", created_at: "2013-03-05 09:51:55", updated_at: "2013-03-05 09:51:55">]
However, the associations stop working when I add the following after_save callback to question.rb:
def create_matching_surveys
self.reports.each do |report|
report.reviews.each do |review|
review.competitors.each do |competitor|
competitor.surveys.find_or_create_by_question_id(self.id)
end
end
end
end
Then, in Rails Console, you get:
> #report = Report.create :name => "foo"
=> #<Report id: 13, name: "foo", created_at: "2013-03-05 10:20:51", updated_at: "2013-03-05 10:20:51">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 24, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 10:21:02", updated_at: "2013-03-05 10:21:02", additive: false, instructions: nil>
> #report.questions
=> [#<Question id: 24, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 10:21:02", updated_at: "2013-03-05 10:21:02", additive: false, instructions: nil>]
> #question.reports
=> []
This happens whether or not the report has reviews that have competitors.
The strange thing is I thought the callback was meant to happen after the question was saved? So by rights the association should save too before any of this happens, right?
How do I fix it?
UPDATE
I think I have to call the callback in the right spot in the object's life cycle, but I can't find that spot. Here's why I think this:
> #report = Report.create :name => "foo"
=> #<Report id: 20, name: "foo", created_at: "2013-03-05 12:29:35", updated_at: "2013-03-05 12:29:35">
> #question = #report.questions.create :description => "bar"
=> #<Question id: 31, standard_id: nil, description: "bar", element_id: nil, condition_id: nil, blueprint_name: nil, blueprint_url: nil, created_at: "2013-03-05 12:30:14", updated_at: "2013-03-05 12:30:14", additive: false, instructions: nil>
> #question.reports
=> []
> #question.update_attributes :description => "foo"
=> true
> #question.reports
=> [#<Report id: 20, name: "foo", created_at: "2013-03-05 12:29:35", updated_at: "2013-03-05 12:29:35">]
BTW, the method is now in question_observer.rb:
class QuestionObserver < ActiveRecord::Observer
def after_save(model)
model.reload
model.reports.reload
model.reports.each do |report|
report.reviews.each do |review|
review.competitors.each do |competitor|
competitor.surveys.find_or_create_by_question_id(model.id)
end
end
end
return true
end
end
The answer was to use a neat new callback hook called after_commit which was introduced with Rails 3.
See http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit.
The only issue is after_commit doesn't work "out of the box" with transactional fixtures, but there are plenty of solutions out there, and I found this one worked well for me: https://supportbee.com/devblog/2012/01/14/testing-after_commitafter_transaction-with-rspec/

After Rails console restart, parent model can't find associated child model objects (and then can)

I've been stuck trying to figure out why a counter cache on my (parent) BlogPosts table won't update from the (child) Comments table. At first I thought the answer provided in my earlier question might be the solution but something happened after I went to bed last night because when I woke up this morning and restarted my Rails console, my BlogPosts (actually just one Post - id# 1) aren't able to find their associated child Comments. I checked the Comments table and the five comments I create are all there, attached to post_id = 1. The output from my Rails console in the earlier question indicates that the post could find the comments last night. Perhaps this explains why the counter cache was not updating but I'm still not sure why the parent would not be able to find its children. Any hints?
Loading development environment (Rails 2.3.2)
>> p = Post.find(1)
p = Post.find(1)
=> #<Post id: 1, title: "test", content: "test", author_id: 1, status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-24 07:21:24", comments_count: 0>
>> p.comments.size
p.comments.size
=> 0
>> p.comments
p.comments
=> []
UPDATE: This is strange - I restarted the Rails console again but this time I called p.comments BEFORE I called "p.comments.size" - AND IT FOUND THE COMMENTS!! What's going on here?
Loading development environment (Rails 2.3.2)
>> p = Post.find 1
p = Post.find 1
=> #<Post id: 1, title: "test", content: "test", author_id: 1, status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-24 07:21:24", comments_count: 0>
>> p.comments
p.comments
=> [#<Comment id: 5, post_id: 1, author_id: 1, content: "Fifth Comment", status: "ok", created_at: "2009-05-24 07:08:56", updated_at: "2009-05-24 07:08:56">, #<Comment id: 4, post_id: 1, author_id: 1, content: "Fourth Comment", status: "ok", created_at: "2009-05-24 07:05:32", updated_at: "2009-05-24 07:05:32">, #<Comment id: 3, post_id: 1, author_id: 1, content: "Third Comment", status: "ok", created_at: "2009-05-24 06:34:59", updated_at: "2009-05-24 06:34:59">, #<Comment id: 2, post_id: 1, author_id: 1, content: "Second Comment", status: "ok", created_at: "2009-05-24 05:20:43", updated_at: "2009-05-24 05:20:43">, #<Comment id: 1, post_id: 1, author_id: 1, content: "First Comment", status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-21 19:27:14">]
>> p.comments.size
p.comments.size
=> 5
UPDATE 2: Following srboisvert's advice I created a new Comment and added it to the Post. This worked and the comments_counter updated to 1.:
Loading development environment (Rails 2.3.2)
>> p = Post.find 1
p = Post.find 1
=> #<Post id: 1, title: "test", content: "test", author_id: 1, status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-24 07:21:24", comments_count: 0>
>> com = Comment.new(:post_id => 1, :author_id => 1, :content => 'Sixth Comment', :status => 'ok')
com = Comment.new(:post_id => 1, :author_id => 1, :content => 'Sixth Comment', :status => 'ok')
=> #<Comment id: nil, post_id: 1, author_id: 1, content: "Sixth Comment", status: "ok", created_at: nil, updated_at: nil>
>> p.comments << com
p.comments << com
=> [#<Comment id: 6, post_id: 1, author_id: 1, content: "Sixth Comment", status: "ok", created_at: "2009-05-24 17:59:45", updated_at: "2009-05-24 17:59:45">, #<Comment id: 5, post_id: 1, author_id: 1, content: "Fifth Comment", status: "ok", created_at: "2009-05-24 07:08:56", updated_at: "2009-05-24 07:08:56">, #<Comment id: 4, post_id: 1, author_id: 1, content: "Fourth Comment", status: "ok", created_at: "2009-05-24 07:05:32", updated_at: "2009-05-24 07:05:32">, #<Comment id: 3, post_id: 1, author_id: 1, content: "Third Comment", status: "ok", created_at: "2009-05-24 06:34:59", updated_at: "2009-05-24 06:34:59">, #<Comment id: 2, post_id: 1, author_id: 1, content: "Second Comment", status: "ok", created_at: "2009-05-24 05:20:43", updated_at: "2009-05-24 05:20:43">, #<Comment id: 1, post_id: 1, author_id: 1, content: "First Comment", status: "ok", created_at: "2009-05-21 19:27:14", updated_at: "2009-05-21 19:27:14">]
Can you create a comment in the console, add it to a post and then display it in 3 separate steps?
You are doing lots of non-default fk name specification (though your names don't seem to be that different from what rails would expect so you might want to just use the conventions)so my guess is that somehow your has_many belongs_to are messed up.

Resources