reset_counters is failing on exisitng and new projects - ruby-on-rails

Ok..I hope I am doing something dumb (which is usually the case). I have been humming along, adding cache counters to my existing project, when all of the sudden reset_counters failed for pretty much everything. I checked out an old copy when things were working, and it still failed, so I started on a brand new demo project.
I assume this is my error because it suddenly stopped working..and I don't see anyone else having similar issues.
I am using ruby 1.9.3-p392, and rails 3.2.12.
The counters themselves work on both existing and new projects. you just can't use reset_counters.
So here is the problem on a brand new project:
class Post < ActiveRecord::Base
attr_accessible :name
has_many :comments
end
class Comment < ActiveRecord::Base
attr_accessible :name
belongs_to :post, :counter_cache => true
end
here we can see the counters increment:
irb(main):073:0* post = Post.create(:name => 'i am a post')
(0.3ms) BEGIN
SQL (0.4ms) INSERT INTO `posts` (`comments_count`, `created_at`, `name`, `updated_at`) VALUES (0, '2013-03-14 19:56:53', 'i am a post', '2013-03-14 19:56:53')
(0.5ms) COMMIT
=> #<Post id: 3, name: "i am a post", created_at: "2013-03-14 19:56:53", updated_at: "2013-03-14 19:56:53", comments_count: 0>
irb(main):074:0> post.comments << Comment.new(:name => 'i am a comment')
(0.2ms) BEGIN
SQL (0.3ms) INSERT INTO `comments` (`created_at`, `name`, `post_id`, `updated_at`) VALUES ('2013-03-14 19:57:18', 'i am a comment', 3, '2013-03-14 19:57:18')
Post Load (0.3ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 3 LIMIT 1
SQL (0.3ms) UPDATE `posts` SET `comments_count` = COALESCE(`comments_count`, 0) + 1 WHERE `posts`.`id` = 3
(0.4ms) COMMIT
Comment Load (0.2ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` = 3
=> [#<Comment id: 3, name: "i am a comment", post_id: 3, created_at: "2013-03-14 19:57:18", updated_at: "2013-03-14 19:57:18">]
irb(main):075:0> post = Post.find(3)
Post Load (0.5ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 3 LIMIT 1
=> #<Post id: 3, name: "i am a post", created_at: "2013-03-14 19:56:53", updated_at: "2013-03-14 19:56:53", comments_count: 1>
running the code I get:
irb(main):001:0> Post.reset_counters(1,:comments_count)
Post Load (0.5ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
NoMethodError: undefined method `options' for nil:NilClass
from /Users/charles/.rbenv/versions/1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-3.2.12/lib/active_record/counter_cache.rb:22:in `block in reset_counters'
from /Users/charles/.rbenv/versions/1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-3.2.12/lib/active_record/counter_cache.rb:19:in `each'
from /Users/charles/.rbenv/versions/1.9.3-p392/lib/ruby/gems/1.9.1/gems/activerecord-3.2.12/lib/active_record/counter_cache.rb:19:in `reset_counters'
from (irb):1
from /Users/charles/.rbenv/versions/1.9.3-p392/lib/ruby/gems/1.9.1/gems/railties-3.2.12/lib/rails/commands/console.rb:47:in `start'
from /Users/charles/.rbenv/versions/1.9.3-p392/lib/ruby/gems/1.9.1/gems/railties-3.2.12/lib/rails/commands/console.rb:8:in `start'
from /Users/charles/.rbenv/versions/1.9.3-p392/lib/ruby/gems/1.9.1/gems/railties-3.2.12/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

You're almost entirely correct. The parameter in reset_counters though should be the object name, not the column name. So in your case you want to:
1.9.3-p392 :005 > Post.reset_counters(1, :comments)
Post Load (0.3ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT 1 [["id", 1]]
(0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 1
(2.3ms) UPDATE "posts" SET "comments_count" = 1 WHERE "posts"."id" = 1
=> true

Related

Rails creating a M:M relationship

I have a user and team models joined by a membership model.
One user can have many teams and each team can have many users etc.
class User < ApplicationRecord
has_many :memberships
has_many :teams, through: :memberships
End
class Team < ApplicationRecord
has_many :memberships
has_many :users, through: :memberships
End
class Membership < ApplicationRecord
belongs_to :user
belongs_to :team
end
I am looking for a way to simply create a memberships record however am missing something:
2.4.0 :026 > t = Team.last
Team Load (1.2ms) SELECT "teams".* FROM "teams" ORDER BY "teams"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Team id: 42129, name: "Reds", description: "A good team", created_at: "2017-05-18 05:05:09", updated_at: "2017-05-18 05:05:09">
2.4.0 :027 > User.first.memberships
User Load (1.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
Membership Load (1.1ms) SELECT "memberships".* FROM "memberships" WHERE "memberships"."user_id" = $1 LIMIT $2 [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.4.0 :028 > User.first.memberships << t
User Load (0.8ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.4ms) BEGIN
(0.4ms) ROLLBACK
ActiveRecord::AssociationTypeMismatch: Membership(#23507760) expected, got #<Team id: 42129, name: "Reds", description: "A good team", created_at: "2017-05-18 05:05:09", updated_at: "2017-05-18 05:05:09"> which is an instance of Team(#33684520)
from (irb):28
2.4.0 :029 >
What am I missing here?
Update:
When I create a new memberships record and manually add the fk values I get this error when I try to save:
2.4.0 :037 > m
=> #<Membership id: nil, user_id: 1, team_id: 22641, created_at: nil, updated_at: nil>
2.4.0 :038 > m.save
(0.5ms) BEGIN
(0.4ms) ROLLBACK
NoMethodError: undefined method `class_name' for nil:NilClass
Did you mean? class_eval
from (irb):38
Update2
2.4.0 :022 > user = User.first
User Load (2.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<User id: 1, created_at: "2017-05-15 08:17:01", updated_at: "2017-05-19 02:54:30">
2.4.0 :023 > team = Team.first
Team Load (2.3ms) SELECT "teams".* FROM "teams" ORDER BY "teams"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Team id: 22641, name: "Reds", description: "This is a good team", created_at: "2017-05-18 01:41:00", updated_at: "2017-05-18 05:05:09">
2.4.0 :027 > Membership.delete_all
SQL (1.9ms) DELETE FROM "memberships"
=> 1
2.4.0 :029 > m=Membership.new(user: user, team: team)
=> #<Membership id: nil, user_id: 1, team_id: 22641, created_at: nil, updated_at: nil>
2.4.0 :031 > m.save
(1.0ms) BEGIN
SQL (2.3ms) INSERT INTO "memberships" ("user_id", "team_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["user_id", 1], ["team_id", 22641], ["created_at", "2017-05-22 03:37:22.803718"], ["updated_at", "2017-05-22 03:37:22.803718"]]
(2.1ms) COMMIT
=> true
2.4.0 :032 > Membership.delete_all
SQL (1.9ms) DELETE FROM "memberships"
=> 1
2.4.0 :033 > m=Membership.new
=> #<Membership id: nil, user_id: nil, team_id: nil, created_at: nil, updated_at: nil>
2.4.0 :034 > m.user_id=user.id
=> 1
2.4.0 :035 > m.team_id=team.id
=> 22641
2.4.0 :036 > m
=> #<Membership id: nil, user_id: 1, team_id: 22641, created_at: nil, updated_at: nil>
2.4.0 :037 > m.save
(0.5ms) BEGIN
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.8ms) ROLLBACK
NoMethodError: undefined method `class_name' for nil:NilClass
Did you mean? class_eval
from (irb):37
The first error message you're getting (before the Update) is because ActiveRecord is expecting an instance of Membership, and it's getting a Team. User.first.teams << t should work for that example.
As far as the error you shared in the Update, I didn't encounter that when I tried it, and it seemed to work fine with the relationships you gave.
2.2.3 :009 > user = User.first
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1>
2.2.3 :010 > team = Team.first
Team Load (0.2ms) SELECT "teams".* FROM "teams" ORDER BY "teams"."id" ASC LIMIT 1
=> #<Team id: 1>
2.2.3 :011 > membership = Membership.new(user: user, team: team)
=> #<Membership id: nil, user_id: 1, team_id: 1>
2.2.3 :012 > membership.save
(0.1ms) begin transaction
SQL (1.2ms) INSERT INTO "memberships" ("user_id", "team_id") VALUES (?, ?) [["user_id", 1], ["team_id", 1]]
(2.3ms) commit transaction
=> true
One thing to double check would be in how you're constructing the Membership record, and that there are no pluralization errors in the models. Passing in the actual User and Team instances to Membership.new or Membership.create should work.

"RecordNotFound: Couldn't find User with" in rails tutorial

I follow the [railstutorial][rails tutorial] try to setup a simple web site.
In this page, after inserting several records to the Users table, I use rails console, trying to find out some records. But all findings fail.
Could anyone help explain why it fails? Thanks very much
2.0.0-p247 :064 > User.all
User Load (0.3ms) SELECT "users".* FROM "users"
=> #<ActiveRecord::Relation [#<User id: 1, name: "Shijie", email: "shijiexu#yahoo.com", created_at: "2016-12-24 22:36:56", updated_at: "2016-12-24 22:36:56">, #<User id: 2, name: "chen jie", email: "chejie#yahoo.com", created_at: "2016-12-24 22:37:36", updated_at: "2016-12-24 22:37:36">, #<User id: 3, name: "Michael Hartl", email: "mmm#example.com", created_at: "2016-12-25 20:56:58", updated_at: "2016-12-25 21:21:38">]>
2.0.0-p247 :065 > User.find(name: "Shijie")
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", nil]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'={:name=>"Shijie"}
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-4.2.0/lib/active_record/core.rb:154:in `find'
from (irb):65
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.2.0/lib/rails/commands/console.rb:110:in `start'
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.2.0/lib/rails/commands/console.rb:9:in `start'
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:68:in `console'
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.2.0/lib/rails/commands.rb:17:in `<top (required)>'
2.0.0-p247 :066 > User.find(email: "mmm#example.com")
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", nil]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'={:email=>"mmm#example.com"}
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-4.2.0/lib/active_record/core.rb:154:in `find'
from (irb):66
from /home/shijiex/.rvm/gems/ruby-2.0.0-p247/gems/railties-4.2.0/lib/rails/commands/console.rb:110:in `start'
The user.rb is generated.
class User < ActiveRecord::Base
has_many :microposts
end
The rails in my version is Rails 4.2.0, though the rails in the tutorial is for v5+.
The find method will look for the record using the id column. So, in your case, it checks for the record with the id value of email: "mmm#example.com" which obviously doesn't exist.
If you want to find records by a column other than id, use find_by method and pass the column name and value as a hash.
User.find_by(email: "mmm#example.com")
For more, read the documentation of find_by

Retrieving attributes of associated object

I have a post that has many comments. Comments have a body and a title
=> #<ActiveRecord::Associations::CollectionProxy [#<Comment id: 1, author: "jack", body: "how do you like dem apples?", post_id: 1, created_at: "2016-09-29 02:11:00", updated_at: "2016-09-29 02:11:00">]>
2.3.0 :005 > Post.first.comments
Post Load (0.5ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Comment id: 1, author: "jack", body: "how do you like dem apples?", post_id: 1, created_at: "2016-09-29 02:11:00", updated_at: "2016-09-29 02:11:00">]>
2.3.0 :006 > Post.first.comments.body
NoMethodError: Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
undefined method `body' for #<Comment::ActiveRecord_Associations_CollectionProxy:0x007f9bef0a33a8>
In the code above you can see that I try to get the body attribute from the post that has a comment, but I get a no method exception. How do I retrieve the associated objects data in these types of situations?
1) You get error because you are calling body on the collection of comments, not a single instance of Comment class.
2) To get it working:
# select the comment, which's body you want to get
Post.first.comments.first.body
Post.first.comments is a collection, you can treat it as an array and map it, for example, to get all comments' bodies:
# would return all bodies of all comments, that belongs to the `Post.first`
Post.first.comments.pluck(:body)
Always read exceptions messages carefully.

Correct association for a status of another model

So I have two models - Status and Post.
A status can either be: Empty, Half, or Full.
Each post can only have 1 status at any one time, i.e. a post can only be either empty or half, etc. It has to be one of the 3.
However, a status can have many posts. So the Empty status may have 20 posts.
What is the best way to approach this, from an associations perspective?
I was initially thinking that a Post has_one Status. But the issue with that, is that the Status would have to belong_to a Post.
So right now, I have it as:
Status has_many :posts
Post belongs_to :status, counter_cache: true
But whenever I want to assign a status to a post, I have to do it backways and it feels weird.
i.e. I have to do something like:
> g = Post.second
Post Load (0.7ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1 OFFSET 1
=> #<Post id: 19, title: "10PP gives you 1 on 1", photo: "1-on-1-Icon.jpg", body: "10PP gives you the real one on one attention you c...", created_at: "2014-08-30 10:48:18", updated_at: "2014-08-30 10:48:18", user_id: 1, ancestry: nil, file: nil, status_id: nil>
2.1.1p76 :010 > half = Status.second
Status Load (0.5ms) SELECT "statuses".* FROM "statuses" ORDER BY "statuses"."id" ASC LIMIT 1 OFFSET 1
=> #<Status id: 2, name: "half", created_at: "2014-08-28 08:04:42", updated_at: "2014-08-28 08:04:42", posts_count: nil>
2.1.1p76 :011 > half.posts << g
(0.1ms) BEGIN
SQL (0.3ms) UPDATE "posts" SET "status_id" = $1, "updated_at" = $2 WHERE "posts"."id" = 19 [["status_id", 2], ["updated_at", "2014-08-30 11:37:16.121245"]]
SQL (0.4ms) UPDATE "statuses" SET "posts_count" = COALESCE("posts_count", 0) + 1 WHERE "statuses"."id" = 2
(0.9ms) COMMIT
Post Load (0.8ms) SELECT "posts".* FROM "posts" WHERE "posts"."status_id" = $1 [["status_id", 2]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Post id: 19, title: "10PP gives you 1 on 1", photo: "1-on-1-Icon.jpg", body: "10PP gives you the real one on one attention you c...", created_at: "2014-08-30 10:48:18", updated_at: "2014-08-30 11:37:16", user_id: 1, ancestry: nil, file: nil, status_id: 2>]>
I would rather be going the other way, i.e. assigning a status to the post.
I feel like this is not the best way to do it, but not sure how else to tackle it.
Also, how would I tackle the PostsController#Create?
Right now, I have just:
#post = current_user.posts.new(post_params)
Which doesn't assign the correct Status object to the current post.
you could try other way with using enum http://edgeguides.rubyonrails.org/4_1_release_notes.html#active-record-enums
class Post < ActiveRecord::Base
enum status: [ :empty, :half, :full ]
end
Post.full
Post.first.empty?

'name' AR attribute returning class name and id not working

I have a User object and getting name like this is fine:
>u=User.find(1)
>u.name
>jt
but it has an association with an object that when I get the user back it returns the name of the class:
oc=ObjectConnection.find(1)
oc.user.name
> User
and the id is giving me an error:
ruby-1.9.2-p290 :063 > oc.user.id
NoMethodError: User Load (0.4ms) SELECT id, name FROM `users` WHERE (id=1)
undefined method `id' for [#<User id: 1, name: "jt">]:ActiveRecord::Relation
from /Users/jt/.rvm/gems/ruby-1.9.2-p290/gems/activerecord-3.1.0/lib/active_record/relation.rb:459:in `method_missing'
from (irb):63
from /Users/jt/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0/lib/rails/commands/console.rb:45:in `start'
from /Users/jt/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0/lib/rails/commands/console.rb:8:in `start'
from /Users/jt/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.1.0/lib/rails/commands.rb:40:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
ruby-1.9.2-p290 :064 >
The classes are:
class ObjectConnection < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :object_connections
end
What is going on? This seems really simple.
thx
I have a nearly identical relationship between Contact and Company (Contact belongs_to company, company has_many contacts.) Here's my rails console (Rails 3.2, Ruby 1.9.3).
1.9.3p125 :019 > company = Company.find(1)
Company Load (2.7ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT 1 [["id", 1]]
=> #<Company id: 1, name: "Acme Corp", created_at: "2012-03-20 17:49:44", updated_at: "2012-03-20 17:49:44">
1.9.3p125 :020 > contact = Contact.find(1)
Contact Load (1.8ms) SELECT "contacts".* FROM "contacts" WHERE "contacts"."id" = $1 LIMIT 1 [["id", 1]]
=> #<Contact id: 1, first: "Tom", last: "Harrison", email: "foo#example.com", created_at: "2012-03-12 19:11:57", updated_at: "2012-03-20 17:56:37", birthdate: "1962-02-26", company_id: 1>
1.9.3p125 :021 > contact.company.name
Company Load (0.7ms) SELECT "companies".* FROM "companies" WHERE "companies"."id" = 1 LIMIT 1
=> "Acme Corp"
Perhaps there's some case where "name" was used in an earlier version of AREL? Also, notice the odd SQL syntax ... WHERE (id=1) ... What happens if you look for a different attribute of ObjectConnection?
This is an identical situation, isn't it? Perhaps the version of Rails? Ruby? Seems implausible, but the answer is "You're doing it right".

Resources