Rails 4 - Making profile pages sharable - ruby-on-rails

SOLUTION:
I changed this line of code and now it displays perfectly.
<% if answer.user == current_user %>
to
<% if answer.user == #user %>
I posted a similar question last night but this is a new one.
I am building a question/answer app. The questions are static and provided, not user generated. The data is being accessed via a nested relationship.
On the individual user page, I am showing the questions/answers for that specific user using the code below. It is working great. Unfortunately, I want users to be able to share their profile pages and with the way things are set up, users can only see their own stuff, not that of others. What do I need to change so that only the data for user 1 is displayed on /users/1 but user 2 can view it?
I am assuming it will have something to do with using params to find the user vs. current_user, but I'm not sure how to set it up.
Here is the code in my show.view.
<% #user.questions.uniq.each do |question| %>
<h3><div class="answer"><%= question.question %> </div></h3>
<% question.answers.each do |answer| %>
<% if answer.user == current_user %>
<div class="answer"><%= answer.answer %> <%= answer.created_at.strftime("(%Y)") %></div>
<% end %>
<% end %>
<% end %>
My models:
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
class Question < ActiveRecord::Base
has_many :answers
has_many :users, through: :answers
end
class User < ActiveRecord::Base
has_many :answers
has_many :questions, through: :answers
end
My Routes:
resources :users do
resources :answers
end
I'm happy to provide any other code that would be helpful. Thanks!

Okay, then you need to have in your show method in user controller something like this:
#answers_to_one_question = Answer.all.where("question_id = ?", question_id)
or
#answers_to_one_question = User.all.answers.where("question_id = ?", question_id)
And in the show view something like this:
<%= render partial: "answer", collection: #answers_to_one_question, as: answer %>
I didn't test this code, but the logic is like this.

Related

Simple_form_for many to many with validation

Setup
I have a simple many to many relationship between a Submit and an Answer through SubmitAnswer.
Answers are grouped by a Question (in my case each question has three answers) - think of it as a multiple choice quiz.
I have been trying to use SimpleFormFor to make a form which renders a predetermined set of questions, where each question has a predetermined set of answers.
Something like this:
#form
<%= simple_form_for Submit.new, url: "/questionnaire" do |f| %>
<% #questions.each do |question| %>
<%= f.association :answers, collection: question.answers %>
<% end %>
<%= f.submit :done %>
<% end %>
#controller
def create
#submit = Submit.new(submit_params)
#submit.user = current_user
if #submit.save
redirect_to root_path
else
render :new
end
end
def submit_params
params.require(:submit).permit(answer_ids: [])
end
When I submit the form, Rails creates the join table, SubmitAnswers, automatically.
So here is the crux of the matter: Whats the easiest way to re-render the form, errors and all, if not all questions have been answered, ie if #submit.answers.length != #question.length ?
I can add a custom error with errors.add(:answers, 'error here'), but when I re-render, the correctly selected answers arent repopulated, which is suboptimal.
For completions sacke, here are my models:
class Submit < ApplicationRecord
belongs_to :user
has_many :submit_answers
has_many :answers, through: :submit_answers
end
class SubmitAnswer < ApplicationRecord
belongs_to :submit
belongs_to :answer
end
class Answer < ApplicationRecord
has_many :submit_answers
has_many :submits, through: :submit_answers
end
Alright, after some digging we did find the answer to make the form work, albeit with more pain that we anticipated a simple many-to-many should take.
#model
class Submit < ApplicationRecord
belongs_to :user
has_many :submit_answers
has_many :answers, through: :submit_answers
accepts_nested_attributes_for :submit_answers
end
#controller
def new
#submit = Submit.new
#questions.count.times { #submit.submit_answers.build }
end
def create
#submit = Submit.new(submit_params)
#submit.user = current_user
if #submit.save
redirect_to root_path
else
render :home
end
end
def submit_params
params.require(:submit).permit(submit_answers_attributes:[:answer_id])
end
#form
<%= simple_form_for #submit do |f| %>
<%= f.simple_fields_for :submit_answers do |sa| %>
<%= sa.input :answer_id, collection: #answers[sa.options[:child_index]], input_html: { class: "#{'is-invalid' if sa.object.errors.any?}"}, label: #questions[sa.options[:child_index]].name %>
<div class="invalid-feedback d-block">
<ul>
<% sa.object.errors.full_messages.each do |msg| %>
<li> <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.submit :done %>
<% end %>
The solution is to use simple_fields_for/fields_for. Note that <%= sa.input :answer_id %> must be :answer_id, not :answer, which is something I had tried before.
Also one must allow accepts_nested_attributes_for :submit_answers, where :submit_answers is the join_table.
I prebuild my SubmitAnswers like so: #questions.count.times { #submit.submit_answers.build } which generates an input field for each question, all of which get saved on the form submit, a la build.
For the strong_params one needs to permit the incoming ids:
params.require(:submit).permit(submit_answers_attributes:[:answer_id]), so in this case submit_answers_attributes:[:answer_id].
For anyone wondering what the params look like:
{"authenticity_token"=>"[FILTERED]",
"submit"=>
{"submit_answers_attributes"=>
{"0"=>{"answer_id"=>""}, "1"=>{"answer_id"=>""}, "2"=>{"answer_id"=>""}, "3"=>{"answer_id"=>""}, "4"=>{"answer_id"=>""}, "5"=>{"answer_id"=>""}, "6"=>{"answer_id"=>""}}},
"commit"=>"done"}
As for the errors, im sure there might be a better way, but for now I have just manually added them with input_html: { class: "#{'is-invalid' if sa.object.errors.any?}"}.
On a final note, the sa.object # => SubmitAnswer allows me to retrieve the Model, the errors of that Model or whatever else one might want.

