rails game project: ajax requests from model.rb - ruby-on-rails

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>

Related

Check from View if Record Exists in Rails has_many through

I have a has_many through: relationship in my app where people have many occasions through gifts and visa versa.
It's set up properly in the models:
person.rb is like this:
belongs_to :user
has_many :gifts
has_many :occasions, through: :gifts
occasion.rb is like this:
belongs_to :user
has_many :gifts
has_many :people, through: :gifts
gift.rb is like this:
belongs_to :user
belongs_to :person
belongs_to :occasion
In the view I'm trying to make a table with people on the y axis, occasions on the x axis, and gifts in the appropriate cells. I have this in my gifts#index view:
<table>
<tr>
<th>Recipient</th>
<% #occasions.each do |occasion| %>
<th><%= occasion.name %></th>
<% end %>
</tr>
<% #people.each do |person| %>
<tr>
<td>
<a href="#edit-recipient-popup" class="open-popup-link">
<p><strong><%= person.name %></strong></p>
</a>
</td>
<% #occasions.each do |occasion| %>
<td>
<% if #gift.where(person_id: person.id, occasion_id: occasion.id) %> <<<<<<THIS LINE IS THE ISSUE
<%= gift.name %>
<% else %>
<h1>
<%= link_to new_gift_path do %>
<i class="icon ion-ios-add green"></i>
<% end %> <!-- link -->
</h1>
<% end %> <!-- if/else end -->
</td>
<% end %> <!-- occasions each -->
</tr>
<% end %> <!-- people each -->
</table>
My problem here is that, on the line indicated, I get a undefined methodwhere' for nil:NilClass. I assume this is because you can't use controller logic likewhere` in the view, but I'm not sure how else to phrase this.
Can anyone help me perform this task successfully and in the most "railsy" way?
edited:
search the result if there is an result then print it, this will prevent an error with nil value
<% gift = Gift.where(person_id: person.id, occasion_id: occasion.id) %>
<% gift != [] %>
<%= gift.name %>
<% else %>
<h1>
<%= link_to new_gift_path do %>
<i class="icon ion-ios-add green"></i>
<% end %> <!-- link -->
</h1>
<% end %>
<% if #occasions.gifts && #occasions.gifts.where(person_id: person.id) %>
I assume this is because you can't use controller logic likewhere` in the view
This is not the problem, first thing where is not controller logic, it is model logic, second, it is just called on the model(s), it doesn't matter where, it can be invoked from anywhere in your codebase as long as it is on the model.
in terms of your error, you are getting it because #gift instance variable is not set, it is nil, are you setting this variable anywhere before you call it?
In the block:
<% if #gift.where(person_id: person.id, occasion_id: occasion.id) %> <<<<<<THIS LINE IS THE ISSUE
<%= gift.name %>
<% else %>
<h1>
<%= link_to new_gift_path do %>
<i class="icon ion-ios-add green"></i>
<% end %> <!-- link -->
</h1>
<% end %>
Your first problem is that as mentioned above your #gift variable is nil, second, you are then calling name on another unset local variable gift when you write <%= gift.name %>.
I assume you are after something along the lines of
<% gift = occasion.gifts.find_by(person_id: person.id) %>
<% if gift.present? %>
<%= gift.name %>
<% else %>
....
<% end %>
As you may have noticed I have changed .where to .find_by as well, this is because '.where' will return a collection, where as looking at your code you are looking to display a single gift record's name.

How do I use lookup_context to make this view as DRY as possible?

For starters, this is the view I am trying to replicate:
This is the HTML from that layout (from the SAT portion anyway, you can extrapolate the rest):
<table class="table table-hover table-bordered">
<thead>
<td colspan="2" class="text-center">
<strong>SAT</strong>
</td>
<tr>
<th>Subject</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr>
<td>Reading</td>
<td>900</td>
</tr>
<tr>
<td>Math</td>
<td>700</td>
</tr>
<tr>
<td>Writing</td>
<td>800</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td><strong>2,400</strong></td>
</tr>
</tbody>
This is what my Grade.rb model looks like:
# == Schema Information
#
# Table name: grades
#
# id :integer not null, primary key
# subject :string
# result :string
# grade_type :integer
# profile_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Grade < ActiveRecord::Base
belongs_to :profile
enum grade_type: { csec: 0, cape: 1, sat: 2, g7: 3, g8: 4, g9: 5, g10: 6, g11: 7, g12: 8, g13: 9 }
end
This is what that table looks like currently, i.e. before using the lookup_context method in Rails:
<table class="table table-hover table-bordered">
<thead>
<td colspan="2" class="text-center">
<strong>SAT</strong>
</td>
<tr>
<th>Subject</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<% #sat_grades.each do |grade| %>
<tr>
<% if grade.subject.eql? "Total" %>
<td><strong><%= grade.subject %></strong></td>
<td><strong><%= grade.result %></strong></td>
<% else %>
<td><%= grade.subject %></td>
<td><%= grade.result %></td>
<% end %>
</tr>
<% end %>
</tbody>
Where #sat_grades is this: #sat_grades = #profile.grades.where(grade_type: :sat).
I want to use this lookup_context method, I was thinking like this:
<% #grades.each do |grade| %>
<% if lookup_context.template_exists?(grade.grade_type, "grades/grade_types", true) %>
<%= render partial: "grade/grade_types/#{grade.grade_type}", locals: {event: event, index: index} %>
<% end %>
<% end %>
The issue I am running into is that each grade_type has a different table. So grade_type: :sat belongs in the "SAT" table, the same for "CSEC", "g11", etc.
I can't think of a way to have each of those grade_types rendered specifically within their HTML table, without having lots of lookup_context.template_exists? calls within that view.
It almost defeats the purpose of doing it like that, if I have to have a lookup_context call for each grade_type.
What's the best way to approach this so I just have 1 lookup_context call (if possible), but it correctly renders and handles all the different grades correctly.
With the given fragment I would try the following:
# Render each grade
<%= render(partial: "grade/grade", collection: #grades, locals: {event: event, index: index}) || "There's grade to be displayed" %>
# Render Concated content
<%= content_for :all_grades %>
Within grade/_grade.html.erb:
# If a special grade template exists prepare the content to be shown
# but don't display it right now
<% if lookup_context.template_exists?(grade.grade_type, "grades/grade_types", true) %>
<%= render partial: "grade/grade_types/#{grade.grade_type}", locals: {event: event, index: index} %>
<% end %>
# Render the common stuff
...
# Display the special stuff stored for the grade
<%= content_for :grade_table %>
# Repeat previous steps
...
Within the grade template (for instance grade/grade_types/_g7.html.erb):
# remove content from previous grades
<% content_for :grade_table, flush: true do %>
...
<% end %>
<% content_for :xxx_xxx, flush: true do %>
...
<% end %>
...
# Concat content for all grades together (flush: false)
<% content_for :all_grades do %>
...
<% end %>
Another approach can be a presenter or maybe even Single Table Inheritance.

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.

Sorting by values in a loop

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 %>

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!

Resources