Order by score through other model - ruby-on-rails

i'm having a users model that has a method score, which actually gets a sum of the score of solved challenges :
def score
self.challenge_level_solutions.inject(0) do |sum, x|
sum + x.challenge_level.points
end
end
Now, i have a Kaminari related controller action like :
def index
#users = User.page(params[:page])
end
My problem is how i would go about displaying the user score in DESC order, that is show the users with a better score about others with a lower score. My view is :
<% #users.each_with_index do |user, index| %>
<%= user.username %> - <%= user.score %>
<br>
<% end %>
and shows :
1. user1 - 0
2. user2 - 0
3. user3 - 2
Any ideas so that it shows properly, with user3 on the first place ?

def index
#users = User.all.sort_by(&:score).reverse
#users = Kaminari.paginate_array(#users).page(params[:page]).per(2)
end

The ruby way to do this is use collection.sort_by
Example:
collection.sort_by{|o|o.score}
Chain .reverse/.reverse! if needed.

Related

Using nested form to update and create associated items with validation

I have a order form with restrictions on the number of items you can select that accepts nested attributes and I'd like to perform a create and an update on it's associated items when I update the order.
My form looks like:
<%= simple_form_for #food_order do |f| %>
<% #food_order.order_items.each do |oi| %>
<%= f.fields_for :order_items, oi do |oi_form| %>
<%= oi_form.input :quantity %>
<% end -%>
<% end -%>
<% end -%>
My validator looks like:
# OrderItem.rb
validate :food_order_quantity
def food_order_quantity
if (order.limit - order.order_items.sum(:quantity)) < self.quantity
errors.add(:base, "Too many products. Please update your cart.")
end
end
Let's imagine I create an order with a limit of 10 items and select 10 items:
food_order = FoodOrder.create(limit: 10)
order_item_1 = OrderItem.create(order: food_order, quantity: 10)
If I attempt to update the order by reducing order_item_1's quantity by 1 and adding a new order_item with a quantity of 1 I get an error even though the total quantity is correct:
order_item_1.quantity = 9
order_item_2 = OrderItem.new(order: food_order, quantity: 1)
put client_food_order_path(#client, food_order), params: {
food_order: {
id: food_order.id,
order_items_attributes: [
order_item_1.attributes,
order_item_2.attributes,
]
}
}
# returns the following error
#messages={:"order_items.base"=>["Too many products. Please update your cart."]}
I understand that it's attempting to save order_item_2 before updating order_item_1 and in that time, the controller believes there are 11 items (simply because order_item_1 has not yet been updated).
What can I do to allow this sort of operation?

Find previous or next array

