Why is my instance variable being overwritten inside my Rails controller action? - ruby-on-rails

I have two variables in my index action in my post controller. However I am using the kaminari gem and it requires that the variable for how many post per pages go in the index action.
However, I have another variable for my search. I don't want to over ride so what is a work around?
def index
#posts = Post.where(["title LIKE ?", "%#{params[:search]}%"])
#posts = Post.page(params[:page]).per(10)
end

Have you tried
#posts = Post.where(["title LIKE ?", "%#{params[:search]}%"]).page(params[:page]).per(10)
or this
#posts = Post.where(["title LIKE ?", "%#{params[:search]}%"])
#posts = #posts.page(params[:page]).per(10)
The way you have it set now the second #posts variable will override the first #posts variable every time 100% because you are just simply reassigning it.

Related

Rails - Using Posts#Index View to show only 'Liked' Posts

Since the index view for posts has the same code, I have an action in the Posts controller #liked, which save all of the current_user's liked posts. I am trying to redirect_to posts_path(#posts), but for some reason the index view still has #posts returning all posts..? I'm sure there is some sort of rails magic happening here.
I already checked my #liked method and it is successfully saving only liked posts in #posts, but the redirect is simply not passing #posts from this action, but rather from the original #index action.
Ideas?
Austin Burke, I am assuming you have following two methods in posts controller:
def posts
#posts = Post.all
end
And a second method:
def liked
#liked_posts = [post1, post2]
end
You should do:
def posts
#liked_posts = Post.where(liked: true)
end
The reason is you are using redirect_to posts_path(#posts) which calls index method of posts controller. This index method of posts controller should take #liked_posts with it.

How to use math in controller variables

I simply need to be able to use math to set the end point of a query in my controller.
class WelcomeController < ApplicationController
def index
#video = Video.last
#videos = Video.last(7).reverse!.drop(1)
end
def show
#video = Video.find(params[:id])
#videos = Video.where(:id => start..stop)
end
end
This line: #videos = Video.where(:id => start..stop) should be something like #videos = Video.where(:id => params[:id]..params[:id]-7) because that array is supposed to be the next seven database entries after #video.
I'm also certain there's a better way to accomplish what I'm trying to do but I have no idea what it is.
One way to solve this is using a gem called will_paginate, which will automatically do what you are trying to solve
Example would be
#videos = Video.paginate(page: params[:page] || 1, per_page: 5)
you can get the page from the params, so your web page has to request something like this /videos?page=2
Other gem in this category is Kaminari

How do I create a variable in the controller for single or multiple params

def index
#users = User.all.paginate(page: params[:page])
#users = User.named(params[:name]).paginate(page: params[:page]) if params[:name].present?
#users = User.countryname(params[:country]).paginate(page: params[:page]) if params[:country].present?
#users = User.gender(params[:gender_type]).paginate(page: params[:page]) if params[:gender_type].present?
end
The following code works fine if only :name or :country or :gender_type is present. But it does not work if multiple params are present. What is the DRY way of writing this code for multiple params? Obviously, I do not want to create a different line of code for each possible combination of params.
Here are the scopes:
class User
scope :countryname, -> (country) { where("country ILIKE ?", "%#{country}%")}
scope :gender, -> (gender_type) { where gender_type: gender_type}
scope :named, -> (name) { where("name ILIKE ?", "%#{name}%")}
If I have a query string of
example.com/users?name=sam&gender_type=male
it simply returns all users with names like sam and ignores their gender... I would need to code:
#users = User.gender(params[:gender_type]).named(params[:name]).paginate(page: params[:page]) if params[:gender_type] && params[:name].present?
but I do not want to have to write a new line of code for every single combination of parameters.
You could use the ruby try method. For example, you could write something like
#users = User.try(:gender, params[:gender_type]).try(:paginate, page: params[:page])
Look at try in api docs for other ways to use it.
the problem was the code should be
#users = User.all.paginate(page: params[:page])
#users = #users.named(params[:name]).paginate(page: params[:page]) if params[:name].present?
etc
Rails will then allow chain scoping automatically. the previous code creates separate independent non-chained instance variables.

Refactoring a similar controller's action

I used Pull Review for reviewing my app's code and it came back with this:
Consider refactoring, similar code detected.
Occurred at:
SkillsController # index
PagesController # index
So the app/controllers/skills_controller.rb index action code is:
def index
#skill = Skill.new
if params[:search]
#skills = Skill.search(params[:search]).order('created_at DESC')
else
#skills = Skill.all.order('created_at DESC')
end
end
and on app/controllers/pages_controller.rb is:
def index
#users = User.all
if params[:search]
#users = User.search(params[:search]).order('created_at DESC')
else
#users = User.all.order('created_at DESC')
end
end
Am I suppose to somehow refactor these two actions on these two controllers? Also, I am not sure how I refactor this. Do I extract the if params[:search] segment and replace the instance variables with another variable that will be used on both actions?
Thanks for your time.
I don't know where your method search comes from. It seems it comes from a custom module/gem for ActiveRecord.
If so, you can change the method to shorten code in controller
def self.search(args)
return self unless args
original_search_logic args
end
# As well as extract order to a scope
scope :by_time, -> { order('created_at DESC') }
Then in controller:
# Skill
def index
#skills = Skill.search(params[:search]).by_time
end
# User
def index
#users = User.search(params[:search]).by_time
end
These should be dry enough for now.
take a look at the has_scope and inherited_resources. You can extract the params[:search] part with has_scope. And use inherited_resources to extract how to get the collection and do the ordering.

Using Simple Search with Kaminari Pagination Gem

I am trying to apply pagination to my rails app using Kaminari. I am also incorporating a simple search form based on the Railscast Episode #37. When I try to apply the kaminari page and per methods I get the error 'undefined method page'. Below is the code I'm using.
posts_controller.rb
def index
#posts = Post.search(params[:search]).page(params[:page]).per(2)
end
post.rb
def self.search(search)
if search
find(:all, conditions: ['title || body LIKE ?', "%#{search}%"], order: "created_at DESC")
else
find(:all)
end
end
index.html.erb
<%= paginate #posts %>
When I remove the pagination the search works fine. When I remove the search the pagination works fine. I just can't seem to use them both and have the code function properly. Please advise if there is something in my code that I am missing that is causing this not to work properly.
In your case, you are returning array object from the search method not ActiveRecord::Relation object.
find(:all, conditions: ...) # find method will return an array object.
Add check in your controller,
def index
#posts = Post.search(params[:search])
if #posts.class == Array
#posts = Kaminari.paginate_array(#posts).page(params[:page]).per(10)
else
#posts = #posts.page(params[:page]).per(10) # if #posts is AR::Relation object
end
end
Kaminari pagination with an array https://github.com/amatsuda/kaminari#paginating-a-generic-array-object
for ActiveRecord::Relation object, checkout this http://railscasts.com/episodes/239-activerecord-relation-walkthrough

Resources