Sorting by values in a loop - ruby-on-rails

I'm trying to store FIFA Games, and set a scoreboard with a ranking system.
I shouldn't use logic in the view, but if I calculate them in the controller, it renders an error that the method user is not specified. When I put it in the loop, however, it recognizes it because the user is the looped item.
The app can already save games and calculate the winner. The app adds winner_id and loser_id to each game. Later in the scoreboard, I count how many current user_id's from the loop match all games' winner_id's and loser_id's. This keeps the database clean. I don't want to keep the wins and losses in the db because when a game is deleted, it shouldn't count as a win or loss anymore.
Controller:
class ScoreboardController < ApplicationController
def index
#users = User.all
end
end
VIEW:
<div class="panel panel-default" style="margin-left: 10px; margin-right:10px">
<!-- Default panel contents -->
<div class="panel-heading">Scoreboard</div>
<!-- Table -->
<table class="table">
<thead>
<th>#</th>
<th>Username</th>
<th>Ratio</th>
<th>Wins</th>
<th>Losses</th>
</thead>
<% #users.each do |user|%>
<tbody>
<td>
1
</td>
<td>
<%= user.username %>
</td>
<% if (Game.where(:winner_id => user.id).count) == 0 %>
<td>Unvalid</td>
<% elsif (Game.where(:loser_id => user.id).count) == 0 %>
<td>Unvalid</td>
<% else %>
<% #ratio = (number_with_precision((((Game.where(:winner_id => user.id).count).to_f) / (Game.where(:loser_id => user.id).count).to_f), precision: 2)) %>
<td><%= #ratio %></td>
<% end %>
<td>
<%= Game.where(:winner_id => user.id).count %>
</td>
<td>
<%= Game.where(:loser_id => user.id).count %>
</td>
<% end %>
</tbody>
</table>
</div>
I'd like to put this list in the right order. The list should be ordered by ratio. => the #ratio from the view. Can I do this directly?
In the first td, the current position is shown. It shows 1 for every user. How can I make this 1, 2, 3, ...?

You should add those methods in your User model.
class User < ActiveRecord::Base
has_many :wins, class_name: 'Game', foreign_key: 'winner_id'
has_many :losses, class_name: 'Game', foreign_key: 'loser_id'
def ratio
wins.count / losses.count.to_f * 100
end
end
then in the controller :
def index
#users = User.all.sort_by(&:ratio)
end
and in the view, use the user instance methods directly :
<%= user.wins.count %>

You should be doing it the way #ThomasHaratyk has suggested above.
Additional question: in the first td the current position is shown, for now it shows a 1 for every user, how can I make this 1, 2 , 3 , ... ?
<% #users.each_with_index do |user, index|%>
<tbody>
<td>
<%= index + 1 %>
</td>
<% end %>

Related

rails game project: ajax requests from model.rb

i got some kind-of-game rails project with different banana icons in a garden.
bananas in garden view
bananas in list view with attributes for debug purposes
the bananas are ripening stepwise and are depicted in the garden view.
i got a scheduler.rb as model that updates the banana ripeness attribute in a own thread. the attribute nexttime is for the scheduler to update the ripeness attribute.
so my question is how can i fire a html event from the scheduler.rb so that when the scheduler updates the ripeness attribute of the banana.rb that i get the next-ripe banana icon as ajax in the garden view.
more detail on the code: three classes: banana.rb, scheduler.rb, garden.rb
garden.rb relations
class Garden < ApplicationRecor
has_many :bananas, dependent: :destroy
belongs_to :scheduler, required: false
application_helper.rb
def imghelper ripeness
if ripeness == 0
# inline_svg "banana-green.svg"
image_tag "green.png"
elsif ripeness ==1
# inline_svg "banana-yellowgreen2.svg"
image_tag "yellowgreen.png"
elsif ripeness == 2
# inline_svg "banana.svg"
image_tag "yellow.png"
end
end
garden/show.html.erb
<table>
<tbody>
<% rowid = 0%>
<% #bananas.in_groups_of(#garden.sizex) do |rows| %>
<tr>
<% rowid += 1%>
<% colid = 0%>
<% rows.each do |b| %>
<td>
<% colid += 1%>
<% tostring = "#{rowid}-#{colid}"%>
<div id="<%= tostring %>" class="banana">
<%= imghelper b.ripeness %>
</div>
</td>
<% end %>
</tr>
<% end%>
</tbody>
</table>

trouble coding links which insert search values into ransack form

