Find previous or next array - ruby-on-rails

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

Related

Rails skip over/ignore data for links?

So I am setting up previous post links in my Rails app and have the following in my show:
<% if #post.previous.blog_key == 'blog' && #post.previous.visible == true %>
<%= link_to 'previous post', blog_post_path(#post.previous.url_name%>
<% else %>
<%= link_to 'Home', '/blog/home' %>
<% end %>
Then in my model I have:
def previous
SpudPost.where(["published_at < ?", published_at]).last
end
def next
SpudPost.where(["published_at > ?", published_at]).first
end
What I'm trying to do is only have the previous links to go to those blog posts that are marked as having a blog_key that says blog AND marked to be visible. If it's not blog/false then ignore it and go to the next one that says blog and visible.
The problem is that it doesn't go to the next one that says blog/true. If the next one says blog_key == 'news' then it applies to 'Home'. Should it just be another filter within the previous/next methods instead?
Model works better.
def previous
SpudPost.where(blog_key: 'blog').where(visible: true).where(['published_at < ?', published_at]).last
end
def next
SpudPost.where(blog_key: 'blog').where(visible: true).find_by(['published_at > ?', published_at])
end

Prevent upvote model from being called for every comment

I have three models: User, Comment and Upvote. User-to-Comment has a one-to-many relation, Comment-to-Upvote has a one-to-many relation and User-to-Upvote has a one-to-many relation.
I want to do something similar to the upvoting done on Stackoverflow. So when you upvote/downvote the arrow will highlight and remain highlighted even if you refresh the page or come back to the page days/weeks later.
Currently I am doing this:
<% if Upvote.voted?(#user.id, comment.id) %>
<%= link_to '^', ... style: 'color: orange;'%>
<% else %>
<%= link_to '^', ... style: 'color:black;'%>
<% end %>
where the voted? method looks like this:
def self.voted?(user_id, comment_id)
find_by(comment_id: comment_id, user_id: user_id).present?
end
So if I have 10 comments on a page, this will load an upvote from my database 10 times, just to check if it exist!
There has to be a better way to go about doing this, but I think my brain stopped working, so I can't think of any.
Assuming you have properly set relations
# user.rb
class User
has_many :upvotes
end
we can load comments, current user and his upvotes:
# comments_controller.rb
def index
#comments = Comment.limit(10)
#user = current_user
user_upvotes_for_comments = current_user.upvotes.where(comment_id: #comments.map(&:id))
#upvoted_comments_ids = user_upvotes_for_comments.pluck(:comment_id)
end
And then change if condition in view:
# index.html.erb
<% if #upvoted_comments_ids.include?(comment.id) %>
<%= link_to '^', ... style: 'color: orange;'%>
<% else %>
<%= link_to '^', ... style: 'color:black;'%>
<% end %>
It will require only 2 DB queries. Hope it helps.
We can do it the following way if you want it to be handled by a single query.
Lets make sure the relations are proper
# user.rb
class User < ActiveRecord::Base
has_many :comments
has_many :upvotes
end
# comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
has_many :upvotes
end
# upvote.rb
class Upvote < ActiveRecord::Base
belongs_to :user
belongs_to :comment
end
Then in the controller
def index
current_user = User.first # current_user may come from devise or any authentication logic you have.
#comments = Comment.select('comments.*, upvotes.id as upvote').joins("LEFT OUTER JOIN upvotes ON comments.id = upvotes.comment_id AND upvotes.user_id = #{current_user.id}")
end
And in view
# index.html.erb
<% #comment.each do |comment| %>
<% link_color = comment.upvote ? 'orange' : 'black' %>
<%= link_to '^', ...style: "color: #{link_color}" %>
<% end %>
# And all of your logics ;)
If you're limited to N comments per page then you can probably do this in two queries using the limit and offset methods to return the 1st, 2nd, ... ith set of N comments for the ith page, something like (syntax may be off, Ruby isn't my primary language)
comment_ids =
Comments.select("comment_id")
.where(user_id: user_id)
.order(post_date/comment_id/whatever)
.offset(per_page * (page_number - 1)) // assumes 1-based page index
.limit(per_page)
This gives you a list of comment_ids which you can use to query Upvote:
upvoted_comments =
Upvotes.select("comment_id")
.where(user_id: user_id, comment_id: comment_ids)
If you're sorting the comment_ids by a column that also exists in Upvote (e.g. if you're sorting by comment_id) then you can replace the Upvote set query with a range query.
Put upvoted_comments in a hash and you're good to go - if the comment_id is in the hash then it's been upvoted, else not.
I'm not sure this will avoid excess queries in this state but maybe you could include the upvotes when you fetch comments:
#comments = Comment.includes(:upvotes).where(foo: 'bar').limit(10)
Then in the view:
<%=
link_color = comment.upvotes.map(&:user_id).include?(#user.id) ? 'orange' : 'red'
link_to '^', ...style: "color: #{link_color}"
%>

if something list something with limit else list all in rails

I'm trying to figure out at this point how to display only a certain amount of categories based on a user plan. So in my controller I have the following;
def check_plan?
User.current.plan_id == "" && User.current.bins.count == 2 || User.current.plan_id == "nil" && User.current.bins.count == 2
end
helper_method :check_plan?
NOTE: I know, not pretty but it will do for now. So basically :check_plan? checks a few things.
Now I thought, that I could use that for our category list as a starting point, so that if the user isn't on any plan, they are limited in showing 2 if they are on a plan it shows them all. Here's what I thought would work but it doesn't. I've done it before but can't remember exactly how it went, so any help is appreciated.
<% if check_plan? %>
<% #bin_list.order("position").limit(2).each do |bin| %>
<% else %>
<% #bins_list.order("position").each do |bin| %>
<% end %>
Not really working and I know why, but their will all my other tries with bringing any of those lines together with check_plan.
Such logic belongs to Model natually. Don't abuse helper.
class User < ActiveRecord::Base
def bin_limit
check_plan? ? 0 : 2
end
def check_plan?
# Your logic
end
def bins_with_limit
bins.with_limit(bin_limit)
end
end
class Bin < ActiveRecord::Base
def self.with_limit(limit)
return self if limit <=0
self.limit(limit)
end
end
View
current_user.bins_with_limit.each do |bin|
First of all, the rails method .blank? will return true for either "" or nil, so you can start by refactoring your code as follows:
def check_plan?
( User.current.plan_id.nil? || User.current.plan_id.blank? ) && User.current.bins.count == 2
end
helper_method :check_plan?
Secondly, you're calling blocks in the code below, so you'll need to complete them with end
<% if check_plan? %>
<% #bin_list.order("position").limit(2).each do |bin| %>
<% end %>
<% else %>
<% #bins_list.order("position").each do |bin| %>
<% end %>
<% end %>
Of course, you'll want to put whatever you're doing with bin between the do and end portions above.

How to display a value of a column from a table in Rails

So this (like all my others) is a very beginner question.
This is my routes.rb file
Status::Application.routes.draw do
get '/admin/status' => 'admin#apistatus'
end
This is my controller file (called admin_controller.rb)
class AdminController < ApplicationController
def apistatus
#cdn = Api.find(1,:select=>"status")
def index
#cdn=1
end
end
end
This is my Model file Api.rb
class Api < ActiveRecord::Base
end
This is my erb file apistatus.erb
<h1>Status#index</h1>
<% if #cdn == 1 %>
<p>Foo</p>
<% else %>
<p> hello </p>
<%= #cdn %>
<% end %>
In my database (sqllite) I have a table called apis with the columns id status and status_message
I have 1 record with the id of 1 and status is green and status_message All systems Fine
When I navigate to mysite.com/admin/status/
I expect to see
the #cdn varibale displayed as "green" but what I actually see is #cdn displayed as
#<Api:0x007fecf4fa64a8>
and I'm not sure why. Thanks in advance guys.
Your column values are accessible through ActiveRecord dynamic accessors, like:
<%= #cdn.status %>

Render a rails partial based on the id of an action

I'm building a small ecommerce site that sells a variety of mens and womens clothing. i would like to render a partial based on which taxonomy the user is in. For example, if the user is at mysite.com/t/women/pants I would like to render _women.html.erb, or, if the user is at mysite.com/t/men/shirts I would like to render _men.html.erb.
I have a Taxonomy model that has_many taxons, and the Taxon model has_many products.
In taxons_controller.rb I have:
def show
#taxon = Taxon.find_by_permalink(params[:id])
return unless #taxon
#taxonomy = Spree::Taxonomy.all
#taxon_title = Spree::Taxon.all
#searcher = Spree::Config.searcher_class.new(params.merge(:taxon => #taxon.id))
#searcher.current_user = try_spree_current_user
#searcher.current_currency = current_currency
#products = #searcher.retrieve_products
respond_with(#taxon)
end
And in taxons#show I have: (which I know is wrong)
<% #taxon_title.each do |title| %>
<% #taxonomy.each do |taxonomy| %>
<% if title.name == taxonomy.name %>
<%= render "spree/shared/#{title.name.downcase}" %>
<% end %>
<% end %>
<% end %>
When I go to mysite.com/t/women/long-sleeve the rails debugger displays :
controller: spree/taxons
action: show
id: women/long-sleeve
How do I access the id of the action im inside, so that in the controller/view I can do something like:
'if id equals 'women' render "spree/shared/#{title.name.downcase}"'
where title is the name of the taxonomy?
I imagine I need to find(params[:something] in the show action of the controller, but I'm a little unclear about params.
*
*
*
#beck03076 That's a great trick. Thank you very much. But it's still not working.
In my controller I put:
#taxon_id = Spree::Taxon.find(params[:id])
Then in the action I put:
render 'spree/shared/women' if #taxon_id == params[:id]
And when I load the page it says 'the page you were looking for doesn't exist'. My partial is in the correct directory. Is my syntax correct?
My params are:
{"controller"=>"spree/taxons", "action"=>"show", "id"=>"women/long-sleeve"}
Thanks again for your help!
Whenever you are unclear about params, just put the lines below in the action and execute the action.
p "****************************"
p params
p "****************************"
Now, goto the terminal in which you started your server.
Locate those two "*******" and everything thats in between them are params.
params is basically a ruby hash.
example:
params look like this, {:controller => "hello",:action => "bye", :id => 7, :others => "OK"}
In your controller to access the id, use params[:id].(=7)
to access others, use params[:others].(="OK")

Resources