Rate a post only once per user in rails

I have trouble in allowing users to rate a post. My task is to enable the user to rate a post only once. On the show page, the post I have includes radio buttons for rating. If the user tries to rate for the second time it needs to update the previous rating done by the user for the same post. The issue I am facing is that user is able to rate a post multiple times. How to resolve this?
User model:
class User < ApplicationRecord
has_many :posts
has_many :ratings
end
Post model:
class Post < ApplicationRecord
has_many :ratings
belongs_to :user
end
Ratings model
class Rating < ApplicationRecord
belongs_to :post
belongs_to :user
end
In the post controllers i have used nested attributues for ratings.
def show
#post = #topic.posts.find(params[:id])
#rate = #post.ratings.all
#rate = Rating.where(post_id: #post.id).group("rate").count
end
private def post_params
params.require(:post).permit(:title, :body, ratings_attributes: [:rate])
end
The show page of post include the creating of rating using <fieldset>:
<%= form_for [#topic, #post] do |f| %>
<%= f.fields_for :ratings, #post.ratings.build do |builder| %>
<fieldset>
<% for i in 1..5 %>
<%= builder.radio_button :rate, i %><%= i %>
<% end %>
</fieldset>
<% end %>
<%=f.submit "Rate" %>
<% end %>
First, add validation to the Rating to enforce uniqueness on the combination of user and post. This will stop a duplicate rating ever being created.
validates_uniqueness_of :post_id, scope: :user_id
Then, in the action that saves the rating, first check if there is a record that can be updated, else create a new one.
#rating = Rating.find_or_initialize_by(user: #user, post: #post)
#rating.rate = params[:rate]
#rating.save
This might not be perfect syntax, but you should get the idea about what you are trying to do and can adjust to match your code.
You can use first or initialize like this
#rating=Rating.where(post_id: #post.id,user_id: current_user).first_or_initialize

How to construct a Rails for a many-to-many model?

I have a User, a Wiki, and a Collaborator model:
class User < ActiveRecord::Base
has_many :wikis
has_many :collaborators
end
class Wiki < ActiveRecord::Base
has_many :wikis
has_many :collaborators
end
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :wiki
end
When I am editing a Wiki's collaborators I would like the form to look something like this:
My problem is that I cannot figure out how to construct the form. I thought the following would work but <% form_for :collaborator do |f|%> doesn't result in anything being included in the resulting page.
<% form_for :collaborator do |f|%>
<% possible_collaborators.each do |user| %>
<%= check_box_tag 'wiki[collaborator_ids][]', user.id, wiki.collaborators.include?(user) %>
<%= user.name %>
<br />
<% end %>
<%= f.submit %>
<% end %>
As you didn't post your controller code I can't be entierly sure what you are trying to accomplish, but I think you are missing out on the accepts_nested_attributes_for (docs, tutorial with controller code).
Furthermore I can only recommend you the use simple_forms or formtastic as those gems do a good job when it comes to complex forms and help you a lot with the basic use cases.

how to display data from a associated model

Currently stuck on a problem
I have a company.rb model that has_many :applications
The application.rb model belongs_to :company and has_many :answers
The answer.rb model belongs_to :application.rb and has_many :users
The user.rb model has_many :answers
I allow a company to create an application. There they can input questions. The user can view them and their answers will be stored in answer.rb.
What I'm trying to do now is display all current_company.applications that have received an answer.
I tried this:
<% #applications.all.each do |f| %>
<%= f.answers.answer_1 %><br>
<% end %>
whilst having my controller:
def applicants
#applications = current_company.applications
end
however I get undefined method `answer_1'. It doesn't seem I'm available to access it. I store it like this:
the applications has a company_id and the answers has an application_id and a user_id.
I thought that by doing i the way I do now I'm able to access all applications created by the current company. From there I can view all application_id in the answers as those are the one's I'm outputting but it's not working.
I think you understood the way you can access nested models wrongly.
When an application has_many: :answers, then #application.answers is a collection you can iterate through. For example (in your view):
<% #applications.each do |application| %>
<% application.answers.each do |answer| %>
<%= answer.text %>
<% end %>
<% end %>
(assuming that your answer model has a text attribute).

accepts_nested_attributes_for keeps form fields from showing

When I use accepts_nested_attributes_for the corresponding fields no longer show in my view.
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :survey
end
Then in my view:
<%= form_for #survey do |f| %>
<%= f.fields_for :questions do |question_fields| %>
<%= question_fields.text_area :text %>
<% end %>
<% end %>
If I remove accepts_nested_attributes_for then the text_area shows, but if I keep it...nothing gets renders.
I'm running Rails 3.0.3
Did you build the questions , in the controller ?
Something like
#survey.questions.build
This builds one related question, so only one text area will show up. run it in a loop like
2.times { #survey.questions.build }
It will appear 2 times.
Do you want to create new questions or are you editing them? You might want to try something like this if you are creating a new question for this survey:
<= f.fields_for #survey.questions.build do |question_fields| %>

Resources