I've coded in a page in which data from a database is pulled and subsequently when I click on what is displayed it enters a corresponding value into the search function on the application and displays the results, the code can be seen below:
course view:
<!-- Index of all Courses -->
<% provide(:title, "Course") %>
<!--containers for design/layout -->
<div class = "signinstyle">
<div class = "row">
<!--Page information -->
<%= form_tag(degree_new_path, :method => "get", id: "search-data") do %>
<table border="1" class="table">
<thead>
<tr>
<th>Courses</th>
</tr>
</thead>
<tbody>
<% #ads.each do |degree| %>
<tr>
<td> <%= link_to degree.cname, keyword_search_path(search: degree.cname) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag "Select" %>
<% end %>
<!--closing the design/layout containers -->
</div>
</div>
degree controller (the above view is within this):
class Degree < ActiveRecord::Base
def Degree.search(search)
where("cname LIKE ? OR ucas LIKE ?", "%#{search}%", "%#{search}%")
end
end
search controller (as I use my keyword search in the view):
def keyword_search
#search = Degree.all.select(:uname, :cname, :ucas, :duration, :qualification, :entry).distinct.order(id: :ASC)
if params[:search]
#search_degree = Degree.search(params[:search]).order('cname ASC')
end
end
def course
#select = Degree.all.select(:uname, :cname, :ucas, :duration, :qualification, :entry).distinct.order(id: :ASC)
if params[:search]
#select_degree = Degree.search(params[:search])
end
end
I'm trying to replicate the above code so I can click on similar links which will enter data into the ransack search function I have but have been unable to do so, if anybody could help me out it would be appreciated. Below is the code I'm currently trying to get to work:
Searches controller:
def adsearch
#adsearch = Degree.ransack(params[:q])
#data = #adsearch.result
#adsearch.build_condition if #adsearch.conditions.empty?
#adsearch.build_sort if #adsearch.sorts.empty?
end
the view file:
<!-- Index of all Courses -->
<% provide(:title, "Course") %>
<!--containers for design/layout -->
<div class = "signinstyle">
<div class = "row">
<!--Page information -->
<%= form_tag(degree_new_path, :method => "get", id: "search-data") do %>
<table border="1" class="table">
<thead>
<tr>
<th>Courses</th>
</tr>
</thead>
<tbody>
<% #ads.each do |degree| %>
<tr>
<td> <%= link_to degree.subject_group, adsearch_path(name: ucas & value: degree.ucas_letter) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag "Select" %>
<% end %>
<!--closing the design/layout containers -->
</div>
</div>
With the last two exerts of code it displays what I'm asking it to display on the initial view but doesn't enter the value I wish it to enter into the ransack search and as such doesn't create a search when clicked upon like the first example does.

Rails 4 - Displaying associated attribute (without all attributes in associated table)

