Why vote is Nil class? - ruby-on-rails

Ok guys, these is my first attempt to code without any tutorials or examples, so may be I'm doing something terribly stupid.
I have a Rating modes, which parent to Party, which parent to Vote. I did't generated scaffold for vote, just model, as I suppose generally I don't need to have all standart controller or view here. I try to add simple voting system (I know about gems, but for the sake of practice wanna do it by myself)
So my Party model:
class Party < ActiveRecord::Base
belongs_to :rating
has_one :vote
end
end
Vote model:
class Vote < ActiveRecord::Base
attr_accessible :negative, :positive, :party_id
belongs_to :party
#def self.build(party_id)
#return Vote.new(:party_id=>party_id)
#end
end
Parties_controller:
def create
#rating = current_rating
#party = #rating.parties.build(:rating_id => #rating_id)
##vote = Vote.add_voting(#party.id)
#vote = #party.Vote.build(:party_id=>#party.id)
commented part is one of tries to do pretty same thing.
And if in view I ask for class:
<% #rating.parties.each do |item| %>
<p><%= item.name %></p>
<p><%= item.vote.class %></p>
<% end %>
It shows 'nilClass'
Why?

This syntax is invalid:
#party.Vote.build(:party_id => #party.id)
Use this instead:
#vote = #party.build_vote
There is no need to assign party_id. The build_vote method does this for you.
See explanation on Rails Guides.

#positive_votes = Vote.find_by_positive(params[:user_uid, :party_id])
find_by_positive expects to search the "positive" column in your db, but you are passing in a user_id and party_id. Try this:
#positive_votes = Vote.find_by_user_uid_and_party_id(params[:user_uid], params[:party_id])

Related

Rails 4: Assigning a records "has many" attribute without database saving

I have a couple models shown below and I'm using the search class method in Thing to filter records
class Category << ActiveRecord::Base
has_many :thing
end
class Thing << ActiveRecord::Base
belongs_to :category
:scope approved -> { where("approved = true") }
def self.search(query)
search_condition = "%" + query + "%"
approved.where('name LIKE ?', search_condition)
end
end
It works fine in my Things controller. The index route looks like so:
def index
if params[:search].present?
#things = Thing.search(params[:seach])
else
#thing = Thing.all
end
end
On the categories show route I display the Things for this category. I also have the search form to search within the category.
def show
#category = Categories.find(params[:id])
if params[:search].present?
#category.things = #category.things.search()
end
end
So the problem is that the category_id attribute of all the filtered things are getting set to nil when I use the search class method in the categories#show route. Why does it save it to database? I thought I would have to call #category.save or update_attribute for that. I'm still new to rails so I'm sure its something easy I'm overlooking or misread.
My current solution is to move the if statement to the view. But now I'm trying to add pages with kaminiri to it and its getting uglier.
<% if params[:search].present? %>
<% #category.things.search(params[:search]) do |thing| %>
... Show the filtered things!
<% end %>
<% else %>
<% #category.things do |thing| %>
... Show all the things!
<% end %>
<% end %>
The other solution I thought of was using an #things = #categories.things.search(params[:search]) but that means I'm duplicated things passed to the view.
Take a look at Rails guide. A has_many association creates a number of methods on the model to which collection=(objects) also belongs. According to the guide:
The collection= method makes the collection contain only the supplied
objects, by adding and deleting as appropriate.
In your example you are actually assigning all the things found using #category.things.search() to the Category which has previously been queried using Categories.find(params[:id]).
Like Yan said, "In your example you are actually assigning all the things found using #category.things.search() to the Category which has previously been queried using Categories.find(params[:id])". Validations will solve this problem.
Records are being saved as nil because you have no validations on your model. Read about active record validations.
Here's the example they provide. You want to validate presence as well because records are being created without values.
class Person < ActiveRecord::Base
validates :name, presence: true
end
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

How to store the order of items when using shuffle method

I've built an online examination app. I have some models like:
Question has many answers
Answer belongs to question
Exam
I built the form for user can do exam using form_tag. Currently, I used this code to shuffle the order of question's answers in exam in the view:
<% question.answers.shuffle.each do |answer| %>
...
<% end %>
With above code, every time exam was showed, it had the different order of answers. Now I want to store the order of answers, so I can review exam later. I'm thinking of creating another model to store the order, but I don't know how to get the order from shuffle method.
So I want to ask a way to store the order of answers in an exam that will help me can review the question with right order of answers in exam which user taken. Can someone give me an idea or solution?
Update the model to store user's answers
class ExamAnswer
belongs_to :exam
belongs_to :question
belongs_to :answer
end
This model has columns: exam_id, question_id, user_answer_id
# in app/models/question.rb
def answers_ordered(current_user)
answers_ordered = question.answers.shuffle
exam_answer = self.exam_answers.where(:user_id => current_user.id, :question_id => self.id, :exam_id => self.exam_id).first
if exam_answer.nil?
exam_answer.user_id = current_user.id
exam_answer.question_id = self.id
exam_answer.exam_id = self.exam_id
end
exam_answer.order_answers = answers_ordered.map{&:id} # add .join(';') if your sgdb does not handle the Array type
exam_answer.save
answers_ordered
end
# in your app/models/exam_answer.rb
class ExamAnswer
belongs_to :user
belongs_to :exam
belongs_to :question
belongs_to :answer
# add the field order_answers as type Array or String, depending of your SGBD
end
# in your view
<% question.answers_ordered(current_user).each do |answer| %>
...
<% end %>
And then, when you need the order, you can access it through question.exam_answer.order_answers. I think I'd do something like this. Delete what you don't need in the answers_ordered method.

