Scaling large data in Rails4 - ruby-on-rails

I'm making a Q&A site. I have three models for now:
Question
Answer
User
Both Question and Answer can be voted for, so I need a way to store who voted on the model, to prevent multiple votes on one model by same user.
Would be a good idea to create a vote model? Where either the question_id or the answer_id will be empty in each. I'm afraid that this will create a lot of junk and slow the application down.
My other idea is to store the user_id in a hash in the Answer and in the Question.
If the user is already present in the hash it prevents voting. Or store the answer and question ids in the user.
What would be the Rails4 way to be fast but store most user interactions in models. What can be KISS, DRY and fast?

You should use a polymorphic model if you have multiple models the Vote can refer to.
class Vote < ActiveRecord::Base
belongs_to :voteable, polymorphic: true
end
class Question < ActiveRecord::Base
has_many :votes, as: :voteable
end
class Answer < ActiveRecord::Base
has_many :votes, as: :voteable
end
I don't think you have to worry about the speed for quite a while if you use indexes on the associations. Just put this snippet in a migration and you should be good to go!
add_index :votes, [:voteable_name, :voteable_id]

Related

Rails: set up polymorphic associations for two user types

I'm starting to code a linkedin-like website in Rails for my thesis that would enable members to post job offers (employers) and other members (employees) to respond to them.
I've been trying to wrap my head around the data model and associations - because I don't want to have two channels of authentication for emploYers and Yees - I want to keep email, nickname and password in one table (model) and use that for logging in, and then from there go to Employer and Employee specific data.
I was reading about STI but I figured that Yer has very different data than Yee and there would be a lot of nulls in the Users table, which isn't quite an optimal thing I suppose.
Then I've stumbled across polymorphic associations, but I don't really know how to set them up.
I was thinking of something like this:
User < ApplicationRecord
has_one :employer, polymorphic: true
has_one :employee, polymorphic: true
end
Employee < ApplicationRecord
end
Employer < ApplicationRecord
end
My head boils cause I don't know what's the best way to achieve this, any tips much appreciated,
cheers
You don't need to use the polymorphic association here.
User < ApplicationRecord
has_one :employer
has_one :employee
def employer?
employer.present?
end
def employee?
employee.present?
end
end
Not sure if this would be the best solution in your case though.

Persist array from before_save callback for ActiveModel::Dirty like change tracking on has_many through relationship

Firstly, apologies for the snappy question title! It does however sum up what I am trying to do.
I've been using ActiveModel::Dirty successfully to create a kind of audit trail on various model attributes within my app (like on Product below).
I've now have a fairly pressing request to be able to track the changes (additions & deletions in this case) on an associated has_many through relationship.
The models in question are:
class Product < ActiveRecord::Base
has_many :products_territories, :dependent => :destroy
has_many :territories, :through => :products_territories
end
class Territory < ActiveRecord::Base
has_many :products_territories
has_many :products, :through => :products_territories
end
class ProductsTerritory < ActiveRecord::Base
belongs_to :territory
belongs_to :product
end
I've failed with using ActiveModel::Dirty, it doesn't seem possible, so am trying my own thing which on the surface is quite simple; grab an array of a product's products_territories before_save and then again after_save and then perform comparisons on the two arrays to identify the additions and deletions. What I can't get my head around is the best way to persist the array of products_territories from the before save so it's then available to my after_save callback. I'm pretty certain ## class variables aren't the way to go and i'm also not so sure about session variables. I'm wondering whether something like Redis or Memchached is what I should be looking at?
Can anyone that's had to do something similar to this give me any pointers or direct me to some further reading please?
Thanks in advance.

How to create voting to a multiple choice application using ruby on rails

I am working on a multiple choice question and answer application using Ruby on Rails and I have the following model.
class User < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :user
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :answer
end
My problem is a user can choose all the answers.
How can I fix it so that it updates to the new answer?
For example, a has 5 votes and b 3 votes.
User clicks on a and a increments to 6 votes, same user comes back and clicks on b, a decrements back to 5 votes and b increments to 4 votes.
My first guess is that I need to add another model for example, user_choice with user_id and vote_id to track the previous answer.
You have your answer within the models. Just add a point system to the question model.
Under the question model,
def points
self.answers.count
end
Or, if answers have different values attached to them, make points an attribute of the answer instances.
def points
self.answers.pluck(:points).inject(:+)
end
This will sum up all the points from an answer belonging to a question, which allows you to order the questions by point count.
However, I am assuming you will need a Choice model for a question, unless that is in fact what the votes are for.
question has_many choices
choices belong_to question, etc.
I'm not sure what you mean when you say:
How can I fix it so that it updates to the new answer?
EDIT
Er, okay so you just need exactly what I mentioned earlier. A vote is really just the number of times an answer has been chosen.
If your problem is that users can choose more than 1 answer, it is likely that you should implement view based validations via javascript, or better yet, just disable their ability to select multiple choices, which if you were using a select tag, is actually the default. You must've added
multiple: true
in the select tag options for the user to select multiple entries.
On the backend, you can do this:
class Choice < ActiveRecord::Base
validates_uniqueness_of :user_id, scope: [:question_id, :answer_id]
end

Rails 2 tables, 1 model

I am relatively new to ruby/rails and I have the following question:
I am working on a scheduling app and have a model named Classes and another named ClassEntries. The relationship between them is that each user can have multiple class entries per semester, each relating to one class. Each record in the Classes table belongs to a specific University. A User can have multiple entries in the ClassEntries table for 1 semester (typically 5). Their schedule is comprised of all their ClassEntries with the same semester ID.
I am not sure whether I should have a third model called Schedule that brings together the info in the ClassEntries and Classes models for the user at hand. I originally wrote this functionality in PHP and I simply used a MySQL JOIN to gather the necessary information. In Rails it seems that there should be a better way to accomplish this.
What would be the best way of going about this in Rails?
Many thanks
So, what you are looking for is pretty much associations in Rails.
You would have the following:
def User < ActiveRecord::Base
has_many :course_entries
has_many :courses, :through => :class_entries
end
def CourseEntry < ActiveRecord::Base
belongs_to :user
belongs_to :course
end
def Course < ActiveRecord::Base
has_many :course_entries
has_many :users, :through => :class_entries
end
With those associations set up, Rails would allow you to do such things like
some_user.courses or some_course.users and it will make the joins through CourseEntry for you.
Let me know if this helps. If you need me to go more in depth let me know.

How to save an array of models in Rails

I've got models called answers, surveys, questions. Now in a survey, there can be up to 200 questions and so one survey can generate up to 200 answer-models in a page.
The question is: How can I save the array of answers I have in a single db-action and not iterate over the array and save each element individually, which takes a lot of time relatively?
You can pass the 'belongs_to' relationship an :autosave symbol. This will cause the answers to be automatically saved when you save the parent. Something like this then would probably be what you want:
class Survey < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :survey, :autosave
has_one :answer
end
class Answer < ActiveRecord::Base
belongs_to :question, :autosave
end
I don't know how exactly this will perform behind the scenes, but it will allow ActiveRecord to optimise the SQL and removes the need for you to iterate explicitly over the relationships.
No matter what you do, don't forget to wrap your multiple inserts in a transaction. Will really speed things up.

Resources