I'm trying to make an app in Rails 4.
I have a profile model.
Im trying to display a user's roles in that profile show page.
I have three models:
User
rolify (which has a user_role join table)
Role
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
Profile
belongs_to :user
In my profile show page, I have:
<%= #profile.user.roles.each do |role| %>
<%= role.name.titlecase %> <span style= "margin-right: 30px"></span>
<% end %>
In the show view, I get:
Manager [#<Role id: 9, name: "faculty_manager", resource_id: nil, resource_type: nil, created_at: "2016-01-16 08:06:55", updated_at: "2016-01-16 08:06:55">]
The 'Manager' part is the only correct part. How do I get the show page not to set out all the other attributes in the role table?
You have <%= #profile instead of just <% #profile which puts result of enumerator in the view
<% #profile.user.roles.each do |role| %>
<%= role.name.titlecase %> <span style= "margin-right: 30px"></span>
<% end %>
If you want to fetch name only for each role then do this
<% #profile.user.roles.pluck(:name).each do |role_name| %>
To set your show page in a nice table view format do something like the following in you show page code:
The error was using <%= #profile.user.roles.each do |role| %>
Note the = you used
<table class="table ld-margin-top-20">
<thead>
<tr>
<th>Manager ID</th>
<th>Manger Name</th>
<th>Manger Resource ID</th>
<th>Manger Resource Type</th>
</tr>
</thead>
<tbody>
<% #profile.user.roles.each do |role| %>
<tr>
<td> <%= role.id %></td>
<td> <%= role.name.titlecase %></td>
<td><%= role.resource_id %></td>
<td><%= role.resource_type %></td>
</tr>
<% end %>
</tbody>
</table>

Rails Way of Grouping By an Association

I know I am making this more difficult than it needs to be. There has to be a rails way of accomplishing this task. To demonstrate: here are two models:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :blogs
def to_s
name
end
end
#app/models/blog.rb
class Blog < ActiveRecord::Base
belongs_to :user
delegate :name, to: :user, prefix: true, allow_nil: true
def to_s
title
end
end
So what I want to do is group all of the blogs by the associated user. Then I want to list those blogs per each user.
Key Detail: not all blogs have an associated user. Some have user_id = nil. Here is a listing of the blogs to demonstrate (last two blogs have user_id = nil):
So I got what I wanted to work. But the solution is not easy to read, and I know there must be some way to accomplish this using Rails' query interface. I couldn't figure it out though, so I hacked together my own solution below:
#app/controllers/admin_controller.rb
class AdminController < ApplicationController
def index
#group_blogs_by_user = {}
User.all.pluck(:name).each{|user| #group_blogs_by_user[user] = []}
#group_blogs_by_user[nil] = [] #provide nil category when no user_id was specified for a blog
Blog.all.each {|blog| #group_blogs_by_user[blog.user_name].push(blog)}
#group_blogs_by_user.reject!{ |_ , v|v.empty?} #do not show users that have no blogs
end
end
And here is the view to display it:
#app/views/admin/index.html.erb
<h1>Showing Count of Blogs per User</h1>
<table>
<thead>
<th>User</th>
<th>Blogs Count</th>
</thead>
<tbody>
<% #group_blogs_by_user.each do |user, blogs_of_this_user| %>
<tr>
<td><%= user || "No User Specified"%></td>
<td><%= blogs_of_this_user.size %></td>
</tr>
<% end %>
</tbody>
</table>
<hr>
<h1>Showing Breakdown of Blogs per User</h1>
<% #group_blogs_by_user.each do |user, blogs_of_this_user| %>
<h3><%= (user || "No User Specified") + " (#{blogs_of_this_user.size} blogs)" %></h3>
<table class="table">
<thead>
<th>Blog ID</th>
<th>Created At</th>
</thead>
<tbody>
<% blogs_of_this_user.each do |blog| %>
<tr>
<td> <%= link_to(blog.id, blog)%></td>
<td> <%= blog.created_at.strftime("%d-%m-%Y")%></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
And here is what it renders, which is what I want:
I run into this situation all the time where I want to group a table by some association, and I find myself continually hacking together a solution. How can I do this the rails way with Rails' Query Interface?
To get all the blogs from a user and print the username, blog_id and blog count
<% #users.each do |user| %>
<%= user.name %>
<%= user.blogs.count %>
<% user.blogs.each do |blog|%>
<%= blog.id %>
<% end %>
<% end %>
To get the amount of blogs with no user
<%= Blog.where(user: nil).count %>
I hope I got your question right and this helps!

DRY in Rails 3 if else elsif

Second week on RoR (with no programming background). And I have a bit of an issue, I'm doing a Metacritic type of a website. And there are going to be ratings everywhere. I decided on 0 to 33 = red 34 to 66 = orange 67 to 100 = green which looks like that
index (controller:show)
<td><% if show.reviews.count == 0 %>0
<% elsif show.reviews.average("rating").between?(33, 66) %>
<table class="orange">
<tr>
<td><b><%= number_with_precision(show.reviews.average("rating"), :precision => 0) %></b></td>
</tr>
</table>
<% elsif show.reviews.average("rating").between?(66, 100) %>
<table class="green">
<tr>
<td><%= number_with_precision(show.reviews.average("rating"), :precision => 0) %></td>
</tr>
</table>
<% elsif show.reviews.average("rating").between?(00, 33) %>
<table class="red">
<tr>
<td><%= number_with_precision(show.reviews.average("rating"), :precision => 0) %></td>
</tr>
</table>
<% end %>
</td>
My issue is that I'm gonna need to repeat that code, a lot, see (I'm only getting started:
show (controller show)
<p>
Note: <% if #ratings == 0 %>0
<% elsif #ratings.between?(33, 66) %>
<table class="orange">
<tr>
<td><b><%= number_with_precision(#ratings, :precision => 0) %></b></td>
</tr>
</table>
<% elsif #ratings.between?(66, 100) %>
<table class="green">
<tr>
<td><%= number_with_precision(#ratings, :precision => 0) %></td>
</tr>
</table>
<% elsif #ratings.between?(00, 33) %>
<table class="red">
<tr>
<td><%= number_with_precision(#ratings, :precision => 0) %></td>
</tr>
</table>
<% end %>
</p>
Somebody told me this should be a model but I don't really know how to write it. Any help ?
First of all you should add an instance method to your Show model that retrieves and caches the average rating for a show. This prevents querying the database multiple times for the same data:
def average_rating
#average_rating ||= self.reviews.average('rating')
end
The code that returns the appropriate css class for a Show can go into a helper (e.g. the ShowHelper):
module ShowHelper
def average_rating_class_for(show)
if show.average_rating < 34
'red'
elsif show.average_rating > 66
'green'
else
'orange'
end
end
end
With this, your views become much cleaner:
<td>
<% if show.reviews.count == 0 %>
0
<% else %>
<table class="<%= average_rating_class_for(show) %>">
<tr>
<td><%= number_with_precision(show.average_rating, :precision => 0) %></td>
</tr>
</table>
<% end %>
</td>
And:
<p>
Note:
<% if #show.reviews.count == 0 %>
0
<% else %>
<table class="<%= average_rating_class_for(#show) %>">
<tr>
<td><%= number_with_precision(#show.average_rating, :precision => 0) %></td>
</tr>
</table>
<% end %>
</p>
You could even move the generation of the entire table into a model. (Although you shouldn't be using a table here, but that's a different matter.)
module ShowHelper
def average_rating_class_for(show)
if show.average_rating < 34
'red'
elsif show.average_rating > 66
'green'
else
'orange'
end
end
def average_rating_table_for(show)
if show.reviews.count == 0
'0'
else
content_tag :table do
content_tag :tr do
contect_tag :td, :class => average_rating_class_for(show) do
number_with_precision(show.average_rating, :precision => 0)
end
end
end
end
end
end
With this you view becomes:
<td>
<%= average_rating_table_for(show) %>
</td>
What Andre suggests is possible too, but it may be a bit difficult to comprehend for a beginner like yourself. This is simpler.
You will need to make a new folder in your app directory called presenters.
Then you will want to create a file called rating_presenter.rb
This will be your presenter file
class RatingPresenter
def initialize(rating, template)
#rating = rating
#template = template
end
def get_ratings
# here you will house the logic to display your tables as needed
# I would probably determine the output to return the class to set the table accordingly
# keep in mind that view helpers are available
# ie. h.link_to or h.form_tag
end
private
def h # we don't want to be saying #template.link_to etc everywhere, so this is a shortcut
#template
end
end
And in the application_helper.rb file
We need to determine the class in order to present the class
def present(object, klass = nil)
klass ||= "#{object.class}Presenter".constantize # assign object or nil
presenter = klass.new(object, self) # assign presenter to object instance
yield presenter if block_given? # yield if block is given
presenter # return presenter
end
So in your view
You call the helper method that is then
<% present #rating do |rating_presenter| %>
<p>
<%= rating_presenter.get_ratings %>
</p>
<% end %>
So this is a quick and dirty example from a couple resources I've learned. You will need to experiment/break stuff a bit to have it suit your needs. You can expand on the get_ratings method and use more than one method to build your table ( this is recommended ) instead of having one method be responsible for the whole thing. It will help you to isolate problems. Hope this points you in the right direction
You should also search on google for "presenters +rails", you may find some more articles that will further help you understand this concept. Finally rails has gems for handling complex view logic, check out Draper. https://github.com/drapergem/draper
I think for somebody "Second week on RoR (with no programming background)" using a presenter is a little out of scope. Also one should try and simplyfy step by step and only to the level one is comfortable with (no use in using constructs one does not understand)
My advise would be to simply write a small helper to decide which colour the div should be. As a first shot simply put the following method into app/helpers/application_helper.rb
def color_for_rating(rating)
if show.average_rating < 34
'red'
elsif show.average_rating > 66
'green'
else
'orange'
end
end
you could then clean up your index view as following by using the helper to give the correct colour for your table class
<td><% if show.reviews.count == 0 %>0
<% else %>
<table class="<%= color_for_rating(show.reviews.average("rating")) %>" >
<tr>
<td><b><%= number_with_precision(show.reviews.average("rating"), :precision => 0) %></b></td>
</tr>
</table>
<% end %>
</td>
As soon as you feel comfortable with this easier approach and have used it a little you can check back to investigate the more advanced solutions given in the other answers.

Resources