Rails Design Question

I have a Post Model, and a comment Model. I would like to limit the comments to 3. How would you do this?
Would it be best to create a validation? If so what would this look
like?
Would you do this in the view unless Post.comments == 3?
Would a callback make sense?
the post's comments count validation is the "Comment" model responsibility, so I'd like to suggest the following code:
class Comment < ActiveRecord::Base
belongs_to :post
before_create :limit_number
private
def limit_number
if post.comments.count >= 3
errors.add(:post, "the limit of comments is over")
end
end
end
class Post < ActiveRecord::Base
has_many :comments
end
You should always validate at the model level as well as the views.
class Comment < ActiveRecord::Base
belongs_to :post
validate :check
private
def check
if post.present?
errors.add("Post", "can not have more than 3 comments") if post.comments.size >= 3
end
end
end
class Post < ActiveRecord::Base
# other implementation...
def commentable?
comments.size <= 3
end
end
Then just call #commentable? in your views like this. You should never hard-code values in the views.
<% if #post.commentable? %>
<%= render "form" %>
<% end %>
I would make sure not to let them submit that fourth comment that you don't want to allow. Some might say you should do the check in your controller and pass a flag, but for something this simple the check in the view seems fine.
Validate it in the models. You could use validates_with as described here.
In the view, you'd be better off checking with an inequality like
unless Post.comments.length >= 3
show_form
end
That way if you have four comments for some reason (race condition or an admin posts a response after 3, etc.) the form stils won't show up.

No foreign key in new scaffold

Good day... I have huge trouble with this and it drives me insane.
My user creation should have some additional data, like this:
<div class="field"><%= label_tag 'species', %>
<%= f.collection_select :species_id, Species.all, :id, :name %></div>
It displays the list of species correctly, yes, but when I do a submit, it is utterly ignored. Not even the number appears in the table of the database, it just disappears. I have put the correct associations:
class Species < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
# ... Other stuff
belongs_to :species
# ... Other stuff
end
I have also tried manipulating the controller:
class UsersController < ApplicationController
def create
logout_keeping_session!
#user = User.new(params[:user])
#user.species = Species.find(params[:species_id])
# Other stuff
end
end
But that only gives me 'Cannot find Species without id' even though the params array contains an element 'species_id' with a value.
I am at the end of my wits. Quite new to this, but is this RESTful? Not to find out how to get things done that seem easy? I love Rails and would like to continue.
Thanks for listening
your find fails because the params is probably: params[:user][:species_id] but if it is there like it is supposed, it should be set already, too.

How do I so a select input for a STI column in a Rails model?

I have a model with single-table inheritance on the type column:
class Pet < ActiveRecord::Base
TYPES = [Dog, Cat, Hamster]
validates_presence_of :name
end
I want to offer a <select> dropdown on the new and edit pages:
<% form_for #model do |f| %>
<%= f.label :name %>
<%= f.text_input :name %>
<%= f.label :type %>
<%= f.select :type, Pet::TYPES.map { |t| [t.human_name, t.to_s] } %>
<% end %>
That gives me the following error:
ActionView::TemplateError (wrong argument type String (expected Module))
I read a suggestion to use an alias for the field #type since Ruby considers that a reserved word that's the same as #class. I tried both
class Pet < ActiveRecord::Base
...
alias_attribute :klass, :type
end
and
class Pet < ActiveRecord::Base
...
def klass
self.type
end
def klass=(k)
self.type = k
end
end
Neither worked. Any suggestions? Oddly, it works fine on my machine (MRI 1.8.6 on RVM), but fails on the staging server (MRI 1.8.7 not on RVM).
The key difference between ryan bates' "suggestion" (who knows more about rails than most) and your implementation is the suggestion used the direct access to the attributes via the brackets ("self[:type] = ") versus your implement that uses method calling ("self.type = ")
So try something like:
class Pet < ActiveRecord::Base
...
def klass
self[:type]
end
def klass=(k)
self[:type] = k
end
end
You could also try to change the column name used with STI for something different from type.
According to railsapi.com, it can be set in subclasses, so just add the following in your pet model:
self.inheritance_column = "type_id"
I'm just guessing... so I'm sorry this is totally wrong.
If you absolutely have to do this, I'd do something along the lines of this in your controller:
#pet = Pet.new(params[:pet])
#pet[:type] = params[:pet][:type]
IMO, you're better off being both explicit and painful when trying to do this, as changing types on the fly like that feels like a really bad idea.

Resources