Cannot Get Counter_Cache to update properly in Padrino (Rails) - ruby-on-rails

I have a Padrino app I am building where I want to use counter_cache. I am using ActiveRecord as my ORM. In my Repository model, I want to keep a count of the number of contributions that are associated with a given repository. Here are the relevant models:
class Repository < ActiveRecord::Base
has_many :contributions, autosave: true
has_many :users, through: :contributions
validates :full_name, presence: true, uniqueness: true
end
class Contribution < ActiveRecord::Base
belongs_to :user
belongs_to :repository, counter_cache: true
end
class User < ActiveRecord::Base
has_many :contributions
has_many :repositories, through: :contributions
validates :username, presence: true, uniqueness: true
end
The schema is as follows:
create_table "contributions", :force => true do |t|
t.integer "user_id"
t.integer "repository_id"
end
create_table "repositories", :force => true do |t|
t.string "full_name"
t.integer "contributions_count", :default => 0
end
create_table "users", :force => true do |t|
t.string "username"
end
I have created a test in Rspec to check to see that the contributions_count is updating properly. However, I cannot get it to pass. Here's the spec:
describe "when a new contribution is created" do
it "updates the counter cache" do
repo = Repository.create(full_name: "sample_repo")
user = User.create(username: "sample_user")
expect {
Contribution.create(user: user, repository: repo)
}.to change {repo.contributions_count }.by(1)
end
end
When I run the spec, I get the following failure:
1) Repository when a new contribution is created updates the counter cache
Failure/Error: expect {
result should have been changed by 1, but was changed by 0
# ./spec/models/repository_spec.rb:43:in `block (3 levels) in <top (required)>'
I have also tried creating Contributions in the console and it is not updating the repository counter_cache. I've tried a bunch of stuff, but can't seem to figure out how to get it to work properly. Any help would be appreciated. Thanks.

I think this is because your repo object isn't being magically updated by Ruby when the Contribution object is being saved to the database. You will need to reload the information from the database:
repo.reload.contributions_count

Related

ActiveModel::UnknownAttributeError in rails 5

I'm trying to make a reddit-like website, so I've created a Forum model which should contain posts, hence I also created a Post model which I want to be a Forum child. My code for this idea is:
forum.rb
class Forum < ApplicationRecord
has_many :posts
validates :name, presence: true,
length: { minimum: 2 }
end
post.rb
class Post < ApplicationRecord
validates :title, presence: true,
length: { minimum: 5 }
validates :text, presence: true,
length: { minimum: 5 }
belongs_to :forum
end
The corresponding migrations are:
create_forums.rb
class CreateForums < ActiveRecord::Migration[5.1]
def change
create_table :forums do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
create_posts.rb
class CreatePosts < ActiveRecord::Migration[5.1]
def change
create_table :posts do |t|
t.string :title
t.text :text
t.references :forum, index: true, foreign_key: true
t.timestamps
end
end
end
My problem arise when I try to create a new post given a certain forum. For example, if I run #forum.posts.create(title: "Title", text: "Body") I get ActiveModel::UnknownAttributeError in Posts#new with the description unknown attribute 'forum_id' for Post.
What is going on?
Have you run your migrations after generating them? ( rake db:migrate )
The only way I can get this error to occur in testing is to remove the forum reference/relationship field from the posts table and then try and create a post related to the forum. When the migration is run with the t.references :forum, index: true, foreign_key: true the command runs perfectly for me.
If you added the reference line after running the migration, you could reset your database by running rake db:drop db:create db:migrate and you should be good to go since you have it in the table creation migration file, but it is worth noting that if you want to add or remove columns or modify your database, you should be creating new migrations for this instead of running through the drop/create process.
Have you migrated your db? If no, then rails db:migrate
and then reset your db: rails db:setup
That should fix the issue.

Associating two models in a way both models' attributes are always pulled together in any view

I'm building a tech-specific pricegrabber-like web app, and I have a model that carries params that are common in all products. This model is called Product. Then I have one model for each type of product that I'm going to work with, for example, I'm now trying to build the first specific model, which is Videocard. So, the Product model always must have one Specific model, in this case Product-Videocard.
At this moment I'm stuck finding a way to make a product and a specific model always come tied together whenever I reach to them, be it in an index view, show view, form_for, a search, etc. But I can't picture in my head how a form will create an item and its specifications and insert a foreign key into another model with only one submit request.
Below are both models and the migrations for each:
class Product < ApplicationRecord
#belongs_to :productable, :polymorphic => true
has_one :videocard, dependent: :destroy
# Comment for this Stackoverflow question: the way I'm thinking I
# should have to make tons of has_one associations, for the other
# products. Is there a DRY way to do this?
has_many :prices, through: :stores
validates :platform, presence: { message: "should be specified." }
validates :name, presence: { message: "should be specified." }
validates_associated :videocard
end
class Videocard < ApplicationRecord
belongs_to :product
end
Migrations (shortened to make this question as clear as possible):
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.references :productable, polymorphic: true, index: true
t.string :name
t.string :image
t.string :partnum
t.string :manufacturer
t.string :platform #mobile, desktop, server, laptop
t.timestamps
end
end
end
class CreateVideocards < ActiveRecord::Migration[5.0]
def change
create_table :videocards do |t|
t.references :product, index: true
t.integer :memory
t.string :interface
# [...lots of attributes...]
t.integer :displayport
t.integer :minidisplayport
t.integer :tdp
t.timestamps
end
end
end
Also how can I make it so that Product only needs one has_one association, instead of using multiple ones. Remember that Videocard will have one type of specification, Memory will have other, and so on.

Trouble with self referential model in Rails

I have a model named User and I want to be able to self reference other users as a Contact. In more detail, I want a uni-directional relationship from users to other users, and I want to be able to reference an owned user of one user as a 'contact'. ALSO, i want to have information associated with the relationship, so I will be adding fields to the usercontact relation (I just edited this sentence in).
I attempted to do this while using the answer to this question as a guide.
Here is the User model:
user.rb
class User < ActiveRecord::Base
attr_accessible(:company, :email, :first_name, :last_name,
:phone_number, :position)
has_many(:user_contacts, :foreign_key => :user_id,
:dependent => :destroy)
has_many(:reverse_user_contacts, :class_name => :UserContact,
:foreign_key => :contact_id, :dependent => :destroy)
has_many :contacts, :through => :user_contacts, :source => :contact
end
I also created the model UserContact as a part of connecting contacts to users:
usercontact.rb
class UserContact < ActiveRecord::Base
belongs_to :user, :class_name => :User
belongs_to :contact, :class_name => :User
end
Here is the create_users.rb migration file i used:
create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :phone_number
t.string :email
t.string :company
t.string :position
t.timestamps
end
end
end
And here is the create_users_contacts.rb migration:
create_users_contacts.rb
class CreateUsersContacts < ActiveRecord::Migration
def up
create_table :users_contacts, :force => true do |t|
t.integer :user_id, :null => false
t.integer :contact_id, :null => false
t.boolean :update, :null => false, :default => false
end
# Ensure that each user can only have a unique contact once
add_index :users_contacts, [:user_id, :contact_id], :unique => true
end
def down
remove_index :users_contacts, :column => [:user_id, :contact_id]
drop_table :users_contacts
end
end
However, for reasons unknown to me, I believe something has gone awry in the linking since on my users index page, I have a column using <td><%= user.contacts.count %></td>, but I get this error from the line when I attempt to load the page:
uninitialized constant User::UserContact
I think the issue may be something to do with the fact that I want to name users associated with another user as contacts, because I cannot find other examples where that is done, and as far as I can tell I am doing everything properly otherwise (similarly to other examples).
The closest similar problem that I found was outlined and solved in this question. The issue was incorrect naming of his connecting model, however I double checked my naming and it does not have that asker's problem.
Any help is appreciated, let me know if any other files or information is necessary to diagnose why this is occurring.
EDIT
After changing usercontact.rb to user_contact.rb, I am now getting this error:
PG::Error: ERROR: relation "user_contacts" does not exist
LINE 1: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "...
^
: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "users"."id" = "user_contacts"."contact_id" WHERE "user_contacts"."user_id" = 1
EDIT TWO
The issue was that my linking table, users_contacts, was misnamed, and should have been user_contacts! so I fixed it, and now it appears to work!!
You need to rename your usercontact.rb to user_contact.rb
This is naming convention rails autoload works with.

Rails ActiveRecord trouble

Please help with ActiveRecord testing. Trying my first Rails 3.1.0 project. There I have model named "Account", described like:
migration.rb:
def self.up
create_table :accounts do |t|
t.string :name
t.integer :type
t.references :user
t.timestamps
end
add_index :accounts, :user_id
end
account_model.rb
class Account < ActiveRecord::Base
belongs_to :user
validates_length_of :name, :within => 15..255
validates_numericality_of :type
end
And if i'm making in Rspec :
account = Account.new(:type => 1)
account.type.should == 1
I've got test result:
Failure/Error: account.type.should == 1
expected: 1
got: nil (using ==)
I tried Account creation in console, and every time i'm assigning any integer value as 'type', i got 'nil'. Not assigned value. What I'm making wrong?
'type' is a protected attribute in rails, because .type is a ruby method. Hence you can't mass assign it. Rename the attribute (eg :account_type) & everything should work fine.

ActiveRecord Validations for Models with has_many, belongs_to associations and STI

I have four models:
User
Award
Badge
GameWeek
The associations are as follows:
User has many awards.
Award belongs to user.
Badge has many awards.
Award belongs to badge.
User has many game_weeks.
GameWeek belongs to user.
GameWeek has many awards.
Award belongs to game_week.
Thus, user_id, badge_id and game_week_id are foreign keys in awards table.
Badge implements an STI model. Let's just say it has the following subclasses: BadgeA and BadgeB.
Some rules to note:
The game_week_id fk can be nil for BadgeA, but can't be nil for BadgeB.
Here are my questions:
For BadgeA, how do I write a validation that it can only be awarded one time? That is, the user can't have more than one -- ever.
For BadgeB, how do I write a validation that it can only be awarded one time per game week?
Data model:
In my comprehension, here is your data model (click to enlarge):
Data model http://yuml.me/6afcad62
Migration:
The migration will let you meet your second requirement, at the migration level:
class CreateAwards < ActiveRecord::Migration
def self.up
create_table :awards do |t|
# custom attributes here
t.string :name
t.text :description
t.references :user, :null => false
t.references :game_week#, :null => false
t.references :badge, :null => false
t.timestamps
end
# a user can be awarded no more than a badge per week
add_index :awards, [:user_id, :badge_id, :game_week_id], :unique => true
# a user can be awarded no more than a badge for ever
#add_index :awards, [:user_id, :badge_id], :unique => true
end
def self.down
drop_table :awards
end
end
Model:
The model will let you meet both your requirements, at the model level:
class Award < ActiveRecord::Base
validate_uniqueness_of :user, :badge,
:if => Proc.new { |award| award.badge === BadgeA }
validate_uniqueness_of :user, :badge, game_week,
:unless => Proc.new { |award| award.badge === BadgeA }
#validate_uniqueness_of :user, :badge, game_week,
# :if => Proc.new { |award| award.badge === BadgeB }
end
Note:
I didn't try these snippets, but I think that the idea is here :)

Resources