I created a rails app that allows the user to read a magazine.
To do so, I created two scaffolds, one for the magazine and an other for the pages inside of it. I then made a one-to-many relationship, so the pages belong to the magazine.
Each page is an image, since they are digitized then uploaded in a multi-upload form.
Recently, the group I work for asked me to find a way to allow the user to read two pages at the same time, so I made some tweaks, and it works like a charm.
However, I now have a problem: I want to set some "previous" and "next" links, but I can't find a way to do so
Here is what I have done so far:
magazine_controller.rb
def create
#magazine = Magazine.new(magazine_params)
if #magazine.save
#index = 0
(params[:images] || []).each_with_index do |image, index|
if index.even?
#This way, #index increments every other upload
#So I'm sure I have two images with the same page_number
#index += 1
end
#magazine.pages.create(image: image, page_number: #index)
end
redirect_to #magazine, notice: 'Magazine créé'
else
render :new
end
end
models/page.rb
class Page < ApplicationRecord
belongs_to :magazine
validates_presence_of :magazine
mount_uploader :image, PageUploader
def previous
self.class.first.where('page_number < ?', page_number).limit(1).first
end
def next
self.class.first.where('page_number > ?', page_number).limit(1).last
end
end
views/pages/show.html.erb
<% #page.each do |p| %>
<%= image_tag p.image %>
<%= p.inspect %>
<% end %>
<br />
<%= #page.first.page_number %>
<%= link_to '< Précédent', magazine_page_path(magazine_id: #magazine.slug, id: #page.previous) if #page.previous %>
<%= link_to 'Suivant >', magazine_page_path(magazine_id: #magazine.slug, id: #page.next) if #page.next %>
<br />
<%= link_to 'Back', magazines_path %>
page_controller.rb
private
def set_page
#magazine = Magazine.find_by(slug: params[:magazine_id])
#was 'find_by' before I was asked to show two records at the same time
#page = #magazine.pages.where(page_number: params[:id])
end
So with this code, I'm getting the error undefined method 'previous' for #<Page::ActiveRecord_AssociationRelation:0x007ff4f702ad48>. I don't have a clue about how to find if there is a following "page" or not.
Any idea welcome!
Thank you in advance
Remember that #page is no longer a single record, it's an association with two records.
You can create previous and next page methods in Page class for the association instead of the object (self.previous instead of previous). It will get a new association for the previous (or next) page number. Note the addiitional code to make sure you're getting the same magazine (which you don't have in your current code that worked for single pages).
Also note that if the association has no records (count == 0) the methods return nil... this is to accommodate your if #page.previous test for no previous (and if #page.next if no next)
def self.previous
new_page_set = Page.where(page_number: (first.page_number - 1), magazine: first.magazine)
return new_page_set.count == 0 ? nil : page_set
end
def self.next
new_page_set = Page.where(page_numberL (first.page_number + 1), magazine: first.magazine)
return new_page_set.count == 0 ? nil : page_set
end

Dynamically show total per page, as I paginate

Rails 3.2
I have the following in my invoices_controller.rb:
def index
#invoices = Invoice.all.paginate(:page => params[:page], per_page: 10)
....
#invoices_total = #invoices.compact.inject(0){ |sum, invoice| sum + invoice.total }
Here's the view (slim):
- if #invoices.any?
tfooter
tr
td colspan="#{checkin_action ? '10' : '9'}" Total
td style='text-align:right'
'$#{number_with_delimiter(#invoices_total)}
This allows me to display the total for each page, in the footer. The problem, is that when I click on next, to move on to the next page, the total does not change. How do I get it to change dynamically with each page change.
Any ideas?
In general, the invoices_total amount should not be changed, no matter what page you are in. The invoices_total can write this way:
#invoices_total = Invoice.count
Or if total is invoics's column,you can:
#invoices_total = Invoice.pluck('total')
I updated my answer.
No need to write this query.
#invoices_total = #invoices.compact.inject(0){ |sum, invoice| sum + invoice.total }
I am giving logic to get the sum of #invoices show in the page.
#Declare a variable to store the sum of the invoices.
<% invoices_total = 0 %>
<% #invoices.each do |invoice|%>
# While looping through your invoices add invoice total to that above variable
<% invoices_total += invoice.total %>
#Rest of your code for show
<% end %>
#Finally the Total Sum like this
<%= invoices_total %>

How to query two tables from database in one controller

I have two tables which are one to many (1 challenge to many entry)
I want to get the last entry for all challenges but I also want to get the title of the challenge for that last entry.
So far I have:
def index
#discovers = Challenge.all.map{|c| c.entries.last}
end
How to I also add the fact I want the Challenge.title?
def index
#challenges = Challenge.all
end
Then inside your view
<% #challenges.each do |challenge| %>
<%= challenge.title %> # will give you challenge title
<%= challenge.entries.last %> # will give you last entry for the challnge
<% end %>

Want to display last 3 posts on the homepage

So i have rails app where all posts are displayed on /posts, which is where I want them. There, I have 10 posts per page. But, along with this page - i would like to take the last three posts and display them in a div on the root page.
Not sure where to start.
Thanks
Try this:
%div
-Post.last(3).each do |p|
%h1= p.title
%p= p.author
%p= p.content
Post.last(3) returns the last 3 posts that you're looking for. Hope this helps.
p.s. you may want to refactor this by moving the Post.last(3) into a variable in your controller like #latest_posts = Post.last(3)and iterate over that.
The last finder method will return the results in ascending order. If you want to return the results ordered by created_at in descending order, here's how I would approach it (unit test included).
app/models/post.rb
class Post < ActiveRecord::Base
def self.recent(max = 3)
limit(max).order(created_at: :desc)
end
end
spec/models/post_spec.rb
RSpec.describe Post, type: :model do
describe ".recent" do
it "returns the most recent" do
first_post = Post.create(created_at: 3.days.ago)
second_post = Post.create(created_at: 2.days.ago)
third_post = Post.create(created_at: 1.day.ago)
result = Post.recent(2)
expect(result).to eq([third_post, second_post])
end
end
end
In your controller(s):
#recent_posts = Post.recent
In your view:
<div id="recent-posts">
<ul>
<% #recent_posts.each do |post| %>
<li><%= post.title %></li>
<% end %>
</ul>
</div>
If you want to reuse the view code, put it into a partial and render that in your view(s).

Resources