Rails - showing comments in a different area - ruby-on-rails

Im trying to create a system which allows athletes to respond to their coaches traing plan, to do this i have allowed the coach to create contentsm however i am using a blogging based system to create it... at the moment the page displays like so
CONTENT TITLE
Content info 1...
Content info 2...
Content info 3...
COMMENTS...
comment 1
comment 2
comment 3
.etc
However i want to set it so that there can only be 7 Comments Max per post as well as set out like this per post...
CONTENT TITLE
Content info 1...
comment 1
Content info 2...
comment 2
Content info 3...
comment 3
.etc
I realise this is probably not the best way to do want i want, but it works (just dosnt appear in the place i want it to)
I did do experiments with creating more models, but kept getting errors whenever i tryed to run more than 1 comment system per post. I was wondering if i could have some help in sorting this out, or any methods i could do to make this easier, or even better if the models would work and if i was just doing something wrong?? tell me if this isn't enough information to go off, and ill try provide some more! Thankyou
.
.
EDIT:
The models i have used are
Program - As in the training plan set for the week
Coaches - The coach that is inputing the data to the rider
Riders - To comment on the coaches data with their own data.
I am unsure what files are need exactly so i have included the link to the github page i am pushing to ( https://github.com/effectonedesign/coacheasy1 ), if there is any other info needed, please let me know!
I like what "mind" has said however, i have done everything have said, in my def show (program controller) it is saying there is an error and i keep getting this message undefined method `coaches' for nil:NilClass everything is identical to his but im getting issues, i really do appreciate the help! Thanks

I would probably create 3 models for the above, TrainingPlan, Section (or content, text_block etc.) and Comment.
Then do the following
TrainingPlan has_many :sections
Section belongs_to :training_plan
Section has_one :comment (if you allow only 1 comment per section, otherwise use has_many)
Comment belongs_to :section
Now, to achieve the formatting you wanted do the following in your views:
<% #training_plan.sections.each do |section| %>
<%= section.text %>
<%= section.comment.text %>
<% end %>
If you allow multiple comments:
<% #training_plan.sections.each do |section| %>
<%= section.text %>
<% section.comments.each do |comment| %>
<%= comment.text %>
<% end %>
<% end %>
Form for comments
I haven't tested the following, so you might need to tweak some parts.The training plan controller:
def show
# using includes will query the database 3 times only (once for each table) rather than
# querying it 1 + N + N (in this case 7 sections, 7 comments possibly, so 15 times)
#training_plan = TrainingPlan.includes(:sections, sections: :comment).find(params[:id])
#sections = #training_plan.sections
#sections.each do |section|
# only build a new comment if there is no comment for that section already
section.build_comment unless section.comment
end
end
In your view views/training_plans/show.html.erb
<%= #training_plan.title %> # or whatever
<% #sections.each do |section|
<%= #section.content %>
<% if section.comment %>
<%= section.comment.content %>
<% else %>
<%= render 'comments/form', comment: section.comment %> # or wherever you have the form
<% end %>
<% end %>
views/comments/_form.html.erb
# This might break if you have a separate comment action somewhere which passes an
# instance variable #comment to the form
<%= form_for comment do |f| %>
# normal form stuff
<% end %>
If that all works then on your training plan show page you should see each section, and if it has a comment then that comment will be rendered, otherwise a form will be shown.
Depending on your routes you might need to run rake routes and see where your comment create action is, and then pass that to the form <%= form for comment, url: some_url_helper_here do |comment| %>
If it was me I would create the add comment part through JavaScript, sort of like in this railscast, but since you're new to RoR I've tried to keep it simple.

Related

Ruby on Rails: Check_boxes for each item in a list

I'm trying to render simple check_boxes in a list of results so users can select on which of these results to get additional content for.
Example model setup (names changed):
UserRequest has_one :response
Response has_many :individual_results
IndividualResult has_one :contact_info
In the UserRequestsController I have a show action, which shows the user_request (duh), response, and its individual_results in a table.
The user should be able to select items (= contact_info) to request additional content for those. For some reason I don't manage to render the check_boxes after days of trying. Basically, I don't really understand where and how to store the array of selected contact_infos and how to pass it to the method that then gets the additional content.
My attempt was:
Create a ContactInfosController (didn't exist before, the user was only creating and showing her requests up to now, so contact_info was "model only" before)
Create a method request_content(contact_infos) (as post, to pass the user-selected contact_infos to. If I were to make it Restful, it would probably be "edit/update")
Add the form with check_boxes search_request#show
My search_request/show.html.erb:
<tbody>
<% if #response %>
<% form_for #contact_infos, url: contact_infos_request_content_path(#contact_infos) do |form| %>
<% #response.individual_results.each do |result| %>
# result.foo, ...
# result.contact_info.bar
<%= form.check_box "contact_info", "contact_info.request_content?", "true", "false %>
<% end %>
# form.submit
<% end %>
<% end %>
</tbody>
As it didn't work, I also had to declare the instance variable #contact_infos in RequestsController#show as #contact_infos = #response.individual_results.map { |r| r.contact_info }
Now, it fails at the check_box with "undefined method `merge'". Also, not sure how the params would be passed? I feel I went seriously "off the rails" and probably screwed up the design with this as it seems way too complicated...
Would anybody be so kind and help me get into the right direction, e.g., how would you pass the response to a method to request additional information? Read tons online but somehow couldn't apply it.
Thanks so much!

How to retrieve and output the last record by foreign id?

I have 2 Models in my application. Project & Note. A project has_many notes. This all works great but on the projects index page.html.erb I would like to show the last note for a project.
My question is how do I retrieve this and then display it, on the index screen?
You can get the last note using project.notes.last. In your view, add below :
<% projects.each do |project| %>
<% last_note = project.notes.last %>
<%= last_note.note unless last_note.nil? %> # get last note per project if it exists
<% end %>

Rails 4 x paper_trail: track which attributes are updated and display their new value

In my Rails 4 app, I am using paper_trail to track changes made by users on the records of the Post model:
# post.rb
has_paper_trail :on => [:update, :destroy]
A post belong_to a calendar and a calendar has_many post, so I display the changes made to posts in a dashboard located in the calendar index view:
<div id="my_changes">
<% if #versions.any? %>
<% #versions.each do |version| %>
<% version.reify ? post = version.reify : post = Post.find_by_id(version.item_id) %>
<p>
<strong>
<% if version.whodunnit.to_i === current_user.id %>
You
<% else %>
<%= User.find_by_id(version.whodunnit).first_name %>
<% end %>
</strong> <%= conjugate(version.event) %> the post "<%= post.subject %>" in the <em><%= link_to Calendar.find_by_id(post.calendar_id).name, calendar_path(id: post.calendar_id) %></em> calendar <span id="update_time_ago">— <%= time_ago_in_words(version.created_at) %> ago.</span></p>
<% end %>
<% else %>
<p>
<span class="glyphicon glyphicon-hand-up" aria-hidden="true"></span><br/>There is no change to your posts yet.<br/>
As soon as you update your calendar, a history of your changes will appear here.
</p>
<% end %>
</div>
This is working fine.
What I would like to achieve now — and I can't figure out how — is not only to display which post has been updated, but also which attribute of the post has been updated and its value.
For instance, instead of:
User_1 updated the post "Test post" in the Test calendar — 3 days ago.
I would like to have something like:
User_1 updated the date / time / subject / copy / of the post "Test
post" to NEW_POST_ATTRIBUTE_VALUE in the Test calendar — 3 days ago.
—————
UPDATE: I did read paper_trail's documentation, in particular the Choose attributes to monitor section, but this does seem to explain how to achieve what I am trying to achieve.
—————
UPDATE 2: I understand that what I am looking for may actually be explained in the Diffing versions section of the documentation.
So, I guess in my index.html.erb view, I could do something like:
post.versions.last.changeset
But then, how can I extract from there the information that I am interested in, eg: only the attribute that was updated, and its new value, and not the updated_at attribute, that I cannot ignore with paper_trail since I still want to know when the post was updated?
—————
UPDATE 3: in this Stack Overflow thread, #Maysam suggests:
With this behavior enabled, it is reasonably simple to get
object.versions.map{|v| [v.created_at, v.changeset]} and then
iterate over that structure to render your change log.
I am not sure I understand how I can actually iterate over that structure in my case: any suggestion?
—————
Can I actually achieve that with paper_trail?
.. I would like to .. display .. which attribute of the post has been updated and its value.
Per the documentation on Diffing Versions:
.. to diff adjacent versions .. add an object_changes text column to your versions table .. PaperTrail will store the changes diff .. in each update version. You can use the version.changeset method to retrieve it.
So, once you've done that, try adding
= debug post.versions.last.changeset
To your view. After that, if you know how to work with hashes in ruby, you should be good to go.

Showing the last 5 comments that belong to a post

I have two models, posts and comments, and i am trying to show the last 5 comment titles.
<%= Post.limit(5).order('created_at desc') %>
gives me the last 5 posts.
How do i get the last 5 comments and just the titles?
For getting just last 5 comments, you can use this
Comment.limit(5).order('created_at desc').select(:title)
Showing the title of the post, it looks like that you want to retrieve the last 5 comments of a post.
You can get this way:
<% Post.limit(5).order('created_at desc').each do |post| %>
<% post.comments.select(:title).order('created_at desc').limit(5).each do |comment| %>
<%= comment.title %>
<% end %>
<% end %>
There are two problems:
You're accessing the model directly from View. You should do it from
Controller
There is N + 1 problem. You can solve it by eager loading associations
Note: I couldn't test these but I guess they will work or will, at least, give you idea.
I think you are looking for:
Comment.select(:title).order('created_at desc').limit(5).map {|c| c.title}
This will give you an array of titles for the last 5 comments created. If you need other attributes, either add them to the select method called (Comment.select([:title, :owner])), or remove the select and fetch all comment attributes.

How to perform if statements on associated has_many records in Rails

In a Rails 3.2 app I have a model Project, which has many Tasks. Each Task has a :status field, which is an integer as follows
1=Normal
2=Urgent
In the Project show view, I want to display a text alert if any of the associated tasks are flagged as urgent.
If the status field was within the Project model, I would do something like this:
<% if Project.status == 2 %>
<div class="alert">URGENT TASKS!</div>
<% end %>
How can I set up a similar if statement, that will cycle through all associated Tasks, and return true if at least one task is marked as urgent?
I'm not sure what terms I should be searching on for this sort of functionality. Or maybe I'm not looking at the problem the right way. I'd be grateful for any pointers in the right direction.
Thanks
This method in Project will do it:
def urgent?
tasks.detect{|t| t.status==2}
end
Then you can do, if you have #project set to the project you're looking at:
<% if #project.urgent? %>
...whatever ...
<% end %>
This next bit was added in answer to your comment. This method in Project will return the highest priority set (lowest number in your example) for any task in a particular project:
def highest_priority
tasks.map{|t| t.status}.min
end
You can then switch between them in your view:
<% case #project.highest_priority
when 1 %>
...priority 1 stuff...
<% when 2 %>
...priority 2 stuff...
<% when 3 %>
...and so on...
<% end %>
I guess that you want to check if a project has some urgent task to be completed. If thats the case I think the best way to achieve that would be to create new method in the Project model, something like this:
def has_urgent_task?
tasks.map(&:status).include?(Task::URGENT)
end
Assuming you have defined your statuses as constants in your Task model, if not just replace Task::URGENT for 2.
So in your view you only need to do this:
<% if #project.has_urgent_task? %>
<div class="alert">URGENT TASKS!</div>
<% end %